diff options
Diffstat (limited to 'js/xpconnect/src/xpcrtfuzzing/xpcrtfuzzing.cpp')
-rw-r--r-- | js/xpconnect/src/xpcrtfuzzing/xpcrtfuzzing.cpp | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/js/xpconnect/src/xpcrtfuzzing/xpcrtfuzzing.cpp b/js/xpconnect/src/xpcrtfuzzing/xpcrtfuzzing.cpp new file mode 100644 index 0000000000..95982733cd --- /dev/null +++ b/js/xpconnect/src/xpcrtfuzzing/xpcrtfuzzing.cpp @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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 "xpcrtfuzzing/xpcrtfuzzing.h" + +#include "mozilla/Assertions.h" // MOZ_CRASH +#include "mozilla/Utf8.h" // mozilla::Utf8Unit + +#include <stdio.h> // fflush, fprintf, fputs + +#include "FuzzingInterface.h" +#include "jsapi.h" + +#include "js/CompilationAndEvaluation.h" // JS::Evaluate +#include "js/CompileOptions.h" // JS::CompileOptions +#include "js/Conversions.h" // JS::Conversions +#include "js/ErrorReport.h" // JS::PrintError +#include "js/Exception.h" // JS::StealPendingExceptionStack +#include "js/experimental/TypedData.h" // JS_GetUint8ClampedArrayData, JS_NewUint8ClampedArray +#include "js/PropertyAndElement.h" // JS_SetProperty, JS_HasOwnProperty +#include "js/RootingAPI.h" // JS::Rooted +#include "js/SourceText.h" // JS::Source{Ownership,Text} +#include "js/Value.h" // JS::Value + +using mozilla::dom::AutoJSAPI; + +static AutoJSAPI* gJsapi = nullptr; +static std::string gFuzzModuleName; + +static void CrashOnPendingException() { + if (gJsapi->HasException()) { + gJsapi->ReportException(); + + MOZ_CRASH("Unhandled exception from JS runtime!"); + } +} + +int FuzzXPCRuntimeStart(AutoJSAPI* jsapi, int* argc, char*** argv, + LibFuzzerDriver fuzzerDriver) { + gFuzzModuleName = getenv("FUZZER"); + gJsapi = jsapi; + + int ret = FuzzXPCRuntimeInit(); + if (ret) { + fprintf(stderr, "Fuzzing Interface: Error: Initialize callback failed\n"); + return ret; + } + + ret = fuzzerDriver(argc, argv, FuzzXPCRuntimeFuzz); + if (!ret) { + fprintf(stdout, "Trying to shutdown!\n"); + int shutdown = FuzzXPCRuntimeShutdown(); + if (shutdown) { + fprintf(stderr, "Fuzzing Interface: Error: Shutdown callback failed\n"); + return shutdown; + } + } + + return ret; +} + +int FuzzXPCRuntimeInit() { + JSContext* cx = gJsapi->cx(); + JS::Rooted<JS::Value> v(cx); + JS::CompileOptions opts(cx); + + // Load the fuzzing module specified in the FUZZER environment variable + JS::EvaluateUtf8Path(cx, opts, gFuzzModuleName.c_str(), &v); + + // Any errors while loading the fuzzing module should be fatal + CrashOnPendingException(); + + return 0; +} + +int FuzzXPCRuntimeFuzz(const uint8_t* buf, size_t size) { + if (!size) { + return 0; + } + + JSContext* cx = gJsapi->cx(); + + JS::Rooted<JSObject*> arr(cx, JS_NewUint8ClampedArray(cx, size)); + if (!arr) { + MOZ_CRASH("OOM"); + } + + do { + JS::AutoCheckCannotGC nogc; + bool isShared; + uint8_t* data = JS_GetUint8ClampedArrayData(arr, &isShared, nogc); + MOZ_RELEASE_ASSERT(!isShared); + memcpy(data, buf, size); + } while (false); + + JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx)); + JS::RootedValue arrVal(cx, JS::ObjectValue(*arr)); + if (!JS_SetProperty(cx, global, "fuzzBuf", arrVal)) { + MOZ_CRASH("JS_SetProperty failed"); + } + + JS::Rooted<JS::Value> v(cx); + JS::CompileOptions opts(cx); + + static const char data[] = "JSFuzzIterate();"; + + JS::SourceText<mozilla::Utf8Unit> srcBuf; + if (!srcBuf.init(cx, data, strlen(data), JS::SourceOwnership::Borrowed)) { + return 1; + } + + if (!JS::Evaluate(cx, opts.setFileAndLine(__FILE__, __LINE__), srcBuf, &v) && + !JS_IsExceptionPending(cx)) { + // A return value of `false` without a pending exception indicates + // a timeout as triggered by the `timeout` shell function. + return 1; + } + + // The fuzzing module is required to handle any exceptions + CrashOnPendingException(); + + int32_t ret = 0; + if (!ToInt32(cx, v, &ret)) { + MOZ_CRASH("Must return an int32 compatible return value!"); + } + + return ret; +} + +int FuzzXPCRuntimeShutdown() { + JSContext* cx = gJsapi->cx(); + JS::Rooted<JS::Value> v(cx); + JS::CompileOptions opts(cx); + JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx)); + + bool found = false; + if (JS_HasOwnProperty(cx, global, "JSFuzzShutdown", &found)) { + if (found) { + static const char data[] = "JSFuzzShutdown();"; + JS::SourceText<mozilla::Utf8Unit> srcBuf; + if (!srcBuf.init(cx, data, strlen(data), JS::SourceOwnership::Borrowed)) { + return 1; + } + + if (!JS::Evaluate(cx, opts.setFileAndLine(__FILE__, __LINE__), srcBuf, + &v) && + !JS_IsExceptionPending(cx)) { + // A return value of `false` without a pending exception indicates + // a timeout as triggered by the `timeout` shell function. + return 1; + } + } + } + + // The fuzzing module is required to handle any exceptions + CrashOnPendingException(); + + return 0; +} |