зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1431090 - Add FuzzingInterface support to JS engine. r=froydnj
This patch adds basic support for the fuzzing interface in the JS engine on top of the last patch. This includes all the necessary code except for actual targets (just an example target skeleton) and also makes sure that the fuzzing code is packaged for the standalone release. MozReview-Commit-ID: D6Tyebz3jZS --HG-- extra : rebase_source : 58e4d85e657347b061de0ed912365f2a955a86e3
This commit is contained in:
Родитель
4ce6f81fee
Коммит
08e812cc03
|
@ -69,6 +69,8 @@ included_inclnames_to_ignore = set([
|
|||
'jscustomallocator.h', # provided by embedders; allowed to be missing
|
||||
'js-config.h', # generated in $OBJDIR
|
||||
'fdlibm.h', # fdlibm
|
||||
'FuzzerDefs.h', # included without a path
|
||||
'FuzzingInterface.h', # included without a path
|
||||
'mozmemory.h', # included without a path
|
||||
'pratom.h', # NSPR
|
||||
'prcvar.h', # NSPR
|
||||
|
|
|
@ -36,3 +36,8 @@ if CONFIG['COMPILE_ENVIRONMENT'] and CONFIG['BUILD_CTYPES']:
|
|||
'/config/external/ffi',
|
||||
]
|
||||
|
||||
if CONFIG['JS_STANDALONE'] and CONFIG['FUZZING']:
|
||||
DIRS += [
|
||||
'/tools/fuzzing/',
|
||||
]
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# JS Fuzzing Interface
|
||||
|
||||
This directory contains fuzzing targets that implement the unified fuzzing
|
||||
interface to be used with libFuzzer or AFL.
|
||||
|
||||
## Building the fuzzing targets
|
||||
|
||||
To include this directory in your JS build, you need to build with Clang
|
||||
and the --enable-fuzzing flag enabled. The build system will automatically
|
||||
detect if you are building with afl-clang-fast for AFL or regular Clang
|
||||
for libFuzzer.
|
||||
|
||||
## Running a fuzzing target
|
||||
|
||||
To run a particular target with libFuzzer, use:
|
||||
|
||||
cd $OBJDIR/dist/bin
|
||||
FUZZER=YourTargetName ./fuzz-tests
|
||||
|
||||
To run with AFL, use something like
|
||||
|
||||
cd $OBJDIR/dist/bin
|
||||
FUZZER=YourTargetName MOZ_FUZZ_TESTFILE=input \
|
||||
afl-fuzz <regular AFL options> -f input ./fuzz-tests
|
||||
|
||||
|
||||
## Writing a fuzzing target
|
||||
|
||||
1. Check testExample.cpp for a target skeleton with comments.
|
||||
|
||||
2. Add your own .cpp file to UNIFIED_SOURCES in moz.build
|
|
@ -0,0 +1,52 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
GeckoProgram('fuzz-tests', linkage=None)
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'testExample.cpp',
|
||||
'tests.cpp',
|
||||
]
|
||||
|
||||
DEFINES['EXPORT_JS_API'] = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'!..',
|
||||
'..',
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_INTL_API'] and CONFIG['MOZ_ICU_DATA_ARCHIVE']:
|
||||
# The ICU libraries linked into libmozjs will not include the ICU data,
|
||||
# so link it directly.
|
||||
USE_LIBS += ['icudata']
|
||||
|
||||
if CONFIG['FUZZING']:
|
||||
USE_LIBS += [
|
||||
'static:fuzzer-registry',
|
||||
]
|
||||
|
||||
if CONFIG['LIBFUZZER']:
|
||||
USE_LIBS += [
|
||||
'static:fuzzer',
|
||||
]
|
||||
|
||||
# Add trace-pc coverage for libfuzzer
|
||||
CFLAGS += ['-fsanitize-coverage=trace-pc-guard']
|
||||
CXXFLAGS += ['-fsanitize-coverage=trace-pc-guard']
|
||||
|
||||
USE_LIBS += [
|
||||
'static:js',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_NEEDS_LIBATOMIC']:
|
||||
OS_LIBS += ['atomic']
|
||||
|
||||
OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
|
||||
|
||||
if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
|
||||
CXXFLAGS += ['-Wno-shadow', '-Werror=format', '-fno-strict-aliasing']
|
||||
|
||||
DEFINES['topsrcdir'] = '%s/js/src' % TOPSRCDIR
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "jsapi.h"
|
||||
#include "fuzz-tests/tests.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "jscntxtinlines.h"
|
||||
|
||||
using namespace JS;
|
||||
using namespace js;
|
||||
|
||||
extern JS::PersistentRootedObject gGlobal;
|
||||
extern JSContext* gCx;
|
||||
|
||||
static int
|
||||
testExampleInit(int *argc, char ***argv) {
|
||||
/* This function is called once at startup. You can use it to e.g. read
|
||||
environment variables to initialize additional options you might need.
|
||||
Note that `gCx` and `gGlobal` are pre-initialized by the harness.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
testExampleFuzz(const uint8_t* buf, size_t size)
|
||||
{
|
||||
/* If your code directly or indirectly allocates GC memory, then it makes sense
|
||||
to attempt and collect that after every iteration. This should detect GC issues
|
||||
as soon as possible (right after your iteration), rather than later when your
|
||||
code happens to trigger GC coincidentially. You can of course disable this code
|
||||
if it is not required in your use case, which will speed up fuzzing. */
|
||||
auto gcGuard = mozilla::MakeScopeExit([&] {
|
||||
JS::PrepareForFullGC(gCx);
|
||||
JS::GCForReason(gCx, GC_NORMAL, JS::gcreason::API);
|
||||
});
|
||||
|
||||
/* Add code here that processes the given buffer.
|
||||
While doing so, you need to follow these rules:
|
||||
|
||||
1. Do not modify or free the buffer. Make a copy if necessary.
|
||||
2. This function must always return 0.
|
||||
3. Do not crash or abort unless the condition constitutes a bug.
|
||||
4. You may use the `gGlobal` and `gCx` variables, they are pre-initialized.
|
||||
5. Try to keep the effects of this function contained, such that future
|
||||
calls to this function are not affected. Otherwise you end up with
|
||||
non-reproducible testcases and coverage measurements will be incorrect.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MOZ_FUZZING_INTERFACE_RAW(
|
||||
testExampleInit, /* init function */
|
||||
testExampleFuzz, /* fuzzing function */
|
||||
Example /* module name */
|
||||
);
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "fuzz-tests/tests.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "jsalloc.h"
|
||||
#include "jscntxt.h"
|
||||
#include "js/Initialization.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
#ifdef LIBFUZZER
|
||||
#include "FuzzerDefs.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
JS::PersistentRootedObject gGlobal;
|
||||
JSContext* gCx = nullptr;
|
||||
JSCompartment* gOldCompartment = nullptr;
|
||||
|
||||
static const JSClass*
|
||||
getGlobalClass()
|
||||
{
|
||||
static const JSClassOps cOps = {
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr,
|
||||
JS_GlobalObjectTraceHook
|
||||
};
|
||||
static const JSClass c = {
|
||||
"global", JSCLASS_GLOBAL_FLAGS,
|
||||
&cOps
|
||||
};
|
||||
return &c;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
jsfuzz_createGlobal(JSContext* cx, JSPrincipals* principals)
|
||||
{
|
||||
/* Create the global object. */
|
||||
JS::RootedObject newGlobal(cx);
|
||||
JS::CompartmentOptions options;
|
||||
#ifdef ENABLE_STREAMS
|
||||
options.creationOptions().setStreamsEnabled(true);
|
||||
#endif
|
||||
newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook,
|
||||
options);
|
||||
if (!newGlobal)
|
||||
return nullptr;
|
||||
|
||||
JSAutoCompartment ac(cx, newGlobal);
|
||||
|
||||
// Populate the global object with the standard globals like Object and
|
||||
// Array.
|
||||
if (!JS_InitStandardClasses(cx, newGlobal))
|
||||
return nullptr;
|
||||
|
||||
return newGlobal;
|
||||
}
|
||||
|
||||
static bool
|
||||
jsfuzz_init(JSContext** cx, JS::PersistentRootedObject* global)
|
||||
{
|
||||
*cx = JS_NewContext(8L * 1024 * 1024);
|
||||
if (!*cx)
|
||||
return false;
|
||||
|
||||
const size_t MAX_STACK_SIZE = 500000;
|
||||
|
||||
JS_SetNativeStackQuota(*cx, MAX_STACK_SIZE);
|
||||
|
||||
js::UseInternalJobQueues(*cx);
|
||||
if (!JS::InitSelfHostedCode(*cx))
|
||||
return false;
|
||||
JS_BeginRequest(*cx);
|
||||
global->init(*cx);
|
||||
*global = jsfuzz_createGlobal(*cx, nullptr);
|
||||
if (!*global)
|
||||
return false;
|
||||
JS_EnterCompartment(*cx, *global);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
jsfuzz_uninit(JSContext* cx, JSCompartment* oldCompartment)
|
||||
{
|
||||
if (oldCompartment) {
|
||||
JS_LeaveCompartment(cx, oldCompartment);
|
||||
oldCompartment = nullptr;
|
||||
}
|
||||
if (cx) {
|
||||
JS_EndRequest(cx);
|
||||
JS_DestroyContext(cx);
|
||||
cx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
if (!JS_Init()) {
|
||||
fprintf(stderr, "Error: Call to jsfuzz_init() failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!jsfuzz_init(&gCx, &gGlobal)) {
|
||||
fprintf(stderr, "Error: Call to jsfuzz_init() failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* fuzzerEnv = getenv("FUZZER");
|
||||
if (!fuzzerEnv) {
|
||||
fprintf(stderr, "Must specify fuzzing target in FUZZER environment variable\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string moduleNameStr(getenv("FUZZER"));
|
||||
|
||||
FuzzerFunctions funcs = FuzzerRegistry::getInstance().getModuleFunctions(moduleNameStr);
|
||||
FuzzerInitFunc initFunc = funcs.first;
|
||||
FuzzerTestingFunc testingFunc = funcs.second;
|
||||
if (initFunc) {
|
||||
int ret = initFunc(&argc, &argv);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Fuzzing Interface: Error: Initialize callback failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!testingFunc) {
|
||||
fprintf(stderr, "Fuzzing Interface: Error: No testing callback found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef LIBFUZZER
|
||||
fuzzer::FuzzerDriver(&argc, &argv, testingFunc);
|
||||
#elif __AFL_COMPILER
|
||||
testingFunc(nullptr, 0);
|
||||
#endif
|
||||
|
||||
jsfuzz_uninit(gCx, nullptr);
|
||||
|
||||
JS_ShutDown();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef fuzz_tests_tests_h
|
||||
#define fuzz_tests_tests_h
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "FuzzingInterface.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#endif /* fuzz_tests_tests_h */
|
|
@ -168,6 +168,13 @@ case $cmd in
|
|||
${TOPSRCDIR}/memory/fallible \
|
||||
${TOPSRCDIR}/memory/mozalloc \
|
||||
${tgtpath}/memory
|
||||
${MKDIR} -p ${tgtpath}/tools/fuzzing
|
||||
cp -pPR \
|
||||
${TOPSRCDIR}/tools/fuzzing/moz.build \
|
||||
${TOPSRCDIR}/tools/fuzzing/interface \
|
||||
${TOPSRCDIR}/tools/fuzzing/registry \
|
||||
${TOPSRCDIR}/tools/fuzzing/libfuzzer \
|
||||
${tgtpath}/tools/fuzzing
|
||||
|
||||
# remove *.pyc and *.pyo files if any
|
||||
find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" |xargs rm -f
|
||||
|
|
|
@ -50,6 +50,16 @@ if not CONFIG['JS_DISABLE_SHELL']:
|
|||
|
||||
TEST_DIRS += ['jsapi-tests', 'tests', 'gdb']
|
||||
|
||||
if CONFIG['FUZZING']:
|
||||
if CONFIG['LIBFUZZER']:
|
||||
# Add trace-pc coverage for libfuzzer
|
||||
CFLAGS += ['-fsanitize-coverage=trace-pc-guard']
|
||||
CXXFLAGS += ['-fsanitize-coverage=trace-pc-guard']
|
||||
|
||||
TEST_DIRS += [
|
||||
'fuzz-tests',
|
||||
]
|
||||
|
||||
CONFIGURE_SUBST_FILES += [
|
||||
'devtools/rootAnalysis/Makefile',
|
||||
]
|
||||
|
|
Загрузка…
Ссылка в новой задаче