Skip to content

Commit

Permalink
Update and simplify Jazzer integration (#218)
Browse files Browse the repository at this point in the history
* Update Jazzer to v0.17.1

* Add support for sanitizer runtimes

* Refactor oss-fuzz support

* Address review comments
  • Loading branch information
fmeum authored Jul 7, 2023
1 parent 964d773 commit d93363c
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 256 deletions.
4 changes: 4 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
build --action_env=CC=clang-10
build --action_env=CXX=clang++-10

# Workaround for https://github.com/bazelbuild/bazel/issues/3236
build --sandbox_tmpfs_path=/tmp

# Strict dependency check for C++ includes.
build --features=layering_check

Expand Down Expand Up @@ -64,6 +67,7 @@ build:asan-replay --@rules_fuzzing//fuzzing:cc_engine_instrumentation=none
build:asan-replay --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan

build:oss-fuzz --//fuzzing:cc_engine=@rules_fuzzing_oss_fuzz//:oss_fuzz_engine
build:oss-fuzz --//fuzzing:java_engine=@rules_fuzzing_oss_fuzz//:oss_fuzz_java_engine
build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=oss-fuzz
build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=none

Expand Down
20 changes: 1 addition & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,25 +156,7 @@ The crash is saved under `/tmp/fuzzing/artifacts` and can be further inspected.

### Java fuzzing

You can write `java_fuzz_test`s through the [Jazzer][jazzer-doc] fuzzing engine. You will need to enable it in your WORKSPACE `rules_fuzzing_dependencies` call:

```python
load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")

rules_fuzzing_dependencies(jazzer = True)

load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init")

rules_fuzzing_init()

load("@jazzer//:repositories.bzl", "jazzer_dependencies")

jazzer_dependencies()

load("@jazzer//:init.bzl", "jazzer_init")

jazzer_init()
```
You can write `java_fuzz_test`s through the [Jazzer][jazzer-doc] fuzzing engine.

To use Jazzer, it is convenient to also define a `.bazelrc` configuration, similar to the C++ libFuzzer one above:

Expand Down
10 changes: 1 addition & 9 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")

rules_fuzzing_dependencies(jazzer = True)
rules_fuzzing_dependencies()

load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init")

Expand All @@ -30,14 +30,6 @@ load("@fuzzing_py_deps//:requirements.bzl", "install_deps")

install_deps()

load("@jazzer//:repositories.bzl", "jazzer_dependencies")

jazzer_dependencies()

load("@jazzer//:init.bzl", "jazzer_init")

jazzer_init()

# The support for running the examples and unit tests.

http_archive(
Expand Down
12 changes: 11 additions & 1 deletion fuzzing/engines/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ java_fuzzing_engine(
name = "jazzer",
display_name = "Jazzer",
launcher = "jazzer_launcher.sh",
library = "@jazzer//agent:jazzer_api_compile_only",
library = ":jazzer_stub",
visibility = ["//visibility:public"],
)

# This wrapper target is needed as Jazzer consists of two separate Java targets,
# but java_fuzzing_engine's library attribute only accepts a single target.
java_library(
name = "jazzer_stub",
exports = [
"@rules_fuzzing_jazzer//jar",
"@rules_fuzzing_jazzer_api//jar",
],
)
22 changes: 7 additions & 15 deletions fuzzing/private/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,28 @@ exports_files([
"util.bzl",
])

# Config settings needed for prebuilt engines.
config_setting(
name = "use_sanitizer_none",
name = "is_oss_fuzz",
flag_values = {
"@rules_fuzzing//fuzzing:cc_engine_sanitizer": "none",
"@rules_fuzzing//fuzzing:cc_engine": "@rules_fuzzing_oss_fuzz//:oss_fuzz_engine",
},
visibility = ["//visibility:public"],
)

config_setting(
name = "use_sanitizer_asan",
name = "use_asan",
flag_values = {
"@rules_fuzzing//fuzzing:cc_engine_sanitizer": "asan",
},
visibility = ["//visibility:public"],
)

config_setting(
name = "use_sanitizer_ubsan",
name = "use_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",
# This is required to make the setting an unambiguous specialization of
# the use_sanitizer_* settings.
"@rules_fuzzing//fuzzing:cc_engine_sanitizer": "none",
},
visibility = ["//visibility:public"],
)

exports_files([
Expand Down
87 changes: 54 additions & 33 deletions fuzzing/private/fuzz_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

"""The implementation of the {cc, java}_fuzz_test rules."""

load("@rules_fuzzing_oss_fuzz//:instrum.bzl", "native_library_sanitizer")
load("@rules_cc//cc:defs.bzl", "cc_binary")

# FIXME: Including this leads to a Stardoc error since defs.bzl is not visible. As a workaround, use native.java_binary.
Expand Down Expand Up @@ -213,6 +214,15 @@ def cc_fuzz_test(
test_timeout = timeout,
)

_ASAN_RUNTIME = Label("//fuzzing/private/runtime:asan")
_UBSAN_RUNTIME = Label("//fuzzing/private/runtime:ubsan")
_RUNTIME_BY_NAME = {
"asan": _ASAN_RUNTIME,
"ubsan": _UBSAN_RUNTIME,
"none": None,
}

# buildifier: disable=list-append
def java_fuzz_test(
name,
srcs = None,
Expand Down Expand Up @@ -264,6 +274,8 @@ def java_fuzz_test(
# this target directly. Instead, the binary should be built through the
# instrumented configuration.
raw_target_name = name + "_target_"
metadata_binary_name = name + "_metadata_"
metadata_deploy_jar_name = metadata_binary_name + "_deploy.jar"

# Determine a value for target_class heuristically using the same rules as
# those used by Bazel internally for main_class.
Expand All @@ -277,62 +289,71 @@ def java_fuzz_test(
name = name,
))
target_class_manifest_line = "Jazzer-Fuzz-Target-Class: %s" % target_class
binary_kwargs.setdefault("deps", [])

# Use += rather than append to allow users to pass in select() expressions for
# deps, which only support concatenation with +.
# Workaround for https://github.com/bazelbuild/bazel/issues/14157.
# buildifier: disable=list-append
binary_kwargs["deps"] += [engine]
binary_kwargs.setdefault("deploy_manifest_lines", [])
native.java_binary(
name = metadata_binary_name,
deploy_manifest_lines = [target_class_manifest_line],
tags = ["manual"],
)

# buildifier: disable=list-append
binary_kwargs["deploy_manifest_lines"] += [target_class_manifest_line]
# use += rather than append to allow users to pass in select() expressions for
# deps, which only support concatenation with +.
# workaround for https://github.com/bazelbuild/bazel/issues/14157.
if srcs:
binary_kwargs.setdefault("deps", [])
binary_kwargs["deps"] += [engine, metadata_deploy_jar_name]
else:
binary_kwargs.setdefault("runtime_deps", [])
binary_kwargs["runtime_deps"] += [engine, metadata_deploy_jar_name]

binary_kwargs.setdefault("jvm_flags", [])
binary_kwargs["jvm_flags"] = [
# Ensures that full stack traces are emitted for findings even in highly
# optimized code.
"-XX:-OmitStackTraceInFastThrow",
# Optimized for throughput rather than latency.
"-XX:+UseParallelGC",
# Ignore CriticalJNINatives if not available (JDK 18+).
"-XX:+IgnoreUnrecognizedVMOptions",
# Improves performance of Jazzer's native compare instrumentation.
"-XX:+CriticalJNINatives",
] + binary_kwargs["jvm_flags"]

# tags is not configurable and can thus use append.
binary_kwargs.setdefault("tags", []).append("manual")
native.java_binary(
name = raw_target_name,
srcs = srcs,
create_executable = False,
main_class = "com.code_intelligence.jazzer.Jazzer",
**binary_kwargs
)

raw_binary_name = name + "_raw_"
jazzer_fuzz_binary(
name = raw_binary_name,
agent = select({
"@rules_fuzzing//fuzzing/private:use_oss_fuzz": "@rules_fuzzing_oss_fuzz//:jazzer_agent_deploy.jar",
"//conditions:default": "@jazzer//agent:jazzer_agent_deploy.jar",
sanitizer = select({
"@rules_fuzzing//fuzzing/private:is_oss_fuzz": native_library_sanitizer,
"@rules_fuzzing//fuzzing/private:use_asan": "asan",
"@rules_fuzzing//fuzzing/private:use_ubsan": "ubsan",
"//conditions:default": "none",
}),
# Since the choice of sanitizer is explicit for local fuzzing, we also
# let it apply to projects with no native dependencies.
driver_java_only = select({
"@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",
"@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",
"@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",
"@rules_fuzzing//fuzzing/private:is_oss_fuzz": Label("//fuzzing/private:oss_fuzz_jazzer_sanitizer_options.sh"),
"//conditions:default": Label("//fuzzing/private:local_jazzer_sanitizer_options.sh"),
}),
sanitizer_runtime = select({
"@rules_fuzzing//fuzzing/private:is_oss_fuzz": _RUNTIME_BY_NAME[native_library_sanitizer],
"@rules_fuzzing//fuzzing/private:use_asan": _ASAN_RUNTIME,
"@rules_fuzzing//fuzzing/private:use_ubsan": _UBSAN_RUNTIME,
"//conditions:default": None,
}),
tags = ["manual"],
target = raw_target_name,
target_deploy_jar = raw_target_name + "_deploy.jar",
tags = ["manual"],
)

fuzzing_decoration(
name = name,
raw_binary = raw_binary_name,
# jazzer_fuzz_binary already instrumented the native dependencies.
instrument_binary = False,
engine = engine,
corpus = corpus,
dicts = dicts,
Expand Down
Loading

0 comments on commit d93363c

Please sign in to comment.