Support UBSan for local fuzzing (#187)
With Jazzer supporting full UBSan as of
https://github.com/CodeIntelligenceTesting/jazzer/pull/169
as well as a much simpler way to link the UBSan C++ runtime via the flag
used in #186, UBSan can now be supported in local mode without
introducing additional complexity.
The list of enabled UBSan checks is taken from OSS-Fuzz.
The commit also adds tests to verify that both C++ and Java fuzz tests
support the UBSan C++ checks without linker errors.
diff --git a/.bazelrc b/.bazelrc
index 0083a81..10430bd 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -38,6 +38,11 @@
build:msan-libfuzzer-repro --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
build:msan-libfuzzer-repro --@rules_fuzzing//fuzzing:cc_engine_sanitizer=msan-origin-tracking
+# LibFuzzer + UBSAN
+build:ubsan-libfuzzer --//fuzzing:cc_engine=//fuzzing/engines:libfuzzer
+build:ubsan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
+build:ubsan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=ubsan
+
# Honggfuzz + ASAN
build:asan-honggfuzz --//fuzzing:cc_engine=//fuzzing/engines:honggfuzz
build:asan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=honggfuzz
@@ -48,6 +53,11 @@
build:msan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=honggfuzz
build:msan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=msan
+# Honggfuzz + UBSAN
+build:ubsan-honggfuzz --//fuzzing:cc_engine=//fuzzing/engines:honggfuzz
+build:ubsan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=honggfuzz
+build:ubsan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=ubsan
+
# Replay + ASAN
build:asan-replay --//fuzzing:cc_engine=//fuzzing/engines:replay
build:asan-replay --@rules_fuzzing//fuzzing:cc_engine_instrumentation=none
@@ -70,3 +80,10 @@
build:asan-jazzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan
# Workaround for https://github.com/bazelbuild/bazel/issues/11128
build:asan-jazzer --//fuzzing:cc_engine_sanitizer=asan
+
+# Jazzer + UBSAN
+build:ubsan-jazzer --//fuzzing:java_engine=//fuzzing/engines:jazzer
+build:ubsan-jazzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=jazzer
+build:ubsan-jazzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=ubsan
+# Workaround for https://github.com/bazelbuild/bazel/issues/11128
+build:ubsan-jazzer --//fuzzing:cc_engine_sanitizer=ubsan
diff --git a/README.md b/README.md
index 3e5b540..1c9e343 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@
* Multiple sanitizer configurations:
* [Address Sanitizer][asan-doc]
* [Memory Sanitizer][msan-doc]
+ * [Undefined Behavior Sanitizer][ubsan-doc]
* Corpora and dictionaries.
* Simple "bazel run/test" commands to build and run the fuzz tests.
* No need to understand the details of each fuzzing engine.
@@ -258,3 +259,4 @@
[libfuzzer-doc]: https://llvm.org/docs/LibFuzzer.html
[jazzer-doc]: https://github.com/CodeIntelligenceTesting/jazzer
[msan-doc]: https://clang.llvm.org/docs/MemorySanitizer.html
+[ubsan-doc]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
diff --git a/docs/guide.md b/docs/guide.md
index f352827..42868d8 100644
--- a/docs/guide.md
+++ b/docs/guide.md
@@ -144,6 +144,7 @@
* `asan`: [Address Sanitizer (ASAN)][asan-doc].
* `msan`: [Memory Sanitizer (MSAN)][msan-doc].
* `msan-origin-tracking`: MSAN with [origin tracking][msan-origin-tracking] enabled (useful for debugging crash reproducers; available separately due to it being 1.5-2x slower).
+ * `ubsan`: [Undefined Behavior Sanitizer (UBSAN)][ubsan-doc].
* `--@rules_fuzzing//fuzzing:cc_fuzzing_build_mode` is a bool flag that specifies whether the special [`FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` macro][fuzzing-build-mode] is defined during the build. This is turned on by default and most users should not need to change this flag.
@@ -180,6 +181,11 @@
build:msan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
build:msan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=msan
+# --config=ubsan-libfuzzer
+build:ubsan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer
+build:ubsan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
+build:ubsan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=ubsan
+
# --config=asan-honggfuzz
build:asan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:honggfuzz
build:asan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=honggfuzz
@@ -190,6 +196,11 @@
build:msan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=honggfuzz
build:msan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=msan
+# --config=ubsan-honggfuzz
+build:ubsan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:honggfuzz
+build:ubsan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=honggfuzz
+build:ubsan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=ubsan
+
# --config=asan-replay
build:asan-replay --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:replay
build:asan-replay --@rules_fuzzing//fuzzing:cc_engine_instrumentation=none
@@ -204,6 +215,11 @@
build:asan-jazzer --@rules_fuzzing//fuzzing:java_engine=@rules_fuzzing//fuzzing/engines:jazzer
build:asan-jazzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=jazzer
build:asan-jazzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan
+
+# --config=ubsan-jazzer
+build:ubsan-jazzer --@rules_fuzzing//fuzzing:java_engine=@rules_fuzzing//fuzzing/engines:jazzer
+build:ubsan-jazzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=jazzer
+build:ubsan-jazzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=ubsan
```
## Advanced topics
@@ -241,3 +257,4 @@
[msan-doc]: https://clang.llvm.org/docs/MemorySanitizer.html
[msan-origin-tracking]: https://clang.llvm.org/docs/MemorySanitizer.html#origin-tracking
[seed-corpus]: https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md#seed-corpus
+[ubsan-doc]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
diff --git a/examples/BUILD b/examples/BUILD
index b8afd15..65219ca 100644
--- a/examples/BUILD
+++ b/examples/BUILD
@@ -126,3 +126,19 @@
"@bazel_tools//tools/cpp/runfiles",
],
)
+
+cc_fuzz_test(
+ name = "ubsan_int_overflow_fuzz_test",
+ srcs = ["ubsan_int_overflow_fuzz_test.cc"],
+ tags = [
+ "no-oss-fuzz",
+ ],
+)
+
+cc_fuzz_test(
+ name = "ubsan_function_ptr_fuzz_test",
+ srcs = ["ubsan_function_ptr_fuzz_test.cc"],
+ tags = [
+ "no-oss-fuzz",
+ ],
+)
diff --git a/examples/java/BUILD b/examples/java/BUILD
index 4dc1352..5c23abe 100644
--- a/examples/java/BUILD
+++ b/examples/java/BUILD
@@ -69,6 +69,14 @@
],
)
+java_fuzz_test(
+ name = "NativeUbsanFuncPtrFuzzTest",
+ srcs = ["com/example/NativeUbsanFuncPtrFuzzTest.java"],
+ deps = [
+ ":native_ubsan_func_ptr",
+ ],
+)
+
# A native library that interfaces with Java through the JNI.
cc_binary(
name = "native",
@@ -99,3 +107,15 @@
"@bazel_tools//tools/jdk:jni",
],
)
+
+cc_binary(
+ name = "native_ubsan_func_ptr",
+ srcs = [
+ "com/example/NativeUbsanFuncPtrFuzzTest.cpp",
+ "com/example/NativeUbsanFuncPtrFuzzTest.h",
+ ],
+ linkshared = True,
+ deps = [
+ "@bazel_tools//tools/jdk:jni",
+ ],
+)
diff --git a/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.cpp b/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.cpp
new file mode 100644
index 0000000..2735f7b
--- /dev/null
+++ b/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.cpp
@@ -0,0 +1,37 @@
+// Copyright 2021 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.
+
+// parse calls a function through a pointer of a mismatched type. This is
+// detected by UBSAN's function check.
+
+#include "NativeUbsanFuncPtrFuzzTest.h"
+
+#include <cstddef>
+#include <cstdint>
+
+int parse_data(const uint16_t *data) {
+ return data[0] + data[1];
+}
+
+int (*mistyped_function_pointer)(const char *data);
+
+JNIEXPORT int JNICALL Java_com_example_NativeUbsanFuncPtrFuzzTest_parse(
+ JNIEnv *env, jobject o, jstring bytes) {
+ const char *input(env->GetStringUTFChars(bytes, nullptr));
+ mistyped_function_pointer =
+ reinterpret_cast<int (*)(const char *)>(&parse_data);
+ int result = mistyped_function_pointer(input);
+ env->ReleaseStringUTFChars(bytes, input);
+ return result;
+}
diff --git a/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.h b/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.h
new file mode 100644
index 0000000..7950ee0
--- /dev/null
+++ b/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.h
@@ -0,0 +1,34 @@
+// Copyright 2021 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.
+
+#include <jni.h>
+/* Header for class com_example_NativeUbsanFuncPtrFuzzTest */
+
+#ifndef EXAMPLES_JAVA_COM_EXAMPLE_NATIVEUBSANFUNCPTRFUZZTEST_H_
+#define EXAMPLES_JAVA_COM_EXAMPLE_NATIVEUBSANFUNCPTRFUZZTEST_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_example_NativeUbsanFuncPtrFuzzTest
+ * Method: parse
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT int JNICALL
+Java_com_example_NativeUbsanFuncPtrFuzzTest_parse(JNIEnv *, jobject, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // EXAMPLES_JAVA_COM_EXAMPLE_NATIVEUBSANFUNCPTRFUZZTEST_H_
diff --git a/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.java b/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.java
new file mode 100644
index 0000000..1f53464
--- /dev/null
+++ b/examples/java/com/example/NativeUbsanFuncPtrFuzzTest.java
@@ -0,0 +1,35 @@
+// Copyright 2021 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
+//
+// http://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.
+
+package com.example;
+
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+
+// The native function parse calls a function through a pointer of a mismatched
+// type. This is detected by UBSAN's function check.
+public class NativeUbsanFuncPtrFuzzTest {
+
+ static {
+ System.loadLibrary("native_ubsan_func_ptr");
+ }
+
+ public static void fuzzerTestOneInput(FuzzedDataProvider data) {
+ String stringData = data.consumeRemainingAsString();
+ if (stringData.length() > 10) {
+ parse(stringData);
+ }
+ }
+
+ private static native void parse(String data);
+}
diff --git a/examples/ubsan_function_ptr_fuzz_test.cc b/examples/ubsan_function_ptr_fuzz_test.cc
new file mode 100644
index 0000000..9adfc88
--- /dev/null
+++ b/examples/ubsan_function_ptr_fuzz_test.cc
@@ -0,0 +1,36 @@
+// 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.
+
+// A fuzz target that calls a function through a pointer of a mismatched type.
+// This is detected by UBSAN's function check and requires the UBSAN C++
+// runtime.
+
+#include <cstddef>
+#include <cstdint>
+
+int parse_data(const uint16_t *data) {
+ return data[0] + data[1];
+}
+
+int (*mistyped_function_pointer)(const uint8_t *data);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 2) {
+ return 0;
+ }
+ mistyped_function_pointer =
+ reinterpret_cast<int (*)(const uint8_t *)>(parse_data);
+ mistyped_function_pointer(data);
+ return 0;
+}
diff --git a/examples/ubsan_int_overflow_fuzz_test.cc b/examples/ubsan_int_overflow_fuzz_test.cc
new file mode 100644
index 0000000..aa6ca66
--- /dev/null
+++ b/examples/ubsan_int_overflow_fuzz_test.cc
@@ -0,0 +1,29 @@
+// 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.
+
+// A fuzz target that triggers a signed integer overflow, which is undefined
+// behavior and detected by UBSAN's signed-integer-overflow check.
+
+#include <cstddef>
+#include <cstdint>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size == 0) {
+ return 0;
+ }
+ int k = 0x7fffffff;
+ k += data[0];
+ // Use k.
+ return k & 0;
+}
diff --git a/fuzzing/BUILD b/fuzzing/BUILD
index 224e47a..d7087ec 100644
--- a/fuzzing/BUILD
+++ b/fuzzing/BUILD
@@ -54,6 +54,9 @@
# MSAN + origin tracking enabled.
# Useful for debugging crash reproducers, 1.5-2x slower.
"msan-origin-tracking",
+ # Undefined Behavior sanitizer (UBSAN).
+ # See https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
+ "ubsan",
],
visibility = ["//visibility:public"],
)
diff --git a/fuzzing/instrum_opts.bzl b/fuzzing/instrum_opts.bzl
index f6be905..b497155 100644
--- a/fuzzing/instrum_opts.bzl
+++ b/fuzzing/instrum_opts.bzl
@@ -44,4 +44,5 @@
"asan": instrum_defaults.asan,
"msan": instrum_defaults.msan,
"msan-origin-tracking": instrum_defaults.msan_origin_tracking,
+ "ubsan": instrum_defaults.ubsan,
}
diff --git a/fuzzing/private/BUILD b/fuzzing/private/BUILD
index 53f4886..3eb5186 100644
--- a/fuzzing/private/BUILD
+++ b/fuzzing/private/BUILD
@@ -45,6 +45,13 @@
)
config_setting(
+ name = "use_sanitizer_ubsan",
+ flag_values = {
+ "@rules_fuzzing//fuzzing:cc_engine_sanitizer": "ubsan",
+ },
+)
+
+config_setting(
name = "use_oss_fuzz",
flag_values = {
"@rules_fuzzing//fuzzing:cc_engine": "@rules_fuzzing_oss_fuzz//:oss_fuzz_engine",
diff --git a/fuzzing/private/fuzz_test.bzl b/fuzzing/private/fuzz_test.bzl
index a3376ea..96f9c74 100644
--- a/fuzzing/private/fuzz_test.bzl
+++ b/fuzzing/private/fuzz_test.bzl
@@ -291,12 +291,14 @@
"@rules_fuzzing//fuzzing/private:use_oss_fuzz": "@rules_fuzzing_oss_fuzz//:jazzer_driver",
"@rules_fuzzing//fuzzing/private:use_sanitizer_none": "@jazzer//driver:jazzer_driver",
"@rules_fuzzing//fuzzing/private:use_sanitizer_asan": "@jazzer//driver:jazzer_driver_asan",
- }, no_match_error = "Jazzer only supports the sanitizer settings \"none\" and \"asan\""),
+ "@rules_fuzzing//fuzzing/private:use_sanitizer_ubsan": "@jazzer//driver:jazzer_driver_ubsan",
+ }, no_match_error = "Jazzer only supports the sanitizer settings: \"none\", \"asan\", \"ubsan\""),
driver_with_native = select({
"@rules_fuzzing//fuzzing/private:use_oss_fuzz": "@rules_fuzzing_oss_fuzz//:jazzer_driver_with_sanitizer",
"@rules_fuzzing//fuzzing/private:use_sanitizer_none": "@jazzer//driver:jazzer_driver",
"@rules_fuzzing//fuzzing/private:use_sanitizer_asan": "@jazzer//driver:jazzer_driver_asan",
- }, no_match_error = "Jazzer only supports the sanitizer settings \"none\" and \"asan\""),
+ "@rules_fuzzing//fuzzing/private:use_sanitizer_ubsan": "@jazzer//driver:jazzer_driver_ubsan",
+ }, no_match_error = "Jazzer only supports the sanitizer settings: \"none\", \"asan\", \"ubsan\""),
sanitizer_options = select({
"@rules_fuzzing//fuzzing/private:use_oss_fuzz": "@rules_fuzzing//fuzzing/private:oss_fuzz_jazzer_sanitizer_options.sh",
"//conditions:default": "@rules_fuzzing//fuzzing/private:local_jazzer_sanitizer_options.sh",
diff --git a/fuzzing/private/instrum_opts.bzl b/fuzzing/private/instrum_opts.bzl
index 1b04184..a2f21a9 100644
--- a/fuzzing/private/instrum_opts.bzl
+++ b/fuzzing/private/instrum_opts.bzl
@@ -120,4 +120,25 @@
],
linkopts = ["-fsanitize=memory"],
),
+ ubsan = _make_opts(
+ copts = [
+ "-fsanitize=undefined",
+ # Enable most of the checks enabled in OSS-Fuzz:
+ # https://github.com/google/oss-fuzz/blob/a896ee749769bd236299041461784f483649fe80/infra/base-images/base-builder/Dockerfile#L77
+ # The only exception is unsigned-integer-overflow, which is not UB,
+ # but enabled in OSS-Fuzz in silent mode as an additional coverage
+ # signal. We do not do this here as it would introduce additional
+ # complexity (setting UBSAN_OPTIONS) to the local mode.
+ "-fsanitize=array-bounds,bool,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr",
+ "-fno-sanitize-recover=all",
+ ],
+ linkopts = [
+ "-fsanitize=undefined",
+ # Bazel uses clang, not clang++, as the linker, which does not link
+ # the C++ UBSan runtime library by default, but can be instructed to
+ # do so with a flag.
+ # https://github.com/bazelbuild/bazel/issues/11122#issuecomment-896613570
+ "-fsanitize-link-c++-runtime",
+ ],
+ ),
)