In addition to the existing targets in Fuzzilli's github repo, we can add extra targets, and make fuzzilli works on more JavaScript Engine.
Migrate Fuzzilli to ChakraCore Engine
ChakraCore is an open source Javascript engine with a C API. Previously Microsoft Edge use this engine (However, they no longer use this until March 2021). So, in this post, I am going to migrate Fuzzilli to this engine, and try whether fuzzilli can easily migrate to other targets or not.
Clone the code
Make sure you have installed clang/clang++ and llvm-dev respectively. The reason of installing clang/clang++ is for using its SanitizerCoverage (document) to get edge coverage on every execution.
First, change the Makefile. Add set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt") to bin/ch/CMakeLists.txt. Search key work shared library below in vim can easily jump to this location.
For avoiding the issues happen on Linking, I add the code in coverage.c (under fuzzilli/Targets) to the library in bin/ch/ch.cpp. I also added some head file at the beginning.
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 //
Add REPRL
After this, I follow the instruction on here to add code of REPRL (Read-Eval-Print-Reset-Loop) in the ch.cpp. REPRL is for reducing the overhead occurred when starting a new instance of JSE, and this loop needs to be triggered by a unique command line option (make sure to include as processArgument in the profile).
The code implementation I referred to some patches under Targets folder. This also can be search by grep -rn "HELO" * or grep -rn "char helo" * (under fuzzilli/Targets) and find some code implementation examples, but ensure that you have clone all JSE source code under that folder.
After doing this, my implementation is as below (file located ChakraCore/bin/ch/ch.cpp) :
/* Run the fuzzilli run-eval-print-repeat loop, and exit at the end. */ if (options.m_reprl) { /* REPRL: let parent know we are ready */ char helo[] = "HELO"; CHECK(write(REPRL_CWFD, helo, 4) == 4);//write "HELO" on REPRL_CWFD CHECK(read(REPRL_CRFD, helo, 4) == 4); //read 4 bytes on REPRL_CRFD
if (memcmp(helo, "HELO", 4) != 0) { fprintf(stderr, "[REPRL] Invalid response from parent\n"); exit(EXIT_FAILURE); //break if 4 read bytes do not equal "HELO" }
do {//while true // Keep indention for easier diffing unsigned action; size_t script_size; char *buffer; CHECK(read(REPRL_CRFD, &action, 4) == 4); //read 4 bytes on REPRL_CRFD if (action != 'cexe') { fprintf(stderr, "[REPRL] Unknown action: %u\n", action); exit(EXIT_FAILURE);//break if 4 read bytes do not equal "cexe" } else{ //read 8 bytes on REPRL_CRFD, store as unsigned 64 bit integer size CHECK(read(REPRL_CRFD, &script_size, 8) == 8); }
char *ptr = buffer; //ptr is buffer size_t remaining = script_size; while (remaining > 0) { ssize_t rv = read(REPRL_DRFD, ptr, remaining);//read size bytes from REPRL_DRFD into allocated buffer if (rv <= 0) { fprintf(stderr, "Failed to load script\n"); _exit(-1); } remaining -= rv; ptr += rv; }
buffer[script_size] = '\0'; if (0 == (result = eval_buf(ctx, buffer, script_size, "reprl", 0))) { js_std_loop(ctx); //Execute buffer as javascript code } else { fprintf(stderr, "Failed to eval_buf reprl\n"); }
js_free(ctx, buffer); //Store return value from JS execution
// In REPRL mode, stdout and stderr may be regular files, so we need to fflush them here. fflush(stdout); //Flush stdout fflush(stderr); //Flush stderr
int status = (result & 0xff) << 8; //Mask return value with 0xff and shift it left by 8 CHECK(write(REPRL_CWFD, &status, 4) == 4); //write that value over REPRL_CWFD //Reset the Javascript engine __sanitizer_cov_reset_edgeguards();//Call __sanitizer_cov_reset_edgeguards to reset coverage } while (true); }
Add Builtin Function
Next step is to add a builtin function that crashes directly to help Fuzzilli test if it can catch the crash of the targeted engine. The targeted file is lib/Runtime/Library/GlobalObject.cpp. This part I will not put the code here. I made this as a patch and named ch.patch, using the following command to patch the directory.
1 2
$cd Targets/ChakraCore $ patch -p1 < ch.patch
Create Fuzzilli Profile
Create ChakraCoreProfile.swift, this is a file guide Fuzzilli how to fuzz the targeted engine. Put this profile file in FuzzilliCli/Profiles.
// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import Fuzzilli letChakraCoreProfile=Profile( processArguments: ["-ForceJITCFGCheck", "-MaxJitThreadCount:1", "-collectgarbage"], // processEnv: [:], processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], codePrefix: """ function main() { """, codeSuffix: """ CollectGarbage(); } main(); """, ecmaVersion: ECMAScriptVersion.es6, crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)"], additionalCodeGenerators: WeightedList<CodeGenerator>([]), additionalProgramTemplates: WeightedList<ProgramTemplate>([]), disabledCodeGenerators: [], additionalBuiltins: ["CollectGarbage" : .function([] => .undefined)] )
Add this Profile into Profile.swift (locate in Fuzzilli/Profiles/Profile.swift). My implementation is as below.
Make sure to rebuild fuzzilli after doing this:
1 2
$cd fuzzilli $ swift build -c release -Xlinker='-lrt'
Build successfully!
Run the Fuzzilli
Build it at first. The building instruction is at here. The executable file will be generate in out/Debug/bin/ch/ch. Install the dependency of building ChakraCore at first.
$cd fuzzilli/Targets/ChakraCore $export CFLAGS="-fsanitize-coverage=trace-pc-guard -O3 -fsanitize=address" $export CXXFLAGS="-fsanitize-coverage=trace-pc-guard -O3 -fsanitize=address" $ ./build.sh --static -d -j --cc=/path/to/clang --cxx=/path/to/clang++ # Can specify --debug or --test-build to build.sh to select Debug or Test build flavors respectively. Default is Release build. # Can specify --static to build ChakraCore as a static library. # Also can run as ./build.sh --static -d --cxx=/usr/bin/clang++-10 --cc=/usr/bin/clang-10 -j
If build with ninja:
1 2 3
$ sudo apt-get install -y ninja-build $ ninja --version #Ensure ninjs has been installed correctly $ ./build.sh -n #Specify the -n flag to build.sh to build with ninja
Let's try fuzzing!
1 2 3
$cd fuzzilli/Targets/ChakraCore $ mkdir fuzz_ch_out $ swift run FuzzilliCli --jobs=2 --profile=chakracore --storagePath=fuzz_ch_out/ out/Debug/ch #or this "out/Debug/bin/ch/ch"
However, I met some problems here...
I try to rebuild fuzzilli and ChakraCore, it met error during compilation again. If want to record the error, use the following command:
1
$ script -f output.txt
After collecting all compilation information, using Ctrl + d to stop. Then, I fixed all the bugs and build successfully.