aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build-replayer.yml76
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/replay/BUILD.bazel18
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java159
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel25
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java4
-rw-r--r--agent/src/main/native/com/code_intelligence/jazzer/replay/BUILD.bazel13
-rw-r--r--agent/src/main/native/com/code_intelligence/jazzer/replay/com_code_intelligence_jazzer_replay_Replayer.cpp48
-rw-r--r--driver/BUILD.bazel10
-rw-r--r--examples/BUILD.bazel30
-rwxr-xr-xexamples/check_for_finding.sh30
-rw-r--r--examples/json_sanitizer_denylist_crashbin0 -> 38 bytes
11 files changed, 401 insertions, 12 deletions
diff --git a/.github/workflows/build-replayer.yml b/.github/workflows/build-replayer.yml
new file mode 100644
index 00000000..aa617416
--- /dev/null
+++ b/.github/workflows/build-replayer.yml
@@ -0,0 +1,76 @@
+name: Release replayer
+
+on:
+ workflow_dispatch:
+
+jobs:
+
+ build_replayer:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-10.15]
+ include:
+ - os: ubuntu-latest
+ arch: "linux"
+ cache: "/home/runner/.cache/bazel-disk"
+ - os: macos-10.15
+ arch: "darwin"
+ cache: "/private/var/tmp/bazel-disk"
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set up JDK
+ uses: actions/setup-java@v1
+ with:
+ java-version: 8
+
+ - name: Mount Bazel disk cache
+ uses: actions/cache@v2
+ with:
+ path: ${{ matrix.cache }}
+ key: bazel-disk-cache-${{ matrix.arch }}-8
+
+ - name: Build
+ run: |
+ bazelisk build --config=ci --remote_header=x-buildbuddy-api-key=${{ secrets.BUILDBUDDY_API_KEY }} --disk_cache=${{ matrix.cache }} --java_runtime_version=localjdk_${{ matrix.jdk }} //agent/src/main/java/com/code_intelligence/jazzer/replay:Replayer_deploy.jar
+ cp -L bazel-bin/agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer_deploy.jar replayer.jar
+
+ - name: Upload test logs
+ if: always()
+ uses: actions/upload-artifact@v2
+ with:
+ name: replayer_${{ matrix.arch }}.jar
+ path: replayer.jar
+
+ merge_jars:
+ runs-on: ubuntu-latest
+ needs: build_replayer
+
+ steps:
+ - name: Download macOS jar
+ uses: actions/download-artifact@v2
+ with:
+ name: replayer_darwin.jar
+ path: replayer_darwin
+
+ - name: Download Linux jar
+ uses: actions/download-artifact@v2
+ with:
+ name: replayer_linux.jar
+ path: replayer_linux
+
+ - name: Merge jars
+ run: |
+ mkdir merged
+ unzip -o replayer_darwin/replayer.jar -d merged
+ unzip -o replayer_linux/replayer.jar -d merged
+ jar cvmf merged/META-INF/MANIFEST.MF replayer.jar -C merged .
+
+ - name: Upload merged jar
+ uses: actions/upload-artifact@v2
+ with:
+ name: replayer.jar
+ path: replayer.jar
+
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/replay/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/replay/BUILD.bazel
new file mode 100644
index 00000000..37365d8c
--- /dev/null
+++ b/agent/src/main/java/com/code_intelligence/jazzer/replay/BUILD.bazel
@@ -0,0 +1,18 @@
+load("@fmeum_rules_jni//jni:defs.bzl", "java_library_with_native")
+
+java_library_with_native(
+ name = "replay",
+ srcs = ["Replayer.java"],
+ native_libs = ["//agent/src/main/native/com/code_intelligence/jazzer/replay"],
+ visibility = ["//agent/src/main/native/com/code_intelligence/jazzer/replay:__pkg__"],
+ deps = [
+ "//agent/src/main/java/com/code_intelligence/jazzer/api",
+ "//agent/src/main/java/com/code_intelligence/jazzer/runtime:fuzzed_data_provider",
+ ],
+)
+
+java_binary(
+ name = "Replayer",
+ visibility = ["//visibility:public"],
+ runtime_deps = [":replay"],
+)
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java b/agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java
new file mode 100644
index 00000000..fc6bfc4f
--- /dev/null
+++ b/agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java
@@ -0,0 +1,159 @@
+// Copyright 2021 Code Intelligence GmbH
+//
+// 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.code_intelligence.jazzer.replay;
+
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+import com.code_intelligence.jazzer.runtime.FuzzedDataProviderImpl;
+import com.github.fmeum.rules_jni.RulesJni;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class Replayer {
+ public static final int STATUS_FINDING = 77;
+ public static final int STATUS_OTHER_ERROR = 1;
+
+ static {
+ try {
+ RulesJni.loadLibrary("replay", Replayer.class);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ System.exit(STATUS_OTHER_ERROR);
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length < 2) {
+ System.err.println("Usage: <fuzz target class> <input file path> <fuzzerInitialize args>...");
+ System.exit(STATUS_OTHER_ERROR);
+ }
+ ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
+
+ Class<?> fuzzTargetClass;
+ try {
+ fuzzTargetClass = Class.forName(args[0]);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ System.exit(STATUS_OTHER_ERROR);
+ // Without this return the compiler sees fuzzTargetClass as possibly uninitialized.
+ return;
+ }
+
+ String inputFilePath = args[1];
+ byte[] input = loadInput(inputFilePath);
+
+ String[] fuzzTargetArgs = Arrays.copyOfRange(args, 2, args.length);
+ executeFuzzerInitialize(fuzzTargetClass, fuzzTargetArgs);
+ executeFuzzTarget(fuzzTargetClass, input);
+ }
+
+ private static byte[] loadInput(String inputFilePath) {
+ try {
+ return Files.readAllBytes(Paths.get(inputFilePath));
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(STATUS_OTHER_ERROR);
+ // Without this return the compiler sees loadInput as possibly not returning a value.
+ return null;
+ }
+ }
+
+ private static void executeFuzzerInitialize(Class<?> fuzzTarget, String[] args) {
+ // public static void fuzzerInitialize()
+ try {
+ Method fuzzerInitialize = fuzzTarget.getMethod("fuzzerInitialize");
+ fuzzerInitialize.invoke(null);
+ return;
+ } catch (Exception e) {
+ handleInvokeException(e, fuzzTarget);
+ }
+ // public static void fuzzerInitialize(String[] args)
+ try {
+ Method fuzzerInitialize = fuzzTarget.getMethod("fuzzerInitialize", String[].class);
+ fuzzerInitialize.invoke(null, (Object) args);
+ } catch (Exception e) {
+ handleInvokeException(e, fuzzTarget);
+ }
+ }
+
+ public static void executeFuzzTarget(Class<?> fuzzTarget, byte[] input) {
+ // public static void fuzzerTestOneInput(byte[] input)
+ try {
+ Method fuzzerTestOneInput = fuzzTarget.getMethod("fuzzerTestOneInput", byte[].class);
+ fuzzerTestOneInput.invoke(null, (Object) input);
+ return;
+ } catch (Exception e) {
+ handleInvokeException(e, fuzzTarget);
+ }
+ // public static void fuzzerTestOneInput(FuzzedDataProvider data)
+ try {
+ Method fuzzerTestOneInput =
+ fuzzTarget.getMethod("fuzzerTestOneInput", FuzzedDataProvider.class);
+ fuzzerTestOneInput.invoke(null, makeFuzzedDataProvider(input));
+ return;
+ } catch (Exception e) {
+ handleInvokeException(e, fuzzTarget);
+ }
+ System.err.printf("%s must define exactly one of the following two functions:%n"
+ + " public static void fuzzerTestOneInput(byte[] ...)%n"
+ + " public static void fuzzerTestOneInput(FuzzedDataProvider ...)%n"
+ + "Note: Fuzz targets returning boolean are no longer supported; exceptions should%n"
+ + "be thrown instead of returning true.%n",
+ fuzzTarget.getName());
+ System.exit(STATUS_OTHER_ERROR);
+ }
+
+ private static void handleInvokeException(Exception e, Class<?> fuzzTarget) {
+ if (e instanceof NoSuchMethodException)
+ return;
+ if (e instanceof InvocationTargetException) {
+ filterOutOwnStackTraceElements(e.getCause(), fuzzTarget);
+ e.getCause().printStackTrace();
+ System.exit(STATUS_FINDING);
+ } else {
+ e.printStackTrace();
+ System.exit(STATUS_OTHER_ERROR);
+ }
+ }
+
+ private static void filterOutOwnStackTraceElements(Throwable t, Class<?> fuzzTarget) {
+ if (t.getCause() != null)
+ filterOutOwnStackTraceElements(t.getCause(), fuzzTarget);
+ if (t.getStackTrace() == null || t.getStackTrace().length == 0)
+ return;
+ StackTraceElement lowestFrame = t.getStackTrace()[t.getStackTrace().length - 1];
+ if (!lowestFrame.getClassName().equals(Replayer.class.getName())
+ || !lowestFrame.getMethodName().equals("main"))
+ return;
+ for (int i = t.getStackTrace().length - 1; i >= 0; i--) {
+ StackTraceElement frame = t.getStackTrace()[i];
+ if (frame.getClassName().equals(fuzzTarget.getName())
+ && frame.getMethodName().equals("fuzzerTestOneInput")) {
+ t.setStackTrace(Arrays.copyOfRange(t.getStackTrace(), 0, i + 1));
+ break;
+ }
+ }
+ }
+
+ private static FuzzedDataProvider makeFuzzedDataProvider(byte[] input) {
+ feedFuzzedDataProvider(input);
+ return new FuzzedDataProviderImpl();
+ }
+
+ private static native void feedFuzzedDataProvider(byte[] input);
+}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
index 764387f0..ff05e026 100644
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
+++ b/agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
@@ -1,13 +1,30 @@
load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
+java_library(
+ name = "fuzzed_data_provider",
+ srcs = [
+ "FuzzedDataProviderImpl.java",
+ ],
+ visibility = ["//agent/src/main/java/com/code_intelligence/jazzer/replay:__pkg__"],
+ deps = [
+ "//agent/src/main/java/com/code_intelligence/jazzer/api",
+ ],
+)
+
kt_jvm_library(
name = "runtime",
- srcs = glob([
- "*.java",
- "*.kt",
- ]),
+ srcs = glob(
+ [
+ "*.java",
+ "*.kt",
+ ],
+ exclude = [
+ "FuzzedDataProviderImpl.java",
+ ],
+ ),
visibility = ["//visibility:public"],
deps = [
+ ":fuzzed_data_provider",
"//agent/src/main/java/com/code_intelligence/jazzer/api",
"//agent/src/main/java/com/code_intelligence/jazzer/utils",
],
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java
index 0f4708e5..fe4d8ac7 100644
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java
+++ b/agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java
@@ -16,8 +16,8 @@ package com.code_intelligence.jazzer.runtime;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
-class FuzzedDataProviderImpl implements FuzzedDataProvider {
- FuzzedDataProviderImpl() {}
+public class FuzzedDataProviderImpl implements FuzzedDataProvider {
+ public FuzzedDataProviderImpl() {}
@Override public native boolean consumeBoolean();
diff --git a/agent/src/main/native/com/code_intelligence/jazzer/replay/BUILD.bazel b/agent/src/main/native/com/code_intelligence/jazzer/replay/BUILD.bazel
new file mode 100644
index 00000000..709156c3
--- /dev/null
+++ b/agent/src/main/native/com/code_intelligence/jazzer/replay/BUILD.bazel
@@ -0,0 +1,13 @@
+load("@fmeum_rules_jni//jni:defs.bzl", "java_native_library")
+
+java_native_library(
+ name = "replay",
+ srcs = [
+ "com_code_intelligence_jazzer_replay_Replayer.cpp",
+ ],
+ java_lib = "//agent/src/main/java/com/code_intelligence/jazzer/replay",
+ visibility = ["//agent/src/main/java/com/code_intelligence/jazzer/replay:__pkg__"],
+ deps = [
+ "//driver:fuzzed_data_provider",
+ ],
+)
diff --git a/agent/src/main/native/com/code_intelligence/jazzer/replay/com_code_intelligence_jazzer_replay_Replayer.cpp b/agent/src/main/native/com/code_intelligence/jazzer/replay/com_code_intelligence_jazzer_replay_Replayer.cpp
new file mode 100644
index 00000000..c4bdfcfb
--- /dev/null
+++ b/agent/src/main/native/com/code_intelligence/jazzer/replay/com_code_intelligence_jazzer_replay_Replayer.cpp
@@ -0,0 +1,48 @@
+// Copyright 2021 Code Intelligence GmbH
+//
+// 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.
+
+#include "com_code_intelligence_jazzer_replay_Replayer.h"
+
+#include <jni.h>
+
+#include "driver/fuzzed_data_provider.h"
+
+namespace {
+uint8_t *data = nullptr;
+}
+
+void Java_com_code_1intelligence_jazzer_replay_Replayer_feedFuzzedDataProvider(
+ JNIEnv *env, jclass, jbyteArray input) {
+ if (data == nullptr) {
+ jazzer::SetUpFuzzedDataProvider(*env);
+ } else {
+ delete[] data;
+ }
+
+ std::size_t size = env->GetArrayLength(input);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->FatalError("Failed to get length of input");
+ }
+ data = static_cast<uint8_t *>(operator new(size));
+ if (data == nullptr) {
+ env->FatalError("Failed to allocate memory for a copy of the input");
+ }
+ env->GetByteArrayRegion(input, 0, size, reinterpret_cast<jbyte *>(data));
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->FatalError("Failed to copy input");
+ }
+ jazzer::FeedFuzzedDataProvider(data, size);
+}
diff --git a/driver/BUILD.bazel b/driver/BUILD.bazel
index f15a2ddd..d0e0d531 100644
--- a/driver/BUILD.bazel
+++ b/driver/BUILD.bazel
@@ -1,10 +1,5 @@
load("//bazel:cc.bzl", "cc_17_library")
-# None of the targets in this package should be built into shared objects. By
-# disabling the supports_pic feature, we reduce build time otherwise spent on
-# .pic.o files.
-package(features = ["-supports_pic"])
-
cc_library(
name = "sanitizer_hooks_with_pc",
srcs = ["sanitizer_hooks_with_pc.cpp"],
@@ -31,9 +26,12 @@ cc_library(
hdrs = [
"fuzzed_data_provider.h",
],
+ visibility = [
+ "//agent/src/main/native/com/code_intelligence/jazzer/replay:__pkg__",
+ ],
deps = [
- "@bazel_tools//tools/jdk:jni",
"@com_google_absl//absl/strings:str_format",
+ "@fmeum_rules_jni//jni",
],
)
diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel
index ca17ba3f..d721b81d 100644
--- a/examples/BUILD.bazel
+++ b/examples/BUILD.bazel
@@ -150,6 +150,36 @@ java_fuzz_target_test(
],
)
+java_binary(
+ name = "JsonSanitizerDenylistCrash",
+ args = [
+ "com.example.JsonSanitizerDenylistFuzzer",
+ "$(location :json_sanitizer_denylist_crash)",
+ ],
+ data = [
+ ":json_sanitizer_denylist_crash",
+ ],
+ main_class = "com.code_intelligence.jazzer.replay.Replayer",
+ runtime_deps = [
+ ":JsonSanitizerDenylistFuzzer_target_deploy.jar",
+ "//agent/src/main/java/com/code_intelligence/jazzer/replay:Replayer_deploy.jar",
+ ],
+)
+
+sh_test(
+ name = "JsonSanitizerDenylistCrashTest",
+ srcs = ["check_for_finding.sh"],
+ args = [
+ "$(location :JsonSanitizerDenylistCrash)",
+ "com.example.JsonSanitizerDenylistFuzzer",
+ "$(location :json_sanitizer_denylist_crash)",
+ ],
+ data = [
+ ":JsonSanitizerDenylistCrash",
+ ":json_sanitizer_denylist_crash",
+ ],
+)
+
java_fuzz_target_test(
name = "JsonSanitizerIdempotenceFuzzer",
srcs = [
diff --git a/examples/check_for_finding.sh b/examples/check_for_finding.sh
new file mode 100755
index 00000000..f60c011c
--- /dev/null
+++ b/examples/check_for_finding.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+# Copyright 2021 Code Intelligence GmbH
+#
+# 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.
+
+eval "$@"
+# Assert that we either found a crash in java (exit code 77) or an ASan crash
+# (exit code 76).
+declare -i exit_code=$?
+if [ $exit_code -eq 77 ] || [ $exit_code -eq 76 ]
+then
+ if [ "$(ls "$DEFAULT_CRASH_PREFIX/")" ]; then
+ exit 0
+ else
+ exit 1
+ fi
+else
+ echo "Unexpected exit code: $exit_code"
+ exit 1
+fi
diff --git a/examples/json_sanitizer_denylist_crash b/examples/json_sanitizer_denylist_crash
new file mode 100644
index 00000000..7324203a
--- /dev/null
+++ b/examples/json_sanitizer_denylist_crash
Binary files differ