diff options
author | Luke <luke.wilde@live.co.uk> | 2020-11-07 03:38:18 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-11-07 10:09:55 +0100 |
commit | 8928607455789981545964f13d2ff7fd1847b9e0 (patch) | |
tree | d043b67673554490c724006e850b192fffd8af29 | |
parent | f5aad71c159fc803a1ab9f07d04bbb12ad1ab650 (diff) | |
download | serenity-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.txt | 9 | ||||
-rw-r--r-- | Meta/Lagom/Fuzzers/FuzzilliJs.cpp | 258 | ||||
-rw-r--r-- | Meta/Lagom/Fuzzers/FuzzilliJsInstructions.md | 8 | ||||
-rw-r--r-- | Meta/Lagom/Fuzzers/add-serenity-support-to-fuzzilli.patch | 44 |
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, + ] |