summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke <luke.wilde@live.co.uk>2020-11-07 03:38:18 +0000
committerAndreas Kling <kling@serenityos.org>2020-11-07 10:09:55 +0100
commit8928607455789981545964f13d2ff7fd1847b9e0 (patch)
treed043b67673554490c724006e850b192fffd8af29
parentf5aad71c159fc803a1ab9f07d04bbb12ad1ab650 (diff)
downloadserenity-8928607455789981545964f13d2ff7fd1847b9e0.zip
Lagom/Fuzzers: Add Fuzzilli version of FuzzJs
Fuzzilli is a JavaScript engine fuzzer made by googleprojectzero. https://github.com/googleprojectzero/fuzzilli/
-rw-r--r--Meta/Lagom/Fuzzers/CMakeLists.txt9
-rw-r--r--Meta/Lagom/Fuzzers/FuzzilliJs.cpp258
-rw-r--r--Meta/Lagom/Fuzzers/FuzzilliJsInstructions.md8
-rw-r--r--Meta/Lagom/Fuzzers/add-serenity-support-to-fuzzilli.patch44
4 files changed, 319 insertions, 0 deletions
diff --git a/Meta/Lagom/Fuzzers/CMakeLists.txt b/Meta/Lagom/Fuzzers/CMakeLists.txt
index a52bb14723..a974296c6f 100644
--- a/Meta/Lagom/Fuzzers/CMakeLists.txt
+++ b/Meta/Lagom/Fuzzers/CMakeLists.txt
@@ -25,6 +25,15 @@ target_link_libraries(FuzzJs
PRIVATE $<$<C_COMPILER_ID:Clang>:-fsanitize=fuzzer>
)
+add_executable(FuzzilliJs FuzzilliJs.cpp)
+target_compile_options(FuzzilliJs
+ PRIVATE $<$<C_COMPILER_ID:Clang>:-g -O1 -fsanitize-coverage=trace-pc-guard>
+ )
+target_link_libraries(FuzzilliJs
+ PUBLIC Lagom
+ PRIVATE $<$<C_COMPILER_ID:Clang>:-fsanitize-coverage=trace-pc-guard>
+ )
+
add_executable(FuzzMarkdown FuzzMarkdown.cpp)
target_compile_options(FuzzMarkdown
PRIVATE $<$<C_COMPILER_ID:Clang>:-g -O1 -fsanitize=fuzzer>
diff --git a/Meta/Lagom/Fuzzers/FuzzilliJs.cpp b/Meta/Lagom/Fuzzers/FuzzilliJs.cpp
new file mode 100644
index 0000000000..3afb898ce3
--- /dev/null
+++ b/Meta/Lagom/Fuzzers/FuzzilliJs.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Function.h>
+#include <AK/String.h>
+#include <AK/StringView.h>
+#include <LibJS/Forward.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Lexer.h>
+#include <LibJS/Parser.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <signal.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <sys/mman.h>
+
+#include <sys/stat.h>
+
+//
+// BEGIN FUZZING CODE
+//
+
+#define REPRL_CRFD 100
+#define REPRL_CWFD 101
+#define REPRL_DRFD 102
+#define REPRL_DWFD 103
+#define REPRL_MAX_DATA_SIZE (16 * 1024 * 1024)
+
+#define SHM_SIZE 0x100000
+#define MAX_EDGES ((SHM_SIZE - 4) * 8)
+
+#define CHECK(cond) \
+ if (!(cond)) { \
+ fprintf(stderr, "\"" #cond "\" failed\n"); \
+ _exit(-1); \
+ }
+
+struct shmem_data {
+ uint32_t num_edges;
+ unsigned char edges[];
+};
+
+struct shmem_data* __shmem;
+uint32_t *__edges_start, *__edges_stop;
+
+void __sanitizer_cov_reset_edgeguards()
+{
+ uint64_t N = 0;
+ for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
+ *x = ++N;
+}
+
+extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop)
+{
+ // Avoid duplicate initialization
+ if (start == stop || *start)
+ return;
+
+ if (__edges_start != NULL || __edges_stop != NULL) {
+ fprintf(stderr, "Coverage instrumentation is only supported for a single module\n");
+ _exit(-1);
+ }
+
+ __edges_start = start;
+ __edges_stop = stop;
+
+ // Map the shared memory region
+ const char* shm_key = getenv("SHM_ID");
+ if (!shm_key) {
+ puts("[COV] no shared memory bitmap available, skipping");
+ __shmem = (struct shmem_data*)malloc(SHM_SIZE);
+ } else {
+ int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE);
+ if (fd <= -1) {
+ fprintf(stderr, "Failed to open shared memory region: %s\n", strerror(errno));
+ _exit(-1);
+ }
+
+ __shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (__shmem == MAP_FAILED) {
+ fprintf(stderr, "Failed to mmap shared memory region\n");
+ _exit(-1);
+ }
+ }
+
+ __sanitizer_cov_reset_edgeguards();
+
+ __shmem->num_edges = stop - start;
+ printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n", shm_key, __shmem->num_edges);
+}
+
+extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard)
+{
+ // There's a small race condition here: if this function executes in two threads for the same
+ // edge at the same time, the first thread might disable the edge (by setting the guard to zero)
+ // before the second thread fetches the guard value (and thus the index). However, our
+ // instrumentation ignores the first edge (see libcoverage.c) and so the race is unproblematic.
+ uint32_t index = *guard;
+ // If this function is called before coverage instrumentation is properly initialized we want to return early.
+ if (!index)
+ return;
+ __shmem->edges[index / 8] |= 1 << (index % 8);
+ *guard = 0;
+}
+
+//
+// END FUZZING CODE
+//
+
+class TestRunnerGlobalObject : public JS::GlobalObject {
+public:
+ TestRunnerGlobalObject();
+ virtual ~TestRunnerGlobalObject() override;
+
+ virtual void initialize() override;
+
+private:
+ virtual const char* class_name() const override { return "TestRunnerGlobalObject"; }
+
+ JS_DECLARE_NATIVE_FUNCTION(fuzzilli);
+};
+
+TestRunnerGlobalObject::TestRunnerGlobalObject()
+{
+}
+
+TestRunnerGlobalObject::~TestRunnerGlobalObject()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(TestRunnerGlobalObject::fuzzilli)
+{
+ if (!vm.argument_count())
+ return JS::js_undefined();
+
+ auto operation = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ if (operation == "FUZZILLI_CRASH") {
+ auto type = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ switch (type) {
+ case 0:
+ *((int*)0x41414141) = 0x1337;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ } else if (operation == "FUZZILLI_PRINT") {
+ static FILE* fzliout = fdopen(REPRL_DWFD, "w");
+ if (!fzliout) {
+ dbg() << "Fuzzer output not available";
+ fzliout = stdout;
+ }
+
+ auto string = vm.argument(1).to_string(global_object);
+ if (vm.exception())
+ return {};
+ fprintf(fzliout, "%s\n", string.characters());
+ fflush(fzliout);
+ }
+
+ return JS::js_undefined();
+}
+
+void TestRunnerGlobalObject::initialize()
+{
+ JS::GlobalObject::initialize();
+ define_property("global", this, JS::Attribute::Enumerable);
+ define_native_function("fuzzilli", fuzzilli, 2);
+}
+
+int main(int, char**)
+{
+ char* reprl_input = nullptr;
+
+ char helo[] = "HELO";
+ if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
+ ASSERT_NOT_REACHED();
+ }
+
+ ASSERT(memcmp(helo, "HELO", 4) == 0);
+ reprl_input = (char*)mmap(0, REPRL_MAX_DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, REPRL_DRFD, 0);
+ ASSERT(reprl_input != MAP_FAILED);
+
+ auto vm = JS::VM::create();
+ auto interpreter = JS::Interpreter::create<TestRunnerGlobalObject>(*vm);
+
+ while (true) {
+ unsigned action;
+ ASSERT(read(REPRL_CRFD, &action, 4) == 4);
+ ASSERT(action == 'cexe');
+
+ size_t script_size;
+ ASSERT(read(REPRL_CRFD, &script_size, 8) == 8);
+ ASSERT(script_size < REPRL_MAX_DATA_SIZE);
+ ByteBuffer data_buffer;
+ if (data_buffer.size() < script_size)
+ data_buffer.grow(script_size - data_buffer.size());
+ ASSERT(data_buffer.size() >= script_size);
+ memcpy(data_buffer.data(), reprl_input, script_size);
+
+ int result = 0;
+
+ auto js = AK::StringView(static_cast<const unsigned char*>(data_buffer.data()), script_size);
+
+ auto lexer = JS::Lexer(js);
+ auto parser = JS::Parser(lexer);
+ auto program = parser.parse_program();
+ if (parser.has_errors()) {
+ result = 1;
+ } else {
+ interpreter->run(interpreter->global_object(), *program);
+ if (interpreter->exception()) {
+ result = 1;
+ vm->clear_exception();
+ }
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+
+ int status = (result & 0xff) << 8;
+ ASSERT(write(REPRL_CWFD, &status, 4) == 4);
+ __sanitizer_cov_reset_edgeguards();
+ }
+
+ return 0;
+}
diff --git a/Meta/Lagom/Fuzzers/FuzzilliJsInstructions.md b/Meta/Lagom/Fuzzers/FuzzilliJsInstructions.md
new file mode 100644
index 0000000000..425d8d38d6
--- /dev/null
+++ b/Meta/Lagom/Fuzzers/FuzzilliJsInstructions.md
@@ -0,0 +1,8 @@
+# How to use FuzzilliJs
+
+1. Download a copy of the Fuzzilli repo from https://github.com/googleprojectzero/fuzzilli
+2. Install Swift and make sure it's in your path environment variable.
+3. Build FuzzilliJs as you would the other fuzzers. [See README.md in the parent folder.](https://github.com/SerenityOS/serenity/blob/master/Meta/Lagom/ReadMe.md)
+4. Apply the add-serenity-support-to-fuzzilli.patch patch file to the Fuzzilli root directory. ```patch -p1 < /path/to/add-serenity-support-to-fuzzilli.patch```
+5. Build Fuzzilli with ```swift build -c release```
+6. Run Fuzzilli with ```swift run -c release FuzzilliCli --profile=serenity /path/to/FuzzilliJs```. See ```swift run FuzzilliCli --help``` for options.
diff --git a/Meta/Lagom/Fuzzers/add-serenity-support-to-fuzzilli.patch b/Meta/Lagom/Fuzzers/add-serenity-support-to-fuzzilli.patch
new file mode 100644
index 0000000000..a36902dfbb
--- /dev/null
+++ b/Meta/Lagom/Fuzzers/add-serenity-support-to-fuzzilli.patch
@@ -0,0 +1,44 @@
+diff --git a/Sources/FuzzilliCli/Profiles/Profile.swift b/Sources/FuzzilliCli/Profiles/Profile.swift
+index 6d8a795..a506d41 100644
+--- a/Sources/FuzzilliCli/Profiles/Profile.swift
++++ b/Sources/FuzzilliCli/Profiles/Profile.swift
+@@ -32,6 +32,33 @@ struct Profile {
+ let additionalBuiltins: [String: Type]
+ }
+
++let serenityProfile = Profile(
++ processArguments: [""],
++
++ processEnv: ["UBSAN_OPTIONS":"handle_segv=0 handle_abrt=0"],
++
++ codePrefix: """
++ function main() {
++ """,
++
++ codeSuffix: """
++ }
++ main();
++ """,
++
++ ecmaVersion: ECMAScriptVersion.es6,
++
++ crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)"],
++
++ additionalCodeGenerators: WeightedList<CodeGenerator>([]),
++
++ disabledCodeGenerators: [],
++
++ additionalBuiltins: [
++ "gc" : .function([] => .undefined)
++ ]
++)
++
+ let profiles = [
+ "qjs": qjsProfile,
+ "jsc": jscProfile,
+@@ -39,4 +66,5 @@ let profiles = [
+ "v8": v8Profile,
+ "duktape": duktapeProfile,
+ "jerryscript": jerryscriptProfile,
++ "serenity": serenityProfile,
+ ]