summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src/xpcrtfuzzing/xpcrtfuzzing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/src/xpcrtfuzzing/xpcrtfuzzing.cpp')
-rw-r--r--js/xpconnect/src/xpcrtfuzzing/xpcrtfuzzing.cpp162
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;
+}