xpcrtfuzzing.cpp (4950B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "xpcrtfuzzing/xpcrtfuzzing.h" 8 9 #include "mozilla/Assertions.h" // MOZ_CRASH 10 #include "mozilla/Utf8.h" // mozilla::Utf8Unit 11 12 #include <stdio.h> // fflush, fprintf, fputs 13 14 #include "FuzzingInterface.h" 15 #include "jsapi.h" 16 17 #include "js/CompilationAndEvaluation.h" // JS::Evaluate 18 #include "js/CompileOptions.h" // JS::CompileOptions 19 #include "js/Conversions.h" // JS::Conversions 20 #include "js/ErrorReport.h" // JS::PrintError 21 #include "js/Exception.h" // JS::StealPendingExceptionStack 22 #include "js/experimental/TypedData.h" // JS_GetUint8ClampedArrayData, JS_NewUint8ClampedArray 23 #include "js/PropertyAndElement.h" // JS_SetProperty, JS_HasOwnProperty 24 #include "js/RootingAPI.h" // JS::Rooted 25 #include "js/SourceText.h" // JS::Source{Ownership,Text} 26 #include "js/Value.h" // JS::Value 27 28 using mozilla::dom::AutoJSAPI; 29 30 static AutoJSAPI* gJsapi = nullptr; 31 MOZ_RUNINIT static std::string gFuzzModuleName; 32 33 static void CrashOnPendingException() { 34 if (gJsapi->HasException()) { 35 gJsapi->ReportException(); 36 37 MOZ_CRASH("Unhandled exception from JS runtime!"); 38 } 39 } 40 41 int FuzzXPCRuntimeStart(AutoJSAPI* jsapi, int* argc, char*** argv, 42 const XREShellData* aShellData) { 43 gFuzzModuleName = getenv("FUZZER"); 44 gJsapi = jsapi; 45 46 int ret = FuzzXPCRuntimeInit(); 47 if (ret) { 48 fprintf(stderr, "Fuzzing Interface: Error: Initialize callback failed\n"); 49 return ret; 50 } 51 52 #ifdef AFLFUZZ 53 ret = aShellData->fuzzerDriver(FuzzXPCRuntimeFuzz); 54 #else 55 ret = aShellData->fuzzerDriver(argc, argv, FuzzXPCRuntimeFuzz); 56 #endif 57 if (!ret) { 58 fprintf(stdout, "Trying to shutdown!\n"); 59 int shutdown = FuzzXPCRuntimeShutdown(); 60 if (shutdown) { 61 fprintf(stderr, "Fuzzing Interface: Error: Shutdown callback failed\n"); 62 return shutdown; 63 } 64 } 65 66 return ret; 67 } 68 69 int FuzzXPCRuntimeInit() { 70 JSContext* cx = gJsapi->cx(); 71 JS::Rooted<JS::Value> v(cx); 72 JS::CompileOptions opts(cx); 73 74 // Load the fuzzing module specified in the FUZZER environment variable 75 JS::EvaluateUtf8Path(cx, opts, gFuzzModuleName.c_str(), &v); 76 77 // Any errors while loading the fuzzing module should be fatal 78 CrashOnPendingException(); 79 80 return 0; 81 } 82 83 int FuzzXPCRuntimeFuzz(const uint8_t* buf, size_t size) { 84 if (!size) { 85 return 0; 86 } 87 88 JSContext* cx = gJsapi->cx(); 89 90 JS::Rooted<JSObject*> arr(cx, JS_NewUint8ClampedArray(cx, size)); 91 if (!arr) { 92 MOZ_CRASH("OOM"); 93 } 94 95 do { 96 JS::AutoCheckCannotGC nogc; 97 bool isShared; 98 uint8_t* data = JS_GetUint8ClampedArrayData(arr, &isShared, nogc); 99 MOZ_RELEASE_ASSERT(!isShared); 100 memcpy(data, buf, size); 101 } while (false); 102 103 JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx)); 104 JS::RootedValue arrVal(cx, JS::ObjectValue(*arr)); 105 if (!JS_SetProperty(cx, global, "fuzzBuf", arrVal)) { 106 MOZ_CRASH("JS_SetProperty failed"); 107 } 108 109 JS::Rooted<JS::Value> v(cx); 110 JS::CompileOptions opts(cx); 111 112 static const char data[] = "JSFuzzIterate();"; 113 114 JS::SourceText<mozilla::Utf8Unit> srcBuf; 115 if (!srcBuf.init(cx, data, strlen(data), JS::SourceOwnership::Borrowed)) { 116 return 1; 117 } 118 119 if (!JS::Evaluate(cx, opts.setFileAndLine(__FILE__, __LINE__), srcBuf, &v) && 120 !JS_IsExceptionPending(cx)) { 121 // A return value of `false` without a pending exception indicates 122 // a timeout as triggered by the `timeout` shell function. 123 return 1; 124 } 125 126 // The fuzzing module is required to handle any exceptions 127 CrashOnPendingException(); 128 129 int32_t ret = 0; 130 if (!ToInt32(cx, v, &ret)) { 131 MOZ_CRASH("Must return an int32 compatible return value!"); 132 } 133 134 return ret; 135 } 136 137 int FuzzXPCRuntimeShutdown() { 138 JSContext* cx = gJsapi->cx(); 139 JS::Rooted<JS::Value> v(cx); 140 JS::CompileOptions opts(cx); 141 JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx)); 142 143 bool found = false; 144 if (JS_HasOwnProperty(cx, global, "JSFuzzShutdown", &found)) { 145 if (found) { 146 static const char data[] = "JSFuzzShutdown();"; 147 JS::SourceText<mozilla::Utf8Unit> srcBuf; 148 if (!srcBuf.init(cx, data, strlen(data), JS::SourceOwnership::Borrowed)) { 149 return 1; 150 } 151 152 if (!JS::Evaluate(cx, opts.setFileAndLine(__FILE__, __LINE__), srcBuf, 153 &v) && 154 !JS_IsExceptionPending(cx)) { 155 // A return value of `false` without a pending exception indicates 156 // a timeout as triggered by the `timeout` shell function. 157 return 1; 158 } 159 } 160 } 161 162 // The fuzzing module is required to handle any exceptions 163 CrashOnPendingException(); 164 165 return 0; 166 }