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:
Christian Holler 2018-01-17 17:05:04 +01:00
Родитель 4ce6f81fee
Коммит 08e812cc03
9 изменённых файлов: 339 добавлений и 0 удалений

Просмотреть файл

@ -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/',
]

31
js/src/fuzz-tests/README Normal file
Просмотреть файл

@ -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 */
);

151
js/src/fuzz-tests/tests.cpp Normal file
Просмотреть файл

@ -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;
}

19
js/src/fuzz-tests/tests.h Normal file
Просмотреть файл

@ -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',
]