From 224e8d00c26d19a78de1720459e3c5b403ae36ca Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 9 Jun 2021 10:03:53 +0200 Subject: Add support for UBSan --- BUILD.bazel | 11 ++++++++++ README.md | 17 ++++++++------- bazel/fuzz_target.bzl | 11 ++++++++-- driver/BUILD.bazel | 14 +++++++++++++ driver/libfuzzer_fuzz_target.cpp | 5 +++++ examples/BUILD.bazel | 20 ++++++++++++++---- .../java/com/example/ExampleFuzzerWithNative.java | 6 +++++- examples/src/main/native/BUILD.bazel | 24 +++++++++++++++++++++- .../native/com_example_ExampleFuzzerWithNative.cpp | 8 +++++++- 9 files changed, 100 insertions(+), 16 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 965c99b1..8c3cbcd7 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -57,6 +57,17 @@ sh_binary( ], ) +sh_binary( + name = "jazzer_ubsan", + srcs = ["//bazel:jazzer_wrapper.sh"], + args = [ + "$(rootpath //driver:jazzer_driver_ubsan)", + ], + data = [ + "//driver:jazzer_driver_ubsan", + ], +) + exports_files([ "jazzer-api.pom", ]) diff --git a/README.md b/README.md index 4e0b42b0..9fb5bff7 100644 --- a/README.md +++ b/README.md @@ -401,15 +401,18 @@ via `--ignore=,`. Jazzer supports fuzzing of native libraries loaded by the JVM, for example via `System.load()`. For the fuzzer to get coverage feedback, these libraries have to be compiled with `-fsanitize=fuzzer-no-link`. -Additional sanitizers such as AddressSanitizer are often desirable to uncover bugs inside the native libraries. This -requires compiling the library with `-fsanitize=fuzzer-no-link,address` and using the asan-ified driver available -as the Bazel target `//:jazzer_asan`. +Additional sanitizers such as AddressSanitizer or UndefinedBehaviorSanitizer are often desirable to uncover bugs inside +the native libraries. The required compilation flags for native libraries are as follows: + - *AddressSanitizer*: `-fsanitize=fuzzer-no-link,address` + - *UndefinedBehaviorSanitizer*: `-fsanitize=fuzzer-no-link,undefined` (add `-fno-sanitize-recover=all` to crash on UBSan reports) -**Note:** Sanitizers other than AddressSanitizer are not yet supported. Furthermore, due to the nature of the JVM's GC, -LeakSanitizer reports currently too many false positives to be useful and are thus disabled. +Then, use the appropriate driver `//:jazzer_asan` or `//:jazzer_ubsan`. -The fuzz target `ExampleFuzzerWithNative` in the `examples/` directory contains a minimal working example for fuzzing -with native libraries. Also see `TurboJpegFuzzer` for a real-world example. +**Note:** Sanitizers other than AddressSanitizer and UndefinedBehaviorSanitizer are not yet supported. +Furthermore, due to the nature of the JVM's GC, LeakSanitizer reports too many false positives to be useful and is thus disabled. + +The fuzz targets `ExampleFuzzerWithNativeASan` and `ExampleFuzzerWithNativeUBSan` in the `examples/` directory contain +minimal working examples for fuzzing with native libraries. Also see `TurboJpegFuzzer` for a real-world example. ### Fuzzing with Custom Mutators diff --git a/bazel/fuzz_target.bzl b/bazel/fuzz_target.bzl index 051f9169..e920e1a7 100644 --- a/bazel/fuzz_target.bzl +++ b/bazel/fuzz_target.bzl @@ -20,7 +20,7 @@ def java_fuzz_target_test( deps = [], hook_classes = [], native_libs = [], - use_asan = False, + sanitizer = None, visibility = None, tags = [], fuzzer_args = [], @@ -52,7 +52,14 @@ def java_fuzz_target_test( if native_libs_paths != "": additional_args.append("--jvm_args=-Djava.library.path=" + native_libs_paths) - driver = "//driver:jazzer_driver_asan" if use_asan else "//driver:jazzer_driver" + if sanitizer == None: + driver = "//driver:jazzer_driver" + elif sanitizer == "address": + driver = "//driver:jazzer_driver_asan" + elif sanitizer == "undefined": + driver = "//driver:jazzer_driver_ubsan" + else: + fail("Invalid sanitizer: " + sanitizer) native.sh_test( name = name, diff --git a/driver/BUILD.bazel b/driver/BUILD.bazel index 4fd89e5f..28b5a85e 100644 --- a/driver/BUILD.bazel +++ b/driver/BUILD.bazel @@ -85,6 +85,20 @@ cc_binary( ], linkopts = [ "-fsanitize=address", + "-rdynamic", + ], + visibility = ["//visibility:public"], + deps = [":driver_lib"], +) + +cc_binary( + name = "jazzer_driver_ubsan", + data = [ + "//agent:jazzer_agent_deploy.jar", + ], + linkopts = [ + "-fsanitize=undefined", + "-rdynamic", ], visibility = ["//visibility:public"], deps = [":driver_lib"], diff --git a/driver/libfuzzer_fuzz_target.cpp b/driver/libfuzzer_fuzz_target.cpp index 8bbed9a1..c2003d02 100644 --- a/driver/libfuzzer_fuzz_target.cpp +++ b/driver/libfuzzer_fuzz_target.cpp @@ -28,6 +28,11 @@ const char *__asan_default_options() { // We use a distinguished exit code to recognize ASan crashes in tests. return "detect_leaks=0,exitcode=76"; } + +const char *__ubsan_default_options() { + // We use a distinguished exit code to recognize UBSan crashes in tests. + return "exitcode=76"; +} } namespace { diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel index b2d021d0..d7f0f8cd 100644 --- a/examples/BUILD.bazel +++ b/examples/BUILD.bazel @@ -14,15 +14,27 @@ java_fuzz_target_test( ) java_fuzz_target_test( - name = "ExampleFuzzerWithNative", + name = "ExampleFuzzerWithASan", srcs = [ "src/main/java/com/example/ExampleFuzzerWithNative.java", ], - native_libs = ["//examples/src/main/native"], + native_libs = ["//examples/src/main/native:native_asan"], + sanitizer = "address", + # Bazel creates shared libraries with an incorrect extension on macOS. + tags = ["broken-on-darwin"], + target_class = "com.example.ExampleFuzzerWithNative", +) + +java_fuzz_target_test( + name = "ExampleFuzzerWithUBSan", + srcs = [ + "src/main/java/com/example/ExampleFuzzerWithNative.java", + ], + native_libs = ["//examples/src/main/native:native_ubsan"], + sanitizer = "undefined", # Bazel creates shared libraries with an incorrect extension on macOS. tags = ["broken-on-darwin"], target_class = "com.example.ExampleFuzzerWithNative", - use_asan = True, ) java_fuzz_target_test( @@ -208,9 +220,9 @@ java_fuzz_target_test( native_libs = [ "@libjpeg_turbo//:turbojpeg_native", ], + sanitizer = "address", tags = ["manual"], target_class = "com.example.TurboJpegFuzzer", - use_asan = True, deps = [ "@libjpeg_turbo//:turbojpeg_java", ], diff --git a/examples/src/main/java/com/example/ExampleFuzzerWithNative.java b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java index 853501bf..071446aa 100644 --- a/examples/src/main/java/com/example/ExampleFuzzerWithNative.java +++ b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java @@ -18,7 +18,11 @@ import com.code_intelligence.jazzer.api.FuzzedDataProvider; public class ExampleFuzzerWithNative { static { - System.loadLibrary("native"); + try { + System.loadLibrary("native_asan"); + } catch (UnsatisfiedLinkError e) { + System.loadLibrary("native_ubsan"); + } } public static void fuzzerTestOneInput(FuzzedDataProvider data) { diff --git a/examples/src/main/native/BUILD.bazel b/examples/src/main/native/BUILD.bazel index 16b48419..df24bb37 100644 --- a/examples/src/main/native/BUILD.bazel +++ b/examples/src/main/native/BUILD.bazel @@ -1,7 +1,7 @@ load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( - name = "native", + name = "native_asan", srcs = [ "com_example_ExampleFuzzerWithNative.cpp", "com_example_ExampleFuzzerWithNative.h", @@ -18,3 +18,25 @@ cc_binary( "@bazel_tools//tools/jdk:jni", ], ) + +cc_binary( + name = "native_ubsan", + srcs = [ + "com_example_ExampleFuzzerWithNative.cpp", + "com_example_ExampleFuzzerWithNative.h", + ], + copts = [ + "-fsanitize=fuzzer-no-link,undefined", + "-fno-sanitize-recover=all", + # Workaround for https://github.com/bazelbuild/bazel/issues/11122. + "-fno-sanitize=vptr,function", + ], + linkopts = [ + "-fsanitize=fuzzer-no-link,undefined", + ], + linkshared = True, + visibility = ["//examples:__pkg__"], + deps = [ + "@bazel_tools//tools/jdk:jni", + ], +) diff --git a/examples/src/main/native/com_example_ExampleFuzzerWithNative.cpp b/examples/src/main/native/com_example_ExampleFuzzerWithNative.cpp index 434c3d5b..774e5998 100644 --- a/examples/src/main/native/com_example_ExampleFuzzerWithNative.cpp +++ b/examples/src/main/native/com_example_ExampleFuzzerWithNative.cpp @@ -14,14 +14,20 @@ #include "com_example_ExampleFuzzerWithNative.h" +#include #include // simple function containing a crash that requires coverage and string compare // instrumentation for the fuzzer to find __attribute__((optnone)) void parseInternal(const std::string &input) { + constexpr int bar = std::numeric_limits::max() - 5; + // Crashes with UBSan. + if (bar + input[0] == 300) { + return; + } if (input[0] == 'a' && input[1] == 'b' && input[5] == 'c') { if (input.find("secret_in_native_library") != std::string::npos) { - // BOOM + // Crashes with ASan. [[maybe_unused]] char foo = input[input.size() + 2]; } } -- cgit v1.2.3