diff --git a/.bazelrc b/.bazelrc index f64a009..b666ec6 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Force the use of Clang for all builds. +# Set clang as the default compiler for most configurations. build --repo_env=CC=clang # Strict dependency check for C++ includes. @@ -68,6 +68,12 @@ 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 +# Honggfuzz + ASAN (GCC) +build:asan-honggfuzz-gcc --//fuzzing:cc_engine=//fuzzing/engines:honggfuzz +build:asan-honggfuzz-gcc --@rules_fuzzing//fuzzing:cc_engine_instrumentation=honggfuzz +build:asan-honggfuzz-gcc --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan +build:asan-honggfuzz-gcc --repo_env=CC=gcc + # Replay + ASAN build:asan-replay --//fuzzing:cc_engine=//fuzzing/engines:replay build:asan-replay --@rules_fuzzing//fuzzing:cc_engine_instrumentation=none diff --git a/README.md b/README.md index 0aa2822..44f6fca 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,10 @@ This section will walk you through the steps to set up fuzzing in your Bazel pro The fuzzing rules have been tested on Bazel 4.0.0 or later. Check your Bazel version by running `bazel --version`. -C++ fuzzing requires a Clang compiler. The libFuzzer engine requires at least Clang 6.0. In addition, the Honggfuzz engine requires the `libunwind-dev` and `libblocksruntime-dev` packages: +The libFuzzer engine requires at least Clang 6.0. Honggfuzz works with both clang and gcc (8 or later) and requires the `libunwind-dev` and `libblocksruntime-dev` packages: ```sh -$ sudo apt-get install clang libunwind-dev libblocksruntime-dev +$ sudo apt-get install libunwind-dev libblocksruntime-dev ``` Java fuzzing requires Clang and the LLD linker: diff --git a/fuzzing/instrum_opts.bzl b/fuzzing/instrum_opts.bzl index a6eadbe..19272e9 100644 --- a/fuzzing/instrum_opts.bzl +++ b/fuzzing/instrum_opts.bzl @@ -29,21 +29,35 @@ load( "oss_fuzz_opts", ) -# Fuzz test binary instrumentation configurations. +# Fuzz test binary instrumentation configurations by compiler type. instrum_configs = { - "none": instrum_opts.make(), - "libfuzzer": instrum_defaults.libfuzzer, - "jazzer": instrum_defaults.jazzer, - "honggfuzz": instrum_defaults.honggfuzz, - "oss-fuzz": oss_fuzz_opts, + "clang": { + "none": instrum_opts.make(), + "libfuzzer": instrum_defaults.libfuzzer, + "jazzer": instrum_defaults.jazzer, + "honggfuzz": instrum_defaults.honggfuzz_clang, + "oss-fuzz": oss_fuzz_opts, + }, + "gcc": { + "none": instrum_opts.make(), + "honggfuzz": instrum_defaults.honggfuzz_gcc, + }, } -# Sanitizer configurations. +# Sanitizer configurations by compiler type. sanitizer_configs = { - "none": instrum_opts.make(), - "asan": instrum_defaults.asan, - "msan": instrum_defaults.msan, - "msan-origin-tracking": instrum_defaults.msan_origin_tracking, - "ubsan": instrum_defaults.ubsan, - "asan-ubsan": instrum_opts.merge(instrum_defaults.asan, instrum_defaults.ubsan), + "clang": { + "none": instrum_opts.make(), + "asan": instrum_defaults.asan, + "msan": instrum_defaults.msan, + "msan-origin-tracking": instrum_defaults.msan_origin_tracking, + "ubsan": instrum_defaults.ubsan_clang, + "asan-ubsan": instrum_opts.merge(instrum_defaults.asan, instrum_defaults.ubsan_clang), + }, + "gcc": { + "none": instrum_opts.make(), + "asan": instrum_defaults.asan, + "ubsan": instrum_defaults.ubsan_gcc, + "asan-ubsan": instrum_opts.merge(instrum_defaults.asan, instrum_defaults.ubsan_gcc), + }, } diff --git a/fuzzing/private/binary.bzl b/fuzzing/private/binary.bzl index 8ff9723..622788d 100644 --- a/fuzzing/private/binary.bzl +++ b/fuzzing/private/binary.bzl @@ -47,29 +47,30 @@ Provider for storing information about a fuzz test binary. }, ) -def _fuzzing_binary_transition_impl(settings, _attr): +def _fuzzing_binary_transition_impl(settings, attr): opts = instrum_opts.make( copts = settings["//command_line_option:copt"], conlyopts = settings["//command_line_option:conlyopt"], cxxopts = settings["//command_line_option:cxxopt"], linkopts = settings["//command_line_option:linkopt"], ) + compiler_type = getattr(attr, "compiler", "clang") is_fuzzing_build_mode = settings["@rules_fuzzing//fuzzing:cc_fuzzing_build_mode"] if is_fuzzing_build_mode: opts = instrum_opts.merge(opts, instrum_defaults.fuzzing_build) instrum_config = settings["@rules_fuzzing//fuzzing:cc_engine_instrumentation"] - if instrum_config in instrum_configs: - opts = instrum_opts.merge(opts, instrum_configs[instrum_config]) + if instrum_config in instrum_configs[compiler_type]: + opts = instrum_opts.merge(opts, instrum_configs[compiler_type][instrum_config]) else: - fail("unsupported engine instrumentation '%s'" % instrum_config) + fail("unsupported engine instrumentation '%s' for compiler '%s'" % (instrum_config, compiler_type)) sanitizer_config = settings["@rules_fuzzing//fuzzing:cc_engine_sanitizer"] - if sanitizer_config in sanitizer_configs: - opts = instrum_opts.merge(opts, sanitizer_configs[sanitizer_config]) + if sanitizer_config in sanitizer_configs[compiler_type]: + opts = instrum_opts.merge(opts, sanitizer_configs[compiler_type][sanitizer_config]) else: - fail("unsupported sanitizer '%s'" % sanitizer_config) + fail("unsupported sanitizer '%s' for compiler '%s'" % (sanitizer_config, compiler_type)) return { "//command_line_option:copt": opts.copts, @@ -185,6 +186,11 @@ The instrumentation is controlled by the following flags: cfg = fuzzing_binary_transition, mandatory = True, ), + "compiler": attr.string( + default = "clang", + values = ["clang", "gcc"], + doc = "The compiler used to build the fuzz test executable.", + ), "_allowlist_function_transition": attr.label( default = "@bazel_tools//tools/allowlists/function_transition_allowlist", ), diff --git a/fuzzing/private/extensions.bzl b/fuzzing/private/extensions.bzl index 3700810..a6b0b68 100644 --- a/fuzzing/private/extensions.bzl +++ b/fuzzing/private/extensions.bzl @@ -18,7 +18,7 @@ load("@bazel_features//:features.bzl", "bazel_features") load("//fuzzing:repositories.bzl", "rules_fuzzing_dependencies") def _non_module_dependencies(mctx): - rules_fuzzing_dependencies() + rules_fuzzing_dependencies(bzlmod = True) if bazel_features.external_deps.extension_metadata_has_reproducible: return mctx.extension_metadata(reproducible = True) diff --git a/fuzzing/private/fuzz_test.bzl b/fuzzing/private/fuzz_test.bzl index a2e4cb5..60d2157 100644 --- a/fuzzing/private/fuzz_test.bzl +++ b/fuzzing/private/fuzz_test.bzl @@ -88,6 +88,10 @@ def fuzzing_decoration( fuzzing_binary( name = instrum_binary_name, binary = raw_binary, + compiler = select({ + str(Label("@rules_cc//cc/compiler:gcc")): "gcc", + "//conditions:default": "clang", + }), engine = engine, corpus = corpus_name, dictionary = dict_name if dicts else None, diff --git a/fuzzing/private/instrum_opts.bzl b/fuzzing/private/instrum_opts.bzl index 60cea93..1c723bb 100644 --- a/fuzzing/private/instrum_opts.bzl +++ b/fuzzing/private/instrum_opts.bzl @@ -91,7 +91,7 @@ instrum_defaults = struct( ), # Reflects the set of options at # https://github.com/google/honggfuzz/blob/master/hfuzz_cc/hfuzz-cc.c - honggfuzz = _make_opts( + honggfuzz_clang = _make_opts( copts = [ "-mllvm", "-inline-threshold=2000", @@ -105,6 +105,16 @@ instrum_defaults = struct( "-fno-sanitize=fuzzer", ], ), + honggfuzz_gcc = _make_opts( + copts = [ + "-finline-limit=1000", + "-fsanitize-coverage=trace-pc,trace-cmp", + "-fno-builtin", + "-fno-omit-frame-pointer", + "-D__NO_STRING_INLINES", + ], + linkopts = [], + ), asan = _make_opts( copts = ["-fsanitize=address"], linkopts = ["-fsanitize=address"], @@ -120,7 +130,7 @@ instrum_defaults = struct( ], linkopts = ["-fsanitize=memory"], ), - ubsan = _make_opts( + ubsan_clang = _make_opts( copts = [ "-fsanitize=undefined", ], @@ -133,4 +143,12 @@ instrum_defaults = struct( "-fsanitize-link-c++-runtime", ], ), + ubsan_gcc = _make_opts( + copts = [ + "-fsanitize=undefined", + ], + linkopts = [ + "-fsanitize=undefined", + ], + ), ) diff --git a/fuzzing/repositories.bzl b/fuzzing/repositories.bzl index 2c5a367..388ded9 100644 --- a/fuzzing/repositories.bzl +++ b/fuzzing/repositories.bzl @@ -18,47 +18,49 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_jar") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") load("//fuzzing/private/oss_fuzz:repository.bzl", "oss_fuzz_repository") -def rules_fuzzing_dependencies(oss_fuzz = True, honggfuzz = True, jazzer = True): +def rules_fuzzing_dependencies(oss_fuzz = True, honggfuzz = True, jazzer = True, bzlmod = False): """Instantiates the dependencies of the fuzzing rules. Args: oss_fuzz: Include OSS-Fuzz dependencies. honggfuzz: Include Honggfuzz dependencies. jazzer: Include Jazzer dependencies. + bzlmod: Whether to exclude dependencies that are already provided by bazel_deps. """ - maybe( - http_archive, - name = "platforms", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz", - "https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz", - ], - sha256 = "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74", - ) - maybe( - http_archive, - name = "rules_python", - sha256 = "d70cd72a7a4880f0000a6346253414825c19cdd40a28289bdf67b8e6480edff8", - strip_prefix = "rules_python-0.28.0", - url = "https://github.com/bazelbuild/rules_python/releases/download/0.28.0/rules_python-0.28.0.tar.gz", - ) - maybe( - http_archive, - name = "bazel_skylib", - sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", - ], - ) - maybe( - http_archive, - name = "com_google_absl", - urls = ["https://github.com/abseil/abseil-cpp/archive/refs/tags/20240116.1.zip"], - strip_prefix = "abseil-cpp-20240116.1", - integrity = "sha256-7capMWOvWyoYbUaHF/b+I2U6XLMaHmky8KugWvfXYuk=", - ) + if not bzlmod: + maybe( + http_archive, + name = "platforms", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz", + "https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz", + ], + sha256 = "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74", + ) + maybe( + http_archive, + name = "rules_python", + sha256 = "d70cd72a7a4880f0000a6346253414825c19cdd40a28289bdf67b8e6480edff8", + strip_prefix = "rules_python-0.28.0", + url = "https://github.com/bazelbuild/rules_python/releases/download/0.28.0/rules_python-0.28.0.tar.gz", + ) + maybe( + http_archive, + name = "bazel_skylib", + sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", + ], + ) + maybe( + http_archive, + name = "com_google_absl", + urls = ["https://github.com/abseil/abseil-cpp/archive/refs/tags/20240116.1.zip"], + strip_prefix = "abseil-cpp-20240116.1", + integrity = "sha256-7capMWOvWyoYbUaHF/b+I2U6XLMaHmky8KugWvfXYuk=", + ) if oss_fuzz: maybe( @@ -71,9 +73,9 @@ def rules_fuzzing_dependencies(oss_fuzz = True, honggfuzz = True, jazzer = True) http_archive, name = "honggfuzz", build_file = "@rules_fuzzing//:honggfuzz.BUILD", - sha256 = "6b18ba13bc1f36b7b950c72d80f19ea67fbadc0ac0bb297ec89ad91f2eaa423e", - url = "https://github.com/google/honggfuzz/archive/2.5.zip", - strip_prefix = "honggfuzz-2.5", + integrity = "sha256-d+DpDrzBMqmSNCuz0+/Vi3jTUiIFNnulWGAeBJaaHJM=", + url = "https://github.com/google/honggfuzz/archive/4cfa62f4fdb56e3027c1cb3aecf04812e786f0fd.zip", + strip_prefix = "honggfuzz-4cfa62f4fdb56e3027c1cb3aecf04812e786f0fd", ) if jazzer: diff --git a/honggfuzz.BUILD b/honggfuzz.BUILD index 583e3f5..1b70a08 100644 --- a/honggfuzz.BUILD +++ b/honggfuzz.BUILD @@ -37,20 +37,31 @@ COMMON_COPTS = [ "-Wall", "-Wextra", "-Werror", - "-Wno-override-init", - "-Wno-initializer-overrides", - "-Wno-gnu-empty-initializer", - "-Wno-format-pedantic", - "-Wno-gnu-statement-expression", - "-mllvm", - "-inline-threshold=2000", - "-fblocks", - - # Do not instrument Honggfuzz itself, in order to avoid recursive - # instrumentation calls that would crash the fuzz test binary. - "-fsanitize-coverage=0", - "-fno-sanitize=all", -] +] + select({ + "@rules_cc//cc/compiler:gcc": [ + "-Wno-override-init", + "-Wno-format-truncation", + # Do not instrument Honggfuzz itself, in order to avoid recursive + # instrumentation calls that would crash the fuzz test binary. + "-fno-sanitize-coverage=trace-pc,trace-cmp", + "-fno-sanitize=all", + ], + # Default to clang compiler flags + "//conditions:default": [ + "-mllvm", + "-inline-threshold=2000", + "-fblocks", + "-Wno-override-init", + "-Wno-initializer-overrides", + "-Wno-gnu-empty-initializer", + "-Wno-format-pedantic", + "-Wno-gnu-statement-expression", + # Do not instrument Honggfuzz itself, in order to avoid recursive + # instrumentation calls that would crash the fuzz test binary. + "-fsanitize-coverage=0", + "-fno-sanitize=all", + ], +}) LIBRARY_COPTS = [ "-fno-stack-protector", @@ -116,6 +127,10 @@ SYMBOL_WRAP_LINKOPTS = select({ "-Wl,--wrap=Curl_safe_strcasecompare", "-Wl,--wrap=Curl_strncasecompare", "-Wl,--wrap=curl_strnequal", + # SQLite3 + "-Wl,--wrap=sqlite3_stricmp", + "-Wl,--wrap=sqlite3_strnicmp", + "-Wl,--wrap=sqlite3StrICmp", ], }) @@ -147,6 +162,10 @@ cc_library( deps = [ ":honggfuzz_common", ], + target_compatible_with = select({ + "@platforms//os:linux": [], + "//conditions:default": ["@platforms//:incompatible"], + }), alwayslink = 1, )