diff options
Diffstat (limited to 'examples')
25 files changed, 1257 insertions, 0 deletions
diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel new file mode 100644 index 00000000..dde8aaeb --- /dev/null +++ b/examples/BUILD.bazel @@ -0,0 +1,321 @@ +load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") +load("@fmeum_rules_jni//jni:defs.bzl", "java_jni_library") +load("//bazel:compat.bzl", "SKIP_ON_MACOS", "SKIP_ON_WINDOWS") +load("//bazel:fuzz_target.bzl", "java_fuzz_target_test") + +java_fuzz_target_test( + name = "Autofuzz", + fuzzer_args = [ + "--autofuzz=com.google.json.JsonSanitizer::sanitize", + # Exit after the first finding for testing purposes. + "--keep_going=1", + ], + runtime_deps = [ + "@maven//:com_mikesamuel_json_sanitizer", + ], +) + +java_fuzz_target_test( + name = "ExampleFuzzer", + srcs = [ + "src/main/java/com/example/ExampleFuzzer.java", + "src/main/java/com/example/ExampleFuzzerHooks.java", + ], + # Comment out the next line to keep the fuzzer running indefinitely. + hook_classes = ["com.example.ExampleFuzzerHooks"], + target_class = "com.example.ExampleFuzzer", +) + +java_jni_library( + name = "example_fuzzer_with_native_lib", + srcs = [ + "src/main/java/com/example/ExampleFuzzerWithNative.java", + ], + native_libs = [ + "//examples/src/main/native/com/example:native_asan", + "//examples/src/main/native/com/example:native_ubsan", + ], + visibility = ["//examples/src/main/native/com/example:__pkg__"], + deps = [ + "//agent:jazzer_api_compile_only", + ], +) + +java_fuzz_target_test( + name = "ExampleFuzzerWithASan", + fuzzer_args = ["--jvm_args=-Djazzer.native_lib=native_asan"], + sanitizer = "address", + target_class = "com.example.ExampleFuzzerWithNative", + runtime_deps = [ + ":example_fuzzer_with_native_lib", + ], +) + +java_fuzz_target_test( + name = "ExampleFuzzerWithUBSan", + fuzzer_args = ["--jvm_args=-Djazzer.native_lib=native_ubsan"], + sanitizer = "undefined", + target_class = "com.example.ExampleFuzzerWithNative", + # Crashes at runtime without an error message. + target_compatible_with = SKIP_ON_WINDOWS, + runtime_deps = [ + ":example_fuzzer_with_native_lib", + ], +) + +java_fuzz_target_test( + name = "ExamplePathTraversalFuzzer", + srcs = [ + "src/main/java/com/example/ExamplePathTraversalFuzzer.java", + "src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java", + ], + hook_classes = ["com.example.ExamplePathTraversalFuzzerHooks"], + target_class = "com.example.ExamplePathTraversalFuzzer", +) + +java_fuzz_target_test( + name = "ExampleValueProfileFuzzer", + srcs = [ + "src/main/java/com/example/ExampleValueProfileFuzzer.java", + ], + # Comment out the next line to keep the fuzzer running indefinitely. + fuzzer_args = ["-use_value_profile=1"], + target_class = "com.example.ExampleValueProfileFuzzer", +) + +java_fuzz_target_test( + name = "ExampleOutOfMemoryFuzzer", + srcs = [ + "src/main/java/com/example/ExampleOutOfMemoryFuzzer.java", + ], + fuzzer_args = ["--jvm_args=-Xmx512m"], + target_class = "com.example.ExampleOutOfMemoryFuzzer", +) + +java_fuzz_target_test( + name = "ExampleStackOverflowFuzzer", + srcs = [ + "src/main/java/com/example/ExampleStackOverflowFuzzer.java", + ], + target_class = "com.example.ExampleStackOverflowFuzzer", + # Crashes with a segfault before any stack trace printing is reached. + target_compatible_with = SKIP_ON_MACOS, +) + +# WARNING: This fuzz target uses a vulnerable version of log4j, which could result in the execution +# of arbitrary code during fuzzing if executed with an older JDK. Use at your own risk. +java_fuzz_target_test( + name = "Log4jFuzzer", + timeout = "long", + srcs = [ + "src/main/java/com/example/Log4jFuzzer.java", + ], + fuzzer_args = [ + "-fork=4", + "-use_value_profile=1", + ], + # Finding this bug takes ~5 minutes on a decent laptop, but the GitHub Actions machines are not + # powerful enough to run it as part of our test suite. + tags = ["manual"], + target_class = "com.example.Log4jFuzzer", + deps = [ + "@maven//:org_apache_logging_log4j_log4j_api", + "@maven//:org_apache_logging_log4j_log4j_core", + ], +) + +java_fuzz_target_test( + name = "JpegImageParserFuzzer", + srcs = [ + "src/main/java/com/example/JpegImageParserFuzzer.java", + ], + fuzzer_args = [ + "-fork=5", + "--additional_jvm_args=-Dbaz=baz", + ] + select({ + # \\\\ becomes \\ when evaluated as a Starlark string literal, then \ in + # java_fuzz_target_test. + "@platforms//os:windows": ["--jvm_args=-Dfoo=foo;-Dbar=b\\\\;ar"], + "//conditions:default": ["--jvm_args=-Dfoo=foo:-Dbar=b\\\\:ar"], + }), + target_class = "com.example.JpegImageParserFuzzer", + # The exit codes of the forked libFuzzer processes are not picked up correctly. + target_compatible_with = SKIP_ON_MACOS, + deps = [ + "@maven//:org_apache_commons_commons_imaging", + ], +) + +java_fuzz_target_test( + name = "GifImageParserFuzzer", + srcs = [ + "src/main/java/com/example/GifImageParserFuzzer.java", + ], + target_class = "com.example.GifImageParserFuzzer", + deps = [ + "@maven//:org_apache_commons_commons_imaging", + ], +) + +java_fuzz_target_test( + name = "TiffImageParserFuzzer", + srcs = [ + "src/main/java/com/example/TiffImageParserFuzzer.java", + ], + tags = ["manual"], + target_class = "com.example.TiffImageParserFuzzer", + deps = [ + "@maven//:org_apache_commons_commons_imaging", + ], +) + +java_fuzz_target_test( + name = "JsonSanitizerCrashFuzzer", + srcs = [ + "src/main/java/com/example/JsonSanitizerCrashFuzzer.java", + ], + target_class = "com.example.JsonSanitizerCrashFuzzer", + deps = [ + "@maven//:com_mikesamuel_json_sanitizer", + ], +) + +java_fuzz_target_test( + name = "JsonSanitizerDenylistFuzzer", + srcs = [ + "src/main/java/com/example/JsonSanitizerDenylistFuzzer.java", + ], + target_class = "com.example.JsonSanitizerDenylistFuzzer", + deps = [ + "@maven//:com_mikesamuel_json_sanitizer", + ], +) + +java_binary( + name = "JsonSanitizerReplayerCrash", + 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 = "JsonSanitizerReplayerCrashTest", + srcs = ["check_for_finding.sh"], + args = [ + "jazzer/$(rootpath :JsonSanitizerReplayerCrash)", + "com.example.JsonSanitizerDenylistFuzzer", + "jazzer/$(rootpath :json_sanitizer_denylist_crash)", + ], + data = [ + ":JsonSanitizerReplayerCrash", + ":json_sanitizer_denylist_crash", + ], + deps = [ + "@bazel_tools//tools/bash/runfiles", + ], +) + +java_fuzz_target_test( + name = "JsonSanitizerIdempotenceFuzzer", + srcs = [ + "src/main/java/com/example/JsonSanitizerIdempotenceFuzzer.java", + ], + target_class = "com.example.JsonSanitizerIdempotenceFuzzer", + deps = [ + "@maven//:com_mikesamuel_json_sanitizer", + ], +) + +java_fuzz_target_test( + name = "JsonSanitizerValidJsonFuzzer", + srcs = [ + "src/main/java/com/example/JsonSanitizerValidJsonFuzzer.java", + ], + target_class = "com.example.JsonSanitizerValidJsonFuzzer", + deps = [ + "@maven//:com_google_code_gson_gson", + "@maven//:com_mikesamuel_json_sanitizer", + ], +) + +java_fuzz_target_test( + name = "JacksonCborFuzzer", + srcs = [ + "src/main/java/com/example/JacksonCborFuzzer.java", + ], + target_class = "com.example.JacksonCborFuzzer", + deps = [ + "@maven//:com_fasterxml_jackson_core_jackson_core", + "@maven//:com_fasterxml_jackson_core_jackson_databind", + "@maven//:com_fasterxml_jackson_dataformat_jackson_dataformat_cbor", + ], +) + +java_fuzz_target_test( + name = "FastJsonFuzzer", + srcs = [ + "src/main/java/com/example/FastJsonFuzzer.java", + ], + target_class = "com.example.FastJsonFuzzer", + deps = [ + "@maven//:com_alibaba_fastjson", + ], +) + +kt_jvm_library( + name = "KlaxonFuzzTarget", + srcs = [ + "src/main/java/com/example/KlaxonFuzzer.kt", + ], + deps = [ + "//agent:jazzer_api_compile_only", + "@maven//:com_beust_klaxon", + ], +) + +java_fuzz_target_test( + name = "KlaxonFuzzer", + fuzzer_args = [ + "--keep_going=7", + ], + target_class = "com.example.KlaxonFuzzer", + runtime_deps = [":KlaxonFuzzTarget"], +) + +java_fuzz_target_test( + name = "TurboJpegFuzzer", + srcs = [ + "src/main/java/com/example/TurboJpegFuzzer.java", + ], + fuzzer_args = [ + "-rss_limit_mb=8196", + ], + native_libs = [ + "@libjpeg_turbo//:turbojpeg_native", + ], + sanitizer = "address", + tags = ["manual"], + target_class = "com.example.TurboJpegFuzzer", + deps = [ + "@libjpeg_turbo//:turbojpeg_java", + ], +) + +java_binary( + name = "examples", + create_executable = False, + visibility = ["//visibility:public"], + runtime_deps = [ + ":ExampleFuzzer_target_deploy.jar", + ":ExampleValueProfileFuzzer_target_deploy.jar", + ":FastJsonFuzzer_target_deploy.jar", + ":JacksonCborFuzzer_target_deploy.jar", + ":JpegImageParserFuzzer_target_deploy.jar", + ":JsonSanitizerDenylistFuzzer_target_deploy.jar", + ], +) diff --git a/examples/check_for_finding.sh b/examples/check_for_finding.sh new file mode 100755 index 00000000..afe110c6 --- /dev/null +++ b/examples/check_for_finding.sh @@ -0,0 +1,41 @@ +#!/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. + +# --- begin runfiles.bash initialization v2 --- +# Copy-pasted from the Bazel Bash runfiles library v2. +set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ + source "$0.runfiles/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e +# --- end runfiles.bash initialization v2 --- + +# Temporarily disable exit on error since we expect the command below to fail. +set +e +"$(rlocation "$1")" "$2" "$(rlocation "$3")" "${@:4}" +declare -i exit_code=$? +set -e + +# Assert that we either found a crash in java (exit code 77) or an ASan crash +# (exit code 76). +if [ $exit_code -eq 77 ] || [ $exit_code -eq 76 ] +then + exit 0 +else + echo "Unexpected exit code: $exit_code" + exit 1 +fi diff --git a/examples/json_sanitizer_denylist_crash b/examples/json_sanitizer_denylist_crash Binary files differnew file mode 100644 index 00000000..7324203a --- /dev/null +++ b/examples/json_sanitizer_denylist_crash diff --git a/examples/src/main/java/com/example/ExampleFuzzer.java b/examples/src/main/java/com/example/ExampleFuzzer.java new file mode 100644 index 00000000..073d924a --- /dev/null +++ b/examples/src/main/java/com/example/ExampleFuzzer.java @@ -0,0 +1,40 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import java.security.SecureRandom; + +public class ExampleFuzzer { + public static void fuzzerInitialize() { + // Optional initialization to be run before the first call to fuzzerTestOneInput. + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + // Without the hook in ExampleFuzzerHooks.java, the value of random would change on every + // invocation, making it almost impossible to guess for the fuzzer. + long random = new SecureRandom().nextLong(); + if (input.startsWith("magicstring" + random) && input.length() > 30 + && input.charAt(25) == 'C') { + mustNeverBeCalled(); + } + } + + private static void mustNeverBeCalled() { + throw new FuzzerSecurityIssueMedium("mustNeverBeCalled has been called"); + } +} diff --git a/examples/src/main/java/com/example/ExampleFuzzerHooks.java b/examples/src/main/java/com/example/ExampleFuzzerHooks.java new file mode 100644 index 00000000..41f16635 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleFuzzerHooks.java @@ -0,0 +1,30 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.HookType; +import com.code_intelligence.jazzer.api.MethodHook; +import java.lang.invoke.MethodHandle; + +public class ExampleFuzzerHooks { + @MethodHook(type = HookType.REPLACE, targetClassName = "java.security.SecureRandom", + targetMethod = "nextLong", targetMethodDescriptor = "()J") + public static long + getRandomNumber(MethodHandle handle, Object thisObject, Object[] args, int hookId) { + return 4; // chosen by fair dice roll. + // guaranteed to be random. + // https://xkcd.com/221/ + } +} diff --git a/examples/src/main/java/com/example/ExampleFuzzerWithNative.java b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java new file mode 100644 index 00000000..b9a13e24 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java @@ -0,0 +1,36 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.github.fmeum.rules_jni.RulesJni; + +public class ExampleFuzzerWithNative { + static { + String native_lib = System.getProperty("jazzer.native_lib"); + RulesJni.loadLibrary(native_lib, ExampleFuzzerWithNative.class); + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + int val = data.consumeInt(); + String stringData = data.consumeRemainingAsString(); + if (val == 17759716 && stringData.length() > 10 && stringData.contains("jazzer")) { + // call native function which contains a crash + new ExampleFuzzerWithNative().parse(stringData); + } + } + + private native boolean parse(String bytes); +}
\ No newline at end of file diff --git a/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java b/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java new file mode 100644 index 00000000..d704da39 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java @@ -0,0 +1,28 @@ +// 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.example; + +import java.util.ArrayList; + +public class ExampleOutOfMemoryFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + ArrayList<Byte> bytes = new ArrayList<>(); + int pos = 0; + while (pos >= 0 && pos < input.length) { + bytes.add(input[pos]); + pos += input[pos] + 1; + } + } +} diff --git a/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java b/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java new file mode 100644 index 00000000..f53ccb3c --- /dev/null +++ b/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java @@ -0,0 +1,43 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.io.File; +import java.io.IOException; + +public class ExamplePathTraversalFuzzer { + /** + * The root path for all files that this application is allowed to upload. + */ + public static final String publicFilesRootPath = "/app/upload/"; + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String relativePath = data.consumeRemainingAsAsciiString(); + // Upload the file and try very hard to ignore errors thrown during the upload. + try { + uploadFile(relativePath); + } catch (Throwable ignored) { + } + } + + private static void uploadFile(String relativePathToFile) throws IOException { + File fileToUpload = new File(publicFilesRootPath + relativePathToFile); + if (!fileToUpload.exists()) { + throw new IOException("File not found"); + } + // In a real application, the file would be uploaded to a public server here. + } +} diff --git a/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java b/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java new file mode 100644 index 00000000..b027de5b --- /dev/null +++ b/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java @@ -0,0 +1,46 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh; +import com.code_intelligence.jazzer.api.HookType; +import com.code_intelligence.jazzer.api.Jazzer; +import com.code_intelligence.jazzer.api.MethodHook; +import java.lang.invoke.MethodHandle; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class ExamplePathTraversalFuzzerHooks { + @MethodHook(type = HookType.BEFORE, targetClassName = "java.io.File", targetMethod = "<init>", + targetMethodDescriptor = "(Ljava/lang/String;)V") + public static void + fileConstructorHook(MethodHandle handle, Object thisObject, Object[] args, int hookId) { + String path = (String) args[0]; + Path normalizedPath; + try { + normalizedPath = Paths.get(path).normalize(); + } catch (InvalidPathException e) { + // Invalid paths are correctly rejected by the application. + return; + } + if (!normalizedPath.startsWith(ExamplePathTraversalFuzzer.publicFilesRootPath)) { + // Simply throwing an exception from here would not work as the calling code catches and + // ignores all Throwables. Instead, use the Jazzer API to report a finding from a hook. + Jazzer.reportFindingFromHook(new FuzzerSecurityIssueHigh( + "Path traversal discovered: '" + path + "' --> '" + normalizedPath + "'")); + } + } +} diff --git a/examples/src/main/java/com/example/ExampleStackOverflowFuzzer.java b/examples/src/main/java/com/example/ExampleStackOverflowFuzzer.java new file mode 100644 index 00000000..47166af4 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleStackOverflowFuzzer.java @@ -0,0 +1,33 @@ +// 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.example; + +import java.math.BigDecimal; + +public class ExampleStackOverflowFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + step1(); + } + + private static void step1() { + BigDecimal unused = BigDecimal.valueOf(10, 100); + step2(); + } + + private static void step2() { + boolean unused = "foobar".contains("bar"); + step1(); + } +} diff --git a/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java b/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java new file mode 100644 index 00000000..acc023a2 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java @@ -0,0 +1,53 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; +import java.util.Base64; + +public class ExampleValueProfileFuzzer { + private static String base64(byte[] input) { + return Base64.getEncoder().encodeToString(input); + } + + private static long insecureEncrypt(long input) { + long key = 0xefe4eb93215cb6b0L; + return input ^ key; + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + // Without -use_value_profile=1, the fuzzer gets stuck here as there is no direct correspondence + // between the input bytes and the compared string. With value profile, the fuzzer can guess the + // expected input byte by byte, which takes linear rather than exponential time. + if (base64(data.consumeBytes(6)).equals("SmF6emVy")) { + long[] plaintextBlocks = data.consumeLongs(2); + if (plaintextBlocks.length != 2) + return; + if (insecureEncrypt(plaintextBlocks[0]) == 0x9fc48ee64d3dc090L) { + // Without --fake_pcs (enabled by default with -use_value_profile=1), the fuzzer would get + // stuck here as the value profile information for long comparisons would not be able to + // distinguish between this comparison and the one above. + if (insecureEncrypt(plaintextBlocks[1]) == 0x888a82ff483ad9c2L) { + mustNeverBeCalled(); + } + } + } + } + + private static void mustNeverBeCalled() { + throw new FuzzerSecurityIssueLow("mustNeverBeCalled has been called"); + } +} diff --git a/examples/src/main/java/com/example/FastJsonFuzzer.java b/examples/src/main/java/com/example/FastJsonFuzzer.java new file mode 100644 index 00000000..2e5d4797 --- /dev/null +++ b/examples/src/main/java/com/example/FastJsonFuzzer.java @@ -0,0 +1,30 @@ +// 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.example; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import com.code_intelligence.jazzer.api.FuzzedDataProvider; + +// Found the issues described in +// https://github.com/alibaba/fastjson/issues/3631 +public class FastJsonFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + try { + JSON.parse(data.consumeRemainingAsString()); + } catch (JSONException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/GifImageParserFuzzer.java b/examples/src/main/java/com/example/GifImageParserFuzzer.java new file mode 100644 index 00000000..ab7de907 --- /dev/null +++ b/examples/src/main/java/com/example/GifImageParserFuzzer.java @@ -0,0 +1,32 @@ +// 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.example; + +import java.io.IOException; +import java.util.HashMap; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.common.bytesource.ByteSourceArray; +import org.apache.commons.imaging.formats.gif.GifImageParser; + +// Found https://issues.apache.org/jira/browse/IMAGING-277 and +// https://issues.apache.org/jira/browse/IMAGING-278. +public class GifImageParserFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + try { + new GifImageParser().getBufferedImage(new ByteSourceArray(input), new HashMap<>()); + } catch (IOException | ImageReadException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/JacksonCborFuzzer.java b/examples/src/main/java/com/example/JacksonCborFuzzer.java new file mode 100644 index 00000000..902c1d96 --- /dev/null +++ b/examples/src/main/java/com/example/JacksonCborFuzzer.java @@ -0,0 +1,34 @@ +// 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.example; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.cbor.CBORFactory; +import java.io.IOException; + +// Reproduces https://github.com/FasterXML/jackson-dataformats-binary/issues/236 and +// https://github.com/FasterXML/jackson-databind/pull/3032 if executed with +// `--keep_going=3 -seed=2735196724`. +public class JacksonCborFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + CBORFactory factory = new CBORFactory(); + ObjectMapper mapper = new ObjectMapper(factory); + mapper.enableDefaultTyping(); + try { + mapper.readTree(input); + } catch (IOException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/JpegImageParserFuzzer.java b/examples/src/main/java/com/example/JpegImageParserFuzzer.java new file mode 100644 index 00000000..a6898bf0 --- /dev/null +++ b/examples/src/main/java/com/example/JpegImageParserFuzzer.java @@ -0,0 +1,45 @@ +// 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.example; + +import java.io.IOException; +import java.util.HashMap; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.common.bytesource.ByteSourceArray; +import org.apache.commons.imaging.formats.jpeg.JpegImageParser; + +// Found https://issues.apache.org/jira/browse/IMAGING-275. +public class JpegImageParserFuzzer { + public static void fuzzerInitialize() { + String foo = System.getProperty("foo"); + String bar = System.getProperty("bar"); + String baz = System.getProperty("baz"); + // Only used to verify that arguments are correctly passed down to child processes. + if (foo == null || bar == null || baz == null || !foo.equals("foo") + || !(bar.equals("b;ar") || bar.equals("b:ar")) || !baz.equals("baz")) { + // Exit the process with an exit code different from that for a finding. + System.err.println("ERROR: Did not correctly pass all jvm_args to child process."); + System.err.printf("foo: %s%nbar: %s%nbaz: %s%n", foo, bar, baz); + System.exit(3); + } + } + + public static void fuzzerTestOneInput(byte[] input) { + try { + new JpegImageParser().getBufferedImage(new ByteSourceArray(input), new HashMap<>()); + } catch (IOException | ImageReadException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/JsonSanitizerCrashFuzzer.java b/examples/src/main/java/com/example/JsonSanitizerCrashFuzzer.java new file mode 100644 index 00000000..05ac4611 --- /dev/null +++ b/examples/src/main/java/com/example/JsonSanitizerCrashFuzzer.java @@ -0,0 +1,30 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.google.json.JsonSanitizer; + +public class JsonSanitizerCrashFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + try { + JsonSanitizer.sanitize(input, 10); + } catch (ArrayIndexOutOfBoundsException ignored) { + // ArrayIndexOutOfBoundsException is expected if nesting depth is + // exceeded. + } + } +} diff --git a/examples/src/main/java/com/example/JsonSanitizerDenylistFuzzer.java b/examples/src/main/java/com/example/JsonSanitizerDenylistFuzzer.java new file mode 100644 index 00000000..e715b1d9 --- /dev/null +++ b/examples/src/main/java/com/example/JsonSanitizerDenylistFuzzer.java @@ -0,0 +1,45 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.google.json.JsonSanitizer; + +public class JsonSanitizerDenylistFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + String validJson; + try { + validJson = JsonSanitizer.sanitize(input, 10); + } catch (Exception e) { + return; + } + + // Check for forbidden substrings. As these would enable Cross-Site Scripting, treat every + // finding as a high severity vulnerability. + assert !validJson.contains("</script") + : new FuzzerSecurityIssueHigh("Output contains </script"); + assert !validJson.contains("]]>") : new FuzzerSecurityIssueHigh("Output contains ]]>"); + + // Check for more forbidden substrings. As these would not directly enable Cross-Site Scripting + // in general, but may impact script execution on the embedding page, treat each finding as a + // medium severity vulnerability. + assert !validJson.contains("<script") + : new FuzzerSecurityIssueMedium("Output contains <script"); + assert !validJson.contains("<!--") : new FuzzerSecurityIssueMedium("Output contains <!--"); + } +} diff --git a/examples/src/main/java/com/example/JsonSanitizerIdempotenceFuzzer.java b/examples/src/main/java/com/example/JsonSanitizerIdempotenceFuzzer.java new file mode 100644 index 00000000..111d3de2 --- /dev/null +++ b/examples/src/main/java/com/example/JsonSanitizerIdempotenceFuzzer.java @@ -0,0 +1,34 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.google.json.JsonSanitizer; + +public class JsonSanitizerIdempotenceFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + String validJson; + try { + validJson = JsonSanitizer.sanitize(input, 10); + } catch (Exception e) { + return; + } + + // Ensure that sanitizing twice does not give different output (idempotence). Since failure to + // be idempotent is not a security issue in itself, fail with a regular AssertionError. + assert JsonSanitizer.sanitize(validJson).equals(validJson) : "Not idempotent"; + } +} diff --git a/examples/src/main/java/com/example/JsonSanitizerValidJsonFuzzer.java b/examples/src/main/java/com/example/JsonSanitizerValidJsonFuzzer.java new file mode 100644 index 00000000..2d270966 --- /dev/null +++ b/examples/src/main/java/com/example/JsonSanitizerValidJsonFuzzer.java @@ -0,0 +1,42 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.json.JsonSanitizer; + +public class JsonSanitizerValidJsonFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + String validJson; + try { + validJson = JsonSanitizer.sanitize(input, 10); + } catch (Exception e) { + return; + } + + // Check that the output is valid JSON. Invalid JSON may crash other parts of the application + // that trust the output of the sanitizer. + try { + Gson gson = new Gson(); + gson.fromJson(validJson, JsonElement.class); + } catch (Exception e) { + throw new FuzzerSecurityIssueLow("Output is invalid JSON", e); + } + } +} diff --git a/examples/src/main/java/com/example/KlaxonFuzzer.kt b/examples/src/main/java/com/example/KlaxonFuzzer.kt new file mode 100644 index 00000000..39216eea --- /dev/null +++ b/examples/src/main/java/com/example/KlaxonFuzzer.kt @@ -0,0 +1,31 @@ +// 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.example + +import com.beust.klaxon.KlaxonException +import com.beust.klaxon.Parser +import com.code_intelligence.jazzer.api.FuzzedDataProvider + +// Reproduces https://github.com/cbeust/klaxon/pull/330 +object KlaxonFuzzer { + + @JvmStatic + fun fuzzerTestOneInput(data: FuzzedDataProvider) { + try { + Parser.default().parse(StringBuilder(data.consumeRemainingAsString())) + } catch (_: KlaxonException) { + } + } +} diff --git a/examples/src/main/java/com/example/Log4jFuzzer.java b/examples/src/main/java/com/example/Log4jFuzzer.java new file mode 100644 index 00000000..41870c9c --- /dev/null +++ b/examples/src/main/java/com/example/Log4jFuzzer.java @@ -0,0 +1,82 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; +import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.status.StatusLogger; + +// This fuzzer reproduces the log4j RCE vulnerability CVE-2021-44228. +public class Log4jFuzzer { + private final static Logger log = LogManager.getLogger(Log4jFuzzer.class.getName()); + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + log.error(data.consumeRemainingAsString()); + } + + public static void fuzzerInitialize() { + // Install a logger that constructs the log message, but never prints it. + // This noticeably increases the fuzzing performance + DefaultConfigurationBuilder configBuilder = new DefaultConfigurationBuilder(); + configBuilder.setPackages(FuzzingAppender.class.getPackage().getName()); + AppenderComponentBuilder fuzzingAppender = + configBuilder.newAppender("nullAppender", "FuzzingAppender"); + configBuilder.add(fuzzingAppender); + RootLoggerComponentBuilder rootLogger = configBuilder.newRootLogger(); + rootLogger.add(configBuilder.newAppenderRef("nullAppender")); + configBuilder.add(rootLogger); + Configurator.reconfigure(configBuilder.build()); + + // Disable logging of exceptions caught in log4j itself. + StatusLogger.getLogger().reset(); + StatusLogger.getLogger().setLevel(Level.OFF); + } + + @Plugin( + name = "FuzzingAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) + public static class FuzzingAppender extends AbstractAppender { + protected FuzzingAppender(String name) { + super(name, null, PatternLayout.createDefaultLayout(), true); + } + + @PluginFactory + public static FuzzingAppender createAppender(@PluginAttribute("name") String name) { + return new FuzzingAppender(name); + } + + @Override + public void append(LogEvent event) { + try { + getLayout().toByteArray(event); + } catch (Exception ignored) { + // Prevent exceptions from being logged to stderr. + } + } + } +} diff --git a/examples/src/main/java/com/example/TiffImageParserFuzzer.java b/examples/src/main/java/com/example/TiffImageParserFuzzer.java new file mode 100644 index 00000000..5fa1af2d --- /dev/null +++ b/examples/src/main/java/com/example/TiffImageParserFuzzer.java @@ -0,0 +1,31 @@ +// 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.example; + +import java.io.IOException; +import java.util.HashMap; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.common.bytesource.ByteSourceArray; +import org.apache.commons.imaging.formats.tiff.TiffImageParser; + +// Found https://issues.apache.org/jira/browse/IMAGING-276. +public class TiffImageParserFuzzer { + public static void fuzzerTestOneInput(byte[] input) { + try { + new TiffImageParser().getBufferedImage(new ByteSourceArray(input), new HashMap<>()); + } catch (IOException | ImageReadException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/TurboJpegFuzzer.java b/examples/src/main/java/com/example/TurboJpegFuzzer.java new file mode 100644 index 00000000..b9ea715b --- /dev/null +++ b/examples/src/main/java/com/example/TurboJpegFuzzer.java @@ -0,0 +1,59 @@ +// 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.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import org.libjpegturbo.turbojpeg.TJ; +import org.libjpegturbo.turbojpeg.TJDecompressor; +import org.libjpegturbo.turbojpeg.TJException; +import org.libjpegturbo.turbojpeg.TJTransform; +import org.libjpegturbo.turbojpeg.TJTransformer; + +public class TurboJpegFuzzer { + static byte[] buffer = new byte[128 * 128 * 4]; + + public static void fuzzerInitialize() throws TJException { + // Trigger an early load of the native library to show the coverage counters stats in libFuzzer. + new TJDecompressor(); + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + try { + int flagsDecompress = data.consumeInt(); + int flagsTransform = data.consumeInt(); + int pixelFormat = data.consumeInt(TJ.PF_RGB, TJ.PF_CMYK); + // Specify explicit small target width/height so that we can reuse a + // fixed-size buffer. + int desiredWidth = data.consumeInt(1, 128); + int desiredHeight = data.consumeInt(1, 128); + int transformOp = data.consumeInt(TJTransform.OP_NONE, TJTransform.OP_ROT270); + int transformOptions = data.consumeInt(); + int transformWidth = data.consumeBoolean() ? 128 : 64; + int transformHeight = data.consumeBoolean() ? 128 : 64; + TJDecompressor tjd; + if (data.consumeBoolean()) { + TJTransformer tjt = new TJTransformer(data.consumeRemainingAsBytes()); + TJTransform tjf = new TJTransform( + 0, 0, transformWidth, transformHeight, transformOp, transformOptions, null); + tjd = tjt.transform(new TJTransform[] {tjf}, flagsTransform)[0]; + } else { + tjd = new TJDecompressor(data.consumeRemainingAsBytes()); + } + tjd.decompress(buffer, 0, 0, desiredWidth, 0, desiredHeight, pixelFormat, flagsDecompress); + } catch (Exception ignored) { + // We are not looking for Java exceptions, but segfaults and ASan reports. + } + } +} diff --git a/examples/src/main/native/com/example/BUILD.bazel b/examples/src/main/native/com/example/BUILD.bazel new file mode 100644 index 00000000..7f23f75e --- /dev/null +++ b/examples/src/main/native/com/example/BUILD.bazel @@ -0,0 +1,49 @@ +load("@fmeum_rules_jni//jni:defs.bzl", "cc_jni_library") + +cc_jni_library( + name = "native_asan", + srcs = [ + "com_example_ExampleFuzzerWithNative.cpp", + ], + copts = [ + "-fsanitize=fuzzer-no-link,address", + "-fno-sanitize-blacklist", + ], + linkopts = select({ + "//:clang_on_linux": ["-fuse-ld=lld"], + "@platforms//os:windows": [ + # Windows requires all symbols that should be imported from the main + # executable to be defined by an import lib. + "/wholearchive:clang_rt.asan_dll_thunk-x86_64.lib", + ], + "//conditions:default": [], + }), + visibility = ["//examples:__pkg__"], + deps = [ + "//examples:example_fuzzer_with_native_lib.hdrs", + ], +) + +cc_jni_library( + name = "native_ubsan", + srcs = [ + "com_example_ExampleFuzzerWithNative.cpp", + ], + copts = [ + "-fsanitize=fuzzer-no-link,undefined", + "-fno-sanitize-recover=all", + ], + linkopts = select({ + "//:clang_on_linux": ["-fuse-ld=lld"], + "@platforms//os:windows": [ + # Using the asan thunk is correct here as it contains symbols for + # UBSan and SanCov as well. + "/wholearchive:clang_rt.asan_dll_thunk-x86_64.lib", + ], + "//conditions:default": [], + }), + visibility = ["//examples:__pkg__"], + deps = [ + "//examples:example_fuzzer_with_native_lib.hdrs", + ], +) diff --git a/examples/src/main/native/com/example/com_example_ExampleFuzzerWithNative.cpp b/examples/src/main/native/com/example/com_example_ExampleFuzzerWithNative.cpp new file mode 100644 index 00000000..774e5998 --- /dev/null +++ b/examples/src/main/native/com/example/com_example_ExampleFuzzerWithNative.cpp @@ -0,0 +1,42 @@ +// 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_example_ExampleFuzzerWithNative.h" + +#include <limits> +#include <string> + +// 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<int>::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) { + // Crashes with ASan. + [[maybe_unused]] char foo = input[input.size() + 2]; + } + } +} + +JNIEXPORT jboolean JNICALL Java_com_example_ExampleFuzzerWithNative_parse( + JNIEnv *env, jobject o, jstring bytes) { + const char *input(env->GetStringUTFChars(bytes, nullptr)); + parseInternal(input); + env->ReleaseStringUTFChars(bytes, input); + return false; +} |