Quick and Dirty Guide to Fuzzing V8 with libFuzzer

What is V8?

V8 (also known as Chrome V8) is an open source JavaScript engine developed by The Chromium Project for the Google Chrome web browser. Other projects that use V8 include Couchbase, MongoDB and Node.js.

With my help, your V8 fuzzers will be up and running before you know it. Google does things a bit different, so it won't be as easy as ./configure && make but it's not really difficult once you figure out the right process. Thanks again to Google for giving me a nudge in the right direction.

Clone the depot_tools repository:

$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

Add depot_tools to the end of your PATH (you will probably want to put this in your ~/.bashrc or ~/.zshrc). Assuming you cloned depot_tools to /path/to/depot_tools:

$ export PATH="$PATH:/path/to/depot_tools"

Now we get the code we're going to fuzz. Create a chromium directory for the checkout and change to it:

$ mkdir ~/chromium && cd ~/chromium

Run the fetch tool from depot_tools to check out the code and its dependencies.

$ fetch --nohooks --no-history chromium

The --no-history option will speed things up as it won't pull down the entire git history, which we don't really need for fuzzing.

The remaining instructions assume you have switch to the src directory:

$ cd src

For this example, I am using Ubuntu 16.04 x64. If you're using Debian, these instructions should also work for you. Otherwise you may need to adjust the build dependencies.

Install additional build dependencies.

$ build/install-build-deps.sh

Run the hooks.

$ gclient runhooks

Now you've got the code base and the tools necessary to start building the fuzzers. For this example, I am using libFuzzer and not AFL. Blasphemy, I know. (;

Build the fuzzers:

First, we're going to run this command which will setup our targets:

$ gn gen out/libfuzzer '--args=use_libfuzzer=true is_asan=true is_ubsan_security=true is_debug=false enable_nacl=false' --check

This will tell the build system to use libFuzzer, ASan and UBSan. When it runs successfully, you'll be greeted by a message similar to Done. Made 6168 targets from 1323 files in 9620ms. Next run gn ls out/libfuzzer | grep fuzzer and you'll be greeted with a list which contains ~519 entries, most of which we won't be concerned with. For this example, I am going to pick a json parser, so let's run gn ls out/libfuzzer | grep json and see what pops up:

//components/json_schema:json_schema
//components/json_schema:unit_tests
//components/json_schema:unit_tests_bundle_data
//components/ntp_tiles:json_unsafe_parser
//components/safe_json:safe_json
//components/safe_json:test_support
//components/safe_json:unit_tests
//components/safe_json/public/interfaces:interfaces
//components/safe_json/public/interfaces:interfaces__check_deps_are_all_mojom
//components/safe_json/public/interfaces:interfaces__generator
//components/safe_json/public/interfaces:interfaces__is_mojom
//components/safe_json/public/interfaces:interfaces__type_mappings
//components/safe_json/public/interfaces:interfaces_blink
//components/safe_json/public/interfaces:interfaces_blink__generator
//components/safe_json/public/interfaces:interfaces_blink__type_mappings
//components/safe_json/public/interfaces:interfaces_js
//components/safe_json/public/interfaces:interfaces_js__generator
//components/safe_json/public/interfaces:interfaces_js_data_deps
//components/safe_json/public/interfaces:interfaces_shared
//components/safe_json/public/interfaces:interfaces_shared__generator
//components/safe_json/public/interfaces:interfaces_shared_cpp_sources
//components/safe_json/utility:utility
//content/browser/devtools:compressed_protocol_json
//extensions/shell/common/api:shell_api_features_json_features
//extensions/shell/common/api:shell_behavior_features_json_features
//extensions/shell/common/api:shell_manifest_features_json_features
//extensions/shell/common/api:shell_permission_features_json_features
//extensions/test:test_api_features_json_features
//extensions/test:test_behavior_features_json_features
//extensions/test:test_manifest_features_json_features
//extensions/test:test_permission_features_json_features
//gpu/config:process_json
//testing/libfuzzer/fuzzers:base_json_reader_fuzzer
//testing/libfuzzer/fuzzers:v8_json_parser_fuzzer
//testing/libfuzzer/fuzzers:v8_json_parser_fuzzer.options
//testing/libfuzzer/fuzzers:v8_json_parser_fuzzer_dict_copy
//third_party/WebKit/Source/platform:blink_json_parser_fuzzer
//third_party/WebKit/Source/platform:blink_json_parser_fuzzer.options
//third_party/WebKit/Source/platform:blink_json_parser_fuzzer_dict_copy
//third_party/angle/src/vulkan_support:vulkan_gen_json_files
//third_party/dom_distiller_js:json_values_converter_test_proto
//third_party/dom_distiller_js:json_values_converter_test_proto_gen
//third_party/dom_distiller_js:json_values_converter_tests
//third_party/jsoncpp:jsoncpp
//third_party/webrtc/base:rtc_json
//third_party/webrtc/rtc_base:rtc_json
//tools/json_schema_compiler:generated_api_util
//tools/json_schema_compiler/test:api
//tools/json_schema_compiler/test:api_schema_generator
//tools/json_schema_compiler/test:features_compiler_test
//tools/json_schema_compiler/test:features_compiler_test_json_features
//tools/json_schema_compiler/test:unit_tests
//v8:json_fuzzer
//v8:v8_simple_json_fuzzer

I think v8_json_parser_fuzzer looks like a good choice. So in order to build this particular fuzzer, we run this command:

$ ninja -C out/libfuzzer v8_json_parser_fuzzer

You'll see text like [55/1074] CXX obj/third_party/libFuzzer/libfuzzer/FuzzerExtraCounters.o while it is building. It can take a while, so be patient and have a cup of coffee. Once it has completed, cd out/libfuzzer and you'll find the v8_json_parser_fuzzer fuzzer along with a v8_json_parser_fuzzer.dict dictionary file and a v8_json_parser_fuzzer.options libFuzzer options file that you can use to define various things but we aren't going to get into that right now. Feel free to explore that on your own.

Create a directory for your starting corpus, naming it whatever you want:
$ mkdir json_parser_corpus

Fill json_parser_corpus with your json files. I don't use the included V8 fuzzing corpus because everyone and their dog fuzzes that stuff. Here is a sample for you to use if you don't want to find your own. (;

{
    "colorsArray":[{
        "colorName":"red",
        "hexValue":"#f00"
    },
    {
        "colorName":"green",
        "hexValue":"#0f0"
    },
    {
        "colorName":"blue",
        "hexValue":"#00f"
    },
    {
        "colorName":"cyan",
        "hexValue":"#0ff"
    },
    {
        "colorName":"magenta",
        "hexValue":"#f0f"
    },
    {
        "colorName":"yellow",
        "hexValue":"#ff0"
    },
    {
        "colorName":"black",
        "hexValue":"#000"
    }
    ]
}

If you have a single core, your command line would be:
$ ./v8_json_parser_fuzzer json_parser_corpus/ -dict=v8_json_parser_fuzzer.dict

If you have multiple cores:
$ ./v8_json_parser_fuzzer json_parser_corpus/ -dict=v8_json_parser_fuzzer.dict -jobs=X -workers=X

Note: -jobs=X where X is the number of times you want the fuzzer to restart after it finds something that triggers that particular job to abort or crash and -workers=X where X is the number of CPU cores you want to libFuzzer to take advantage of.

Congratulations, you're now fuzzing V8. I hope you find a bug or two. Don't forget us little people when you're famous. (=

Sources

Thanks

  • Abhishek Arya at Google.
  • My Twitter followers.
  • My Patreon supporters.

Shameless Plug

As always, you can support me on Patreon, via PayPal, Bitcoin 1HnyFwSJDjWFexD7oRr4HGTFwD8N6NsrfX or Ethereum 0xee9cBCB8DDC4f25832AD7FC02FADB981b1212D04.

Geeknik Labs

Also on this blog

SHARE:  Email · Facebook · Google · Twitter · Tumblr · Kindle
SUBSCRIBE:  Receive an email on new posts from Geeknik Labs

Comments


  • Notify me upon new comments

☺ Got it