Commit cf043588 authored by jochen's avatar jochen Committed by Commit bot

Add a library suitable for libfuzzer with a small unit test runner shell

BUG=chromium:577261
R=machenbach@chromium.org,jarin@chromium.org
LOG=n

Review URL: https://codereview.chromium.org/1604203002

Cr-Commit-Position: refs/heads/master@{#33508}
parent ca23cdd8
......@@ -251,7 +251,7 @@ NACL_ARCHES = nacl_ia32 nacl_x64
GYPFILES = third_party/icu/icu.gypi third_party/icu/icu.gyp \
build/shim_headers.gypi build/features.gypi build/standalone.gypi \
build/toolchain.gypi build/all.gyp build/mac/asan.gyp \
test/cctest/cctest.gyp \
test/cctest/cctest.gyp test/fuzzer/fuzzer.gyp \
test/unittests/unittests.gyp tools/gyp/v8.gyp \
tools/parser-shell.gyp testing/gmock.gyp testing/gtest.gyp \
buildtools/third_party/libc++abi/libc++abi.gyp \
......
......@@ -11,6 +11,7 @@
'../samples/samples.gyp:*',
'../src/d8.gyp:d8',
'../test/cctest/cctest.gyp:*',
'../test/fuzzer/fuzzer.gyp:*',
'../test/unittests/unittests.gyp:*',
],
'conditions': [
......
......@@ -11,6 +11,7 @@
'type': 'none',
'dependencies': [
'cctest/cctest.gyp:cctest_run',
'fuzzer/fuzzer.gyp:fuzzer_run',
'intl/intl.gyp:intl_run',
'message/message.gyp:message_run',
'mjsunit/mjsunit.gyp:mjsunit_run',
......
......@@ -9,6 +9,7 @@
},
'includes': [
'cctest/cctest.isolate',
'fuzzer/fuzzer.isolate',
'intl/intl.isolate',
'message/message.isolate',
'mjsunit/mjsunit.isolate',
......
......@@ -11,6 +11,7 @@
'type': 'none',
'dependencies': [
'cctest/cctest.gyp:cctest_run',
'fuzzer/fuzzer.gyp:fuzzer_run',
'intl/intl.gyp:intl_run',
'message/message.gyp:message_run',
'mjsunit/mjsunit.gyp:mjsunit_run',
......
......@@ -9,6 +9,7 @@
},
'includes': [
'cctest/cctest.isolate',
'fuzzer/fuzzer.isolate',
'intl/intl.isolate',
'message/message.isolate',
'mjsunit/mjsunit.isolate',
......
include_rules = [
"+src",
]
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/fuzzer/fuzzer-support.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
namespace v8_fuzzer {
namespace {
FuzzerSupport* g_fuzzer_support = nullptr;
void DeleteFuzzerSupport() {
if (g_fuzzer_support) {
delete g_fuzzer_support;
g_fuzzer_support = nullptr;
}
}
} // namespace
class FuzzerSupport::ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
virtual void* Allocate(size_t length) {
void* data = AllocateUninitialized(length);
return data == NULL ? data : memset(data, 0, length);
}
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
virtual void Free(void* data, size_t) { free(data); }
};
FuzzerSupport::FuzzerSupport(int* argc, char*** argv) {
v8::V8::SetFlagsFromCommandLine(argc, *argv, true);
v8::V8::InitializeICU();
v8::V8::InitializeExternalStartupData((*argv)[0]);
platform_ = v8::platform::CreateDefaultPlatform();
v8::V8::InitializePlatform(platform_);
v8::V8::Initialize();
allocator_ = new ArrayBufferAllocator;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = allocator_;
isolate_ = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope(isolate_);
context_.Reset(isolate_, v8::Context::New(isolate_));
}
}
FuzzerSupport::~FuzzerSupport() {
{
v8::Isolate::Scope isolate_scope(isolate_);
while (v8::platform::PumpMessageLoop(platform_, isolate_)) /* empty */
;
v8::HandleScope handle_scope(isolate_);
context_.Reset();
}
isolate_->Dispose();
isolate_ = nullptr;
delete allocator_;
allocator_ = nullptr;
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete platform_;
platform_ = nullptr;
}
// static
FuzzerSupport* FuzzerSupport::Get() { return g_fuzzer_support; }
v8::Isolate* FuzzerSupport::GetIsolate() { return isolate_; }
v8::Local<v8::Context> FuzzerSupport::GetContext() {
v8::Isolate::Scope isolate_scope(isolate_);
v8::EscapableHandleScope handle_scope(isolate_);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate_, context_);
return handle_scope.Escape(context);
}
} // namespace v8_fuzzer
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
v8_fuzzer::g_fuzzer_support = new v8_fuzzer::FuzzerSupport(argc, argv);
atexit(&v8_fuzzer::DeleteFuzzerSupport);
return 0;
}
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TEST_FUZZER_FUZZER_SUPPORT_H_
#define TEST_FUZZER_FUZZER_SUPPORT_H_
#include "include/v8.h"
namespace v8_fuzzer {
class FuzzerSupport {
public:
FuzzerSupport(int* argc, char*** argv);
~FuzzerSupport();
static FuzzerSupport* Get();
v8::Isolate* GetIsolate();
v8::Local<v8::Context> GetContext();
private:
// Prevent copying. Not implemented.
FuzzerSupport(const FuzzerSupport&);
FuzzerSupport& operator=(const FuzzerSupport&);
class ArrayBufferAllocator;
v8::Platform* platform_;
ArrayBufferAllocator* allocator_;
v8::Isolate* isolate_;
v8::Global<v8::Context> context_;
};
} // namespace
#endif // TEST_FUZZER_FUZZER_SUPPORT_H_
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
int main(int argc, char* argv[]) {
if (LLVMFuzzerInitialize(&argc, &argv)) {
fprintf(stderr, "Failed to initialize fuzzer target\n");
return 1;
}
if (argc < 2) {
fprintf(stderr, "USAGE: %s <input>\n", argv[0]);
return 1;
}
FILE* input = fopen(argv[1], "rb");
if (!input) {
fprintf(stderr, "Failed to open '%s'\n", argv[1]);
return 1;
}
fseek(input, 0, SEEK_END);
long size = ftell(input);
fseek(input, 0, SEEK_SET);
uint8_t* data = reinterpret_cast<uint8_t*>(malloc(size));
if (!data) {
fclose(input);
fprintf(stderr, "Failed to allocate %ld bytes\n", size);
return 1;
}
size_t bytes_read = fread(data, 1, size, input);
fclose(input);
if (bytes_read != size) {
free(data);
fprintf(stderr, "Failed to read %s\n", argv[1]);
return 1;
}
int result = LLVMFuzzerTestOneInput(data, size);
free(data);
return result;
}
# Copyright 2016 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'variables': {
'v8_code': 1,
},
'includes': ['../../build/toolchain.gypi', '../../build/features.gypi'],
'targets': [
{
'target_name': 'parser_fuzzer',
'type': 'executable',
'dependencies': [
'parser_fuzzer_lib',
],
'include_dirs': [
'../..',
],
'sources': [
'fuzzer.cc',
],
},
{
'target_name': 'parser_fuzzer_lib',
'type': 'static_library',
'dependencies': [
'fuzzer_support',
],
'include_dirs': [
'../..',
],
'sources': [ ### gcmole(all) ###
'parser.cc',
],
},
{
'target_name': 'fuzzer_support',
'type': 'static_library',
'dependencies': [
'../../tools/gyp/v8.gyp:v8_libplatform',
],
'include_dirs': [
'../..',
],
'sources': [ ### gcmole(all) ###
'fuzzer-support.cc',
'fuzzer-support.h',
],
'conditions': [
['component=="shared_library"', {
# fuzzers can't be built against a shared library, so we need to
# depend on the underlying static target in that case.
'dependencies': ['../../tools/gyp/v8.gyp:v8_maybe_snapshot'],
}, {
'dependencies': ['../../tools/gyp/v8.gyp:v8'],
}],
],
},
],
'conditions': [
['test_isolation_mode != "noop"', {
'targets': [
{
'target_name': 'fuzzer_run',
'type': 'none',
'dependencies': [
'parser_fuzzer',
],
'includes': [
'../../build/isolate.gypi',
],
'sources': [
'fuzzer.isolate',
],
},
],
}],
],
}
# Copyright 2016 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'variables': {
'files': [
'<(PRODUCT_DIR)/parser_fuzzer<(EXECUTABLE_SUFFIX)',
'./fuzzer.status',
'./testcfg.py',
'./parser/',
],
},
'includes': [
'../../src/base.isolate',
'../../tools/testrunner/testrunner.isolate',
],
}
# Copyright 2016 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
[
]
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include "include/v8.h"
#include "src/objects.h"
#include "src/parsing/parser.h"
#include "src/parsing/preparser.h"
#include "test/fuzzer/fuzzer-support.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
v8::TryCatch try_catch(isolate);
v8::internal::Isolate* i_isolate =
reinterpret_cast<v8::internal::Isolate*>(isolate);
v8::internal::Factory* factory = i_isolate->factory();
if (size > INT_MAX) return 0;
v8::internal::MaybeHandle<v8::internal::String> source =
factory->NewStringFromOneByte(
v8::internal::Vector<const uint8_t>(data, static_cast<int>(size)));
if (source.is_null()) return 0;
v8::internal::Handle<v8::internal::Script> script =
factory->NewScript(source.ToHandleChecked());
v8::internal::Zone zone;
v8::internal::ParseInfo info(&zone, script);
info.set_global();
v8::internal::Parser parser(&info);
parser.Parse(&info);
return 0;
}
console.log('hello world');
# Copyright 2016 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
from testrunner.local import testsuite
from testrunner.objects import testcase
class FuzzerTestSuite(testsuite.TestSuite):
SUB_TESTS = ( 'parser', )
def __init__(self, name, root):
super(FuzzerTestSuite, self).__init__(name, root)
def ListTests(self, context):
tests = []
for subtest in FuzzerTestSuite.SUB_TESTS:
shell = '%s_fuzzer' % subtest
for fname in os.listdir(os.path.join(self.root, subtest)):
if not os.path.isfile(os.path.join(self.root, subtest, fname)):
continue
test = testcase.TestCase(self, '%s/%s' % (subtest, fname),
override_shell=shell)
tests.append(test)
tests.sort()
return tests
def GetFlagsForTestCase(self, testcase, context):
suite, name = testcase.path.split('/')
return [os.path.join(self.root, suite, name)]
def GetSuite(name, root):
return FuzzerTestSuite(name, root)
......@@ -60,27 +60,33 @@ ARCH_GUESS = utils.DefaultArch()
# expected runtimes (suites with slow test cases first). These groups are
# invoked in seperate steps on the bots.
TEST_MAP = {
# This needs to stay in sync with test/bot_default.isolate.
"bot_default": [
"mjsunit",
"cctest",
"webkit",
"fuzzer",
"message",
"preparser",
"intl",
"unittests",
],
# This needs to stay in sync with test/default.isolate.
"default": [
"mjsunit",
"cctest",
"fuzzer",
"message",
"preparser",
"intl",
"unittests",
],
# This needs to stay in sync with test/ignition.isolate.
"ignition": [
"mjsunit",
"cctest",
],
# This needs to stay in sync with test/optimize_for_size.isolate.
"optimize_for_size": [
"mjsunit",
"cctest",
......
......@@ -82,7 +82,7 @@ def MakeProcessContext(context):
def GetCommand(test, context):
d8testflag = []
shell = test.suite.shell()
shell = test.shell()
if shell == "d8":
d8testflag = ["--test"]
if utils.IsWindows():
......
......@@ -30,12 +30,13 @@ from . import output
class TestCase(object):
def __init__(self, suite, path, variant='default', flags=None,
dependency=None):
dependency=None, override_shell=None):
self.suite = suite # TestSuite object
self.path = path # string, e.g. 'div-mod', 'test-api/foo'
self.flags = flags or [] # list of strings, flags specific to this test
self.variant = variant # name of the used testing variant
self.dependency = dependency # |path| for testcase that must be run first
self.override_shell = override_shell
self.outcomes = set([])
self.output = None
self.id = None # int, used to map result back to TestCase instance
......@@ -44,7 +45,7 @@ class TestCase(object):
def CopyAddingFlags(self, variant, flags):
copy = TestCase(self.suite, self.path, variant, self.flags + flags,
self.dependency)
self.dependency, self.override_shell)
copy.outcomes = self.outcomes
return copy
......@@ -55,15 +56,16 @@ class TestCase(object):
"""
assert self.id is not None
return [self.suitename(), self.path, self.variant, self.flags,
self.dependency, list(self.outcomes or []), self.id]
self.dependency, self.override_shell, list(self.outcomes or []),
self.id]
@staticmethod
def UnpackTask(task):
"""Creates a new TestCase object based on packed task data."""
# For the order of the fields, refer to PackTask() above.
test = TestCase(str(task[0]), task[1], task[2], task[3], task[4])
test.outcomes = set(task[5])
test.id = task[6]
test = TestCase(str(task[0]), task[1], task[2], task[3], task[4], task[5])
test.outcomes = set(task[6])
test.id = task[7]
test.run = 1
return test
......@@ -87,6 +89,11 @@ class TestCase(object):
def GetLabel(self):
return self.suitename() + "/" + self.suite.CommonTestName(self)
def shell(self):
if self.override_shell:
return self.override_shell
return self.suite.shell()
def __getstate__(self):
"""Representation to pickle test cases.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment