diff --git a/bazel/tools/java/com/code_intelligence/jazzer/tools/FuzzTargetTestWrapper.java b/bazel/tools/java/com/code_intelligence/jazzer/tools/FuzzTargetTestWrapper.java index 09aa5765d..eee289697 100644 --- a/bazel/tools/java/com/code_intelligence/jazzer/tools/FuzzTargetTestWrapper.java +++ b/bazel/tools/java/com/code_intelligence/jazzer/tools/FuzzTargetTestWrapper.java @@ -51,8 +51,9 @@ public class FuzzTargetTestWrapper { private static final String FRAME_PREFIX = "\tat "; private static final Pattern SANITIZER_FINDING = Pattern.compile("^SUMMARY: \\w*Sanitizer"); private static final String THREAD_DUMP_HEADER = "Stack traces of all JVM threads:"; - private static final Set PUBLIC_JAZZER_PACKAGES = Collections.unmodifiableSet( - Stream.of("api", "replay", "sanitizers").collect(Collectors.toSet())); + private static final Set PUBLIC_JAZZER_PACKAGES = + Collections.unmodifiableSet( + Stream.of("api", "replay", "sanitizers").collect(Collectors.toSet())); public static void main(String[] args) { Runfiles runfiles; @@ -80,10 +81,11 @@ public static void main(String[] args) { allowedFindings = Arrays.stream(args[8].split(",")).filter(s -> !s.isEmpty()).collect(Collectors.toSet()); // Map all files/dirs to real location - arguments = Arrays.stream(args) - .skip(9) - .map(arg -> arg.startsWith("-") ? arg : runfiles.rlocation(arg)) - .collect(toList()); + arguments = + Arrays.stream(args) + .skip(9) + .map(arg -> arg.startsWith("-") ? arg : runfiles.rlocation(arg)) + .collect(toList()); } catch (IOException | ArrayIndexOutOfBoundsException e) { e.printStackTrace(); System.exit(1); @@ -108,19 +110,28 @@ public static void main(String[] args) { if (hookJarActualPath != null) { command.add(String.format("--main_advice_classpath=%s", hookJarActualPath)); } - command.add("--jvm_flags=" - + String.join(" ", "-XX:-OmitStackTraceInFastThrow", "-XX:+UseParallelGC", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+CriticalJNINatives", - "-XX:+EnableDynamicAgentLoading")); + command.add( + "--jvm_flags=" + + String.join( + " ", + "-XX:-OmitStackTraceInFastThrow", + "-XX:+UseParallelGC", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+CriticalJNINatives", + "-XX:+EnableDynamicAgentLoading")); if (System.getenv("JAZZER_DEBUG") != null) { command.add("--debug"); } } else { - command.add(String.format("--cp=%s", - hookJarActualPath == null - ? targetJarActualPath - : String.join(System.getProperty("path.separator"), targetJarActualPath.toString(), - hookJarActualPath.toString()))); + command.add( + String.format( + "--cp=%s", + hookJarActualPath == null + ? targetJarActualPath + : String.join( + System.getProperty("path.separator"), + targetJarActualPath.toString(), + hookJarActualPath.toString()))); } command.add(String.format("-artifact_prefix=%s/", outputDir)); command.add(String.format("--reproducer_path=%s", outputDir)); @@ -130,8 +141,9 @@ public static void main(String[] args) { command.addAll(arguments); // Make JVM error reports available in test outputs. - processBuilder.environment().put( - "JAVA_TOOL_OPTIONS", String.format("-XX:ErrorFile=%s/hs_err_pid%%p.log", outputDir)); + processBuilder + .environment() + .put("JAVA_TOOL_OPTIONS", String.format("-XX:ErrorFile=%s/hs_err_pid%%p.log", outputDir)); processBuilder.redirectOutput(Redirect.INHERIT); processBuilder.redirectInput(Redirect.INHERIT); processBuilder.command(command); @@ -155,7 +167,8 @@ public static void main(String[] args) { } // Assert that we either found a crash in Java (exit code 77), a sanitizer crash (exit code // 76), or a timeout (exit code 70). - if (exitCode != 76 && exitCode != 77 + if (exitCode != 76 + && exitCode != 77 && !(allowedFindings.contains("timeout") && exitCode == 70)) { System.err.printf("Did expect a crash, but Jazzer exited with exit code %d%n", exitCode); System.exit(1); @@ -163,18 +176,18 @@ public static void main(String[] args) { List outputFiles = Files.list(outputDir).collect(toList()); // Verify that libFuzzer dumped a crashing input. if (shouldVerifyCrashInput - && outputFiles.stream().noneMatch( - name -> name.getFileName().toString().startsWith("crash-")) + && outputFiles.stream() + .noneMatch(name -> name.getFileName().toString().startsWith("crash-")) && !(allowedFindings.contains("timeout") - && outputFiles.stream().anyMatch( - name -> name.getFileName().toString().startsWith("timeout-")))) { + && outputFiles.stream() + .anyMatch(name -> name.getFileName().toString().startsWith("timeout-")))) { System.err.printf("No crashing input found in %s%n", outputDir); System.exit(1); } // Verify that libFuzzer dumped a crash reproducer. if (shouldVerifyCrashReproducer - && outputFiles.stream().noneMatch( - name -> name.getFileName().toString().startsWith("Crash_"))) { + && outputFiles.stream() + .noneMatch(name -> name.getFileName().toString().startsWith("Crash_"))) { System.err.printf("No crash reproducer found in %s%n", outputDir); System.exit(1); } @@ -199,19 +212,25 @@ private static void verifyFuzzerOutput( List stackTrace; try (BufferedReader reader = new BufferedReader(new InputStreamReader(fuzzerOutput))) { stackTrace = - reader.lines() + reader + .lines() .peek(System.err::println) - .filter(line - -> line.startsWith(EXCEPTION_PREFIX) || line.startsWith(FRAME_PREFIX) - || line.equals(THREAD_DUMP_HEADER) || SANITIZER_FINDING.matcher(line).find()) + .filter( + line -> + line.startsWith(EXCEPTION_PREFIX) + || line.startsWith(FRAME_PREFIX) + || line.equals(THREAD_DUMP_HEADER) + || SANITIZER_FINDING.matcher(line).find()) .collect(toList()); } if (expectedFindings.isEmpty()) { if (stackTrace.isEmpty()) { return; } - throw new IllegalStateException(String.format( - "Did not expect a finding, but got a stack trace:%n%s", String.join("\n", stackTrace))); + throw new IllegalStateException( + String.format( + "Did not expect a finding, but got a stack trace:%n%s", + String.join("\n", stackTrace))); } if (expectedFindings.contains("native")) { // Expect a native sanitizer finding as well as a thread dump with at least one frame. @@ -247,8 +266,10 @@ private static void verifyFuzzerOutput( } for (String finding : findings) { if (!expectedFindings.contains(finding)) { - throw new IllegalStateException(String.format("Got finding %s, but expected one of: %s", - findings.get(0), String.join(", ", expectedFindings))); + throw new IllegalStateException( + String.format( + "Got finding %s, but expected one of: %s", + findings.get(0), String.join(", ", expectedFindings))); } } List unexpectedFrames = @@ -257,14 +278,17 @@ private static void verifyFuzzerOutput( .map(line -> line.substring(FRAME_PREFIX.length())) .filter(line -> line.startsWith("com.code_intelligence.jazzer.")) // With --nohooks, Jazzer does not filter out its own stack frames. - .filter(line - -> !noHooks - && !PUBLIC_JAZZER_PACKAGES.contains( - line.substring("com.code_intelligence.jazzer.".length()).split("\\.")[0])) + .filter( + line -> + !noHooks + && !PUBLIC_JAZZER_PACKAGES.contains( + line.substring("com.code_intelligence.jazzer.".length()) + .split("\\.")[0])) .collect(toList()); if (!unexpectedFrames.isEmpty()) { throw new IllegalStateException( - String.format("Unexpected strack trace frames:%n%n%s%n%nin:%n%s", + String.format( + "Unexpected strack trace frames:%n%n%s%n%nin:%n%s", String.join("\n", unexpectedFrames), String.join("\n", stackTrace))); } } @@ -288,8 +312,9 @@ private static String compile(File source, Path api, Path targetJar) throws IOEx JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) { Iterable compilationUnits = fileManager.getJavaFileObjects(source); - List options = Arrays.asList( - "-classpath", String.join(File.pathSeparator, api.toString(), targetJar.toString())); + List options = + Arrays.asList( + "-classpath", String.join(File.pathSeparator, api.toString(), targetJar.toString())); System.out.printf( "Compile crash reproducer %s with options %s%n", source.getAbsolutePath(), options); CompilationTask task = @@ -301,24 +326,27 @@ private static String compile(File source, Path api, Path targetJar) throws IOEx } } - private static void execute(String className, Path outputDir, Path api, Path targetJar, - Set expectedFindings) throws IOException, ReflectiveOperationException { + private static void execute( + String className, Path outputDir, Path api, Path targetJar, Set expectedFindings) + throws IOException, ReflectiveOperationException { try { System.out.printf("Execute crash reproducer %s%n", className); - URLClassLoader classLoader = new URLClassLoader( - new URL[] { - outputDir.toUri().toURL(), - api.toUri().toURL(), - targetJar.toUri().toURL(), - }, - getPlatformClassLoader()); + URLClassLoader classLoader = + new URLClassLoader( + new URL[] { + outputDir.toUri().toURL(), api.toUri().toURL(), targetJar.toUri().toURL(), + }, + getPlatformClassLoader()); Class crashReproducerClass = classLoader.loadClass(className); Method main = crashReproducerClass.getMethod("main", String[].class); System.setProperty("jazzer.is_reproducer", "true"); main.invoke(null, new Object[] {new String[] {}}); if (!expectedFindings.isEmpty()) { - throw new IllegalStateException("Expected crash with any of " - + String.join(", ", expectedFindings) + " not reproduced by " + className); + throw new IllegalStateException( + "Expected crash with any of " + + String.join(", ", expectedFindings) + + " not reproduced by " + + className); } System.out.println("Reproducer finished successfully without finding"); } catch (InvocationTargetException e) { diff --git a/bazel/tools/java/com/code_intelligence/jazzer/tools/JarStripper.java b/bazel/tools/java/com/code_intelligence/jazzer/tools/JarStripper.java index 72f53cd76..88469d5b4 100644 --- a/bazel/tools/java/com/code_intelligence/jazzer/tools/JarStripper.java +++ b/bazel/tools/java/com/code_intelligence/jazzer/tools/JarStripper.java @@ -42,6 +42,7 @@ public class JarStripper { private static final Map ZIP_FS_PROPERTIES = new HashMap<>(); + static { // We copy the input to the output path before modifying, so don't try to create a new file at // that path if something went wrong. @@ -58,17 +59,19 @@ public static void main(String[] args) { Path inFile = Paths.get(args[0]); Path outFile = Paths.get(args[1]); - Map> rawPaths = unmodifiableMap( - Arrays.stream(args) - .skip(2) - .map(arg -> { - if (arg.startsWith("+")) { - return new SimpleEntry<>(true, arg.substring(1)); - } else { - return new SimpleEntry<>(false, arg); - } - }) - .collect(partitioningBy(e -> e.getKey(), mapping(e -> e.getValue(), toList())))); + Map> rawPaths = + unmodifiableMap( + Arrays.stream(args) + .skip(2) + .map( + arg -> { + if (arg.startsWith("+")) { + return new SimpleEntry<>(true, arg.substring(1)); + } else { + return new SimpleEntry<>(false, arg); + } + }) + .collect(partitioningBy(e -> e.getKey(), mapping(e -> e.getValue(), toList())))); try { Files.copy(inFile, outFile); @@ -98,16 +101,18 @@ public static void main(String[] args) { PathMatcher pathsToKeep = toPathMatcher(zipFs, rawPaths.get(true), true); try (Stream walk = Files.walk(zipFs.getPath(""))) { walk.sorted(Comparator.reverseOrder()) - .filter(path - -> (pathsToKeep != null && !pathsToKeep.matches(path)) - || (pathsToDelete != null && pathsToDelete.matches(path))) - .forEach(path -> { - try { - Files.delete(path); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); + .filter( + path -> + (pathsToKeep != null && !pathsToKeep.matches(path)) + || (pathsToDelete != null && pathsToDelete.matches(path))) + .forEach( + path -> { + try { + Files.delete(path); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); } } catch (Throwable e) { Throwable throwable = e; @@ -123,10 +128,12 @@ private static PathMatcher toPathMatcher(FileSystem fs, List paths, bool if (paths.isEmpty()) { return null; } - return fs.getPathMatcher(String.format("glob:{%s}", - paths.stream() - .flatMap(pattern -> keep ? toKeepGlobs(pattern) : toRemoveGlobs(pattern)) - .collect(joining(",")))); + return fs.getPathMatcher( + String.format( + "glob:{%s}", + paths.stream() + .flatMap(pattern -> keep ? toKeepGlobs(pattern) : toRemoveGlobs(pattern)) + .collect(joining(",")))); } private static Stream toRemoveGlobs(String path) { @@ -141,7 +148,8 @@ private static Stream toRemoveGlobs(String path) { private static Stream toKeepGlobs(String path) { // When keeping something, also keep all parents. String[] segments = path.split("/"); - return Stream.concat(Stream.of(path), + return Stream.concat( + Stream.of(path), IntStream.range(0, segments.length) .mapToObj(i -> Arrays.stream(segments).limit(i).collect(joining("/")))); } diff --git a/examples/junit-spring-web/src/test/java/com/example/JunitSpringWebApplicationTests.java b/examples/junit-spring-web/src/test/java/com/example/JunitSpringWebApplicationTests.java index 2cf356f2f..0b00a41d0 100644 --- a/examples/junit-spring-web/src/test/java/com/example/JunitSpringWebApplicationTests.java +++ b/examples/junit-spring-web/src/test/java/com/example/JunitSpringWebApplicationTests.java @@ -24,7 +24,6 @@ import com.code_intelligence.jazzer.api.FuzzedDataProvider; import com.code_intelligence.jazzer.junit.FuzzTest; import com.example.JunitSpringWebApplication.HelloRequest; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assumptions; @@ -79,7 +78,8 @@ public void fuzzTestShouldFail(FuzzedDataProvider data) throws Exception { } String name = data.consumeRemainingAsString(); - mockMvc.perform(get("/buggy-hello").param("name", name)) + mockMvc + .perform(get("/buggy-hello").param("name", name)) .andExpect(content().string(containsString(name))); } @@ -92,9 +92,10 @@ public void fuzzTestWithDtoShouldFail(HelloRequest helloRequest) throws Exceptio helloRequest != null && helloRequest.name != null && !helloRequest.name.isBlank()); mockMvc - .perform(post("/hello") - .contentType(MediaType.APPLICATION_JSON) - .content(mapper.writeValueAsString(helloRequest))) + .perform( + post("/hello") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(helloRequest))) .andExpect(content().string(containsString(helloRequest.name))); } } diff --git a/examples/junit/src/test/java/com/example/JavaSeedFuzzTest.java b/examples/junit/src/test/java/com/example/JavaSeedFuzzTest.java index 4f63e9a17..552dbcadf 100644 --- a/examples/junit/src/test/java/com/example/JavaSeedFuzzTest.java +++ b/examples/junit/src/test/java/com/example/JavaSeedFuzzTest.java @@ -38,7 +38,8 @@ class JavaSeedFuzzTest { Base64.getDecoder().decode("q0vPdz5oeJIW3k2U4VJ+aWDufzzZbKAcevc9cNoUTSM="); static Stream fuzzTheFlag() { - return Stream.of(arguments(asList("red", "herring"), 0), + return Stream.of( + arguments(asList("red", "herring"), 0), // This argument passes the hash check, but does not trigger the finding right away. This // is meant to verify that the seed ends up in the corpus, serving as the base for future // mutations rather than just being executed once. @@ -49,8 +50,9 @@ static Stream fuzzTheFlag() { @FuzzTest void fuzzTheFlag(@NotNull List<@NotNull String> flagParts, int secret) throws NoSuchAlgorithmException { - byte[] hash = MessageDigest.getInstance("SHA-256").digest( - String.join("", flagParts).getBytes(StandardCharsets.UTF_8)); + byte[] hash = + MessageDigest.getInstance("SHA-256") + .digest(String.join("", flagParts).getBytes(StandardCharsets.UTF_8)); if (MessageDigest.isEqual(hash, FLAG_SHA256) && secret == 1337) { throw new Error("Fl4g 4nd s3cr3et f0und!"); } diff --git a/examples/junit/src/test/java/com/example/LifecycleFuzzTest.java b/examples/junit/src/test/java/com/example/LifecycleFuzzTest.java index 6afe78f37..8e3ffb961 100644 --- a/examples/junit/src/test/java/com/example/LifecycleFuzzTest.java +++ b/examples/junit/src/test/java/com/example/LifecycleFuzzTest.java @@ -109,10 +109,24 @@ static void afterAll() throws TestSuccessfulException { boolean isFuzzingFromCommandLine = System.getenv("JAZZER_FUZZ") == null; boolean isFuzzingFromJUnit = !isFuzzingFromCommandLine && !isRegressionTest; - final List expectedBeforeEachEvents = unmodifiableList(asList("beforeEachCallback1", - "beforeEachCallback2", "beforeEachCallback3", "beforeEach1", "beforeEach2", "beforeEach3")); - final List expectedAfterEachEvents = unmodifiableList(asList("afterEach1", "afterEach2", - "afterEach3", "afterEachCallback3", "afterEachCallback2", "afterEachCallback1")); + final List expectedBeforeEachEvents = + unmodifiableList( + asList( + "beforeEachCallback1", + "beforeEachCallback2", + "beforeEachCallback3", + "beforeEach1", + "beforeEach2", + "beforeEach3")); + final List expectedAfterEachEvents = + unmodifiableList( + asList( + "afterEach1", + "afterEach2", + "afterEach3", + "afterEachCallback3", + "afterEachCallback2", + "afterEachCallback1")); ArrayList expectedEvents = new ArrayList<>(); expectedEvents.add("beforeAll"); diff --git a/examples/junit/src/test/java/com/example/TestSuccessfulException.java b/examples/junit/src/test/java/com/example/TestSuccessfulException.java index 04608c181..1519af284 100644 --- a/examples/junit/src/test/java/com/example/TestSuccessfulException.java +++ b/examples/junit/src/test/java/com/example/TestSuccessfulException.java @@ -21,6 +21,7 @@ * *

Use this instead of a generic exception to ensure that tests do not pass if such a generic * exception is thrown unexpectedly. + * *

Use this instead of {@link com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow} and other * Jazzer-specific exceptions as using them in tests leads to classloader issues: The exception * classes may be loaded both in the bootstrap and the system classloader depending on when exactly diff --git a/examples/src/main/java/com/example/BatikTranscoderFuzzer.java b/examples/src/main/java/com/example/BatikTranscoderFuzzer.java index cdd221631..2e96afc96 100644 --- a/examples/src/main/java/com/example/BatikTranscoderFuzzer.java +++ b/examples/src/main/java/com/example/BatikTranscoderFuzzer.java @@ -26,9 +26,12 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) throws IOExceptio String host = data.consumeRemainingAsString(); byte[] svg = - ("\n" - + "\n" - + "") + ("\n" + + "\n" + + "") .getBytes(); // Convert SVG to JPEG diff --git a/examples/src/main/java/com/example/CommonsTextFuzzer.java b/examples/src/main/java/com/example/CommonsTextFuzzer.java index 32b309d5c..40807461e 100644 --- a/examples/src/main/java/com/example/CommonsTextFuzzer.java +++ b/examples/src/main/java/com/example/CommonsTextFuzzer.java @@ -21,8 +21,8 @@ public class CommonsTextFuzzer { public static void fuzzerTestOneInput(FuzzedDataProvider data) { try { StringSubstitutor.createInterpolator().replace(data.consumeAsciiString(20)); - } catch ( - java.lang.IllegalArgumentException | java.lang.ArrayIndexOutOfBoundsException ignored) { + } catch (java.lang.IllegalArgumentException + | java.lang.ArrayIndexOutOfBoundsException ignored) { } } } diff --git a/examples/src/main/java/com/example/ExampleFuzzer.java b/examples/src/main/java/com/example/ExampleFuzzer.java index 073d924ac..6cad59554 100644 --- a/examples/src/main/java/com/example/ExampleFuzzer.java +++ b/examples/src/main/java/com/example/ExampleFuzzer.java @@ -28,7 +28,8 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) { // 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 + if (input.startsWith("magicstring" + random) + && input.length() > 30 && input.charAt(25) == 'C') { mustNeverBeCalled(); } diff --git a/examples/src/main/java/com/example/ExampleFuzzerHooks.java b/examples/src/main/java/com/example/ExampleFuzzerHooks.java index 41f16635d..15ab932aa 100644 --- a/examples/src/main/java/com/example/ExampleFuzzerHooks.java +++ b/examples/src/main/java/com/example/ExampleFuzzerHooks.java @@ -19,12 +19,15 @@ 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) { + @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/ + // 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 index 90639deb0..a9fbe29e7 100644 --- a/examples/src/main/java/com/example/ExampleFuzzerWithNative.java +++ b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java @@ -33,4 +33,4 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) { } private native boolean parse(String bytes); -} \ No newline at end of file +} diff --git a/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java b/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java index f53ccb3c7..da338d676 100644 --- a/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java +++ b/examples/src/main/java/com/example/ExamplePathTraversalFuzzer.java @@ -19,9 +19,7 @@ import java.io.IOException; public class ExamplePathTraversalFuzzer { - /** - * The root path for all files that this application is allowed to upload. - */ + /** 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) { diff --git a/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java b/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java index 109db94e5..0d43cc5bc 100644 --- a/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java +++ b/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java @@ -26,10 +26,13 @@ public class ExamplePathTraversalFuzzerHooks { private static final String publicFilesRootPath = "/app/upload/"; - @MethodHook(type = HookType.BEFORE, targetClassName = "java.io.File", targetMethod = "", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.io.File", + targetMethod = "", targetMethodDescriptor = "(Ljava/lang/String;)V") - public static void - fileConstructorHook(MethodHandle handle, Object thisObject, Object[] args, int hookId) { + public static void fileConstructorHook( + MethodHandle handle, Object thisObject, Object[] args, int hookId) { String path = (String) args[0]; Path normalizedPath; try { @@ -41,8 +44,9 @@ public class ExamplePathTraversalFuzzerHooks { if (!normalizedPath.startsWith(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 + "'")); + Jazzer.reportFindingFromHook( + new FuzzerSecurityIssueHigh( + "Path traversal discovered: '" + path + "' --> '" + normalizedPath + "'")); } } } diff --git a/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java b/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java index b68ef6f7c..2fa18b2e6 100644 --- a/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java +++ b/examples/src/main/java/com/example/ExampleValueProfileFuzzer.java @@ -34,8 +34,7 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) { // expected input byte by byte, which takes linear rather than exponential time. if (((Object) base64(data.consumeBytes(6))).equals("SmF6emVy")) { long[] plaintextBlocks = data.consumeLongs(2); - if (plaintextBlocks.length != 2) - return; + if (plaintextBlocks.length != 2) return; if (insecureEncrypt(plaintextBlocks[0]) == 0x9fc48ee64d3dc090L) { // Without variants of the fuzzer hooks for compares that also take in fake PCs, the fuzzer // would get stuck here as the value profile information for long comparisons would not be diff --git a/examples/src/main/java/com/example/Log4jFuzzer.java b/examples/src/main/java/com/example/Log4jFuzzer.java index 41870c9c7..eb53b4590 100644 --- a/examples/src/main/java/com/example/Log4jFuzzer.java +++ b/examples/src/main/java/com/example/Log4jFuzzer.java @@ -34,7 +34,7 @@ // This fuzzer reproduces the log4j RCE vulnerability CVE-2021-44228. public class Log4jFuzzer { - private final static Logger log = LogManager.getLogger(Log4jFuzzer.class.getName()); + private static final Logger log = LogManager.getLogger(Log4jFuzzer.class.getName()); public static void fuzzerTestOneInput(FuzzedDataProvider data) { log.error(data.consumeRemainingAsString()); @@ -59,7 +59,9 @@ public static void fuzzerInitialize() { } @Plugin( - name = "FuzzingAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) + 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); diff --git a/examples/src/main/java/com/example/MazeFuzzer.java b/examples/src/main/java/com/example/MazeFuzzer.java index beab610e8..fb225f0c0 100644 --- a/examples/src/main/java/com/example/MazeFuzzer.java +++ b/examples/src/main/java/com/example/MazeFuzzer.java @@ -26,48 +26,52 @@ // "IJON: Exploring Deep State Spaces via Fuzzing", available at: // https://wcventure.github.io/FuzzingPaper/Paper/SP20_IJON.pdf public final class MazeFuzzer { - private static final String[] MAZE_STRING = new String[] { - " ███████████████████", - " █ █ █ █ █ █", - "█ █ █ █ ███ █ █ █ ███", - "█ █ █ █ █ █", - "█ █████ ███ ███ █ ███", - "█ █ █ █ █ █", - "█ ███ ███████ █ ███ █", - "█ █ █ █ █ █", - "███████ █ █ █████ ███", - "█ █ █ █ █", - "█ ███████ █ ███ ███ █", - "█ █ █ █ █ █ █", - "███ ███ █ ███ █ ███ █", - "█ █ █ █ █ █", - "█ ███████ █ █ █ █ █ █", - "█ █ █ █ █ █ █", - "█ █ █████████ ███ ███", - "█ █ █ █ █ █ █", - "█ █ █ ███ █████ ███ █", - "█ █ █ ", - "███████████████████ #", - }; + private static final String[] MAZE_STRING = + new String[] { + " ███████████████████", + " █ █ █ █ █ █", + "█ █ █ █ ███ █ █ █ ███", + "█ █ █ █ █ █", + "█ █████ ███ ███ █ ███", + "█ █ █ █ █ █", + "█ ███ ███████ █ ███ █", + "█ █ █ █ █ █", + "███████ █ █ █████ ███", + "█ █ █ █ █", + "█ ███████ █ ███ ███ █", + "█ █ █ █ █ █ █", + "███ ███ █ ███ █ ███ █", + "█ █ █ █ █ █", + "█ ███████ █ █ █ █ █ █", + "█ █ █ █ █ █ █", + "█ █ █████████ ███ ███", + "█ █ █ █ █ █ █", + "█ █ █ ███ █████ ███ █", + "█ █ █ ", + "███████████████████ #", + }; private static final char[][] MAZE = parseMaze(); private static final char[][] REACHED_FIELDS = parseMaze(); public static void fuzzerTestOneInput(byte[] commands) { - executeCommands(commands, (x, y, won) -> { - if (won) { - throw new TreasureFoundException(commands); - } - // This is the key line that makes this fuzz target work: It instructs the fuzzer to track - // every new combination of x and y as a new feature. Without it, the fuzzer would be - // completely lost in the maze as guessing an escaping path by chance is close to impossible. - Jazzer.exploreState((byte) Objects.hash(x, y), 0); - if (REACHED_FIELDS[y][x] == ' ') { - // Fuzzer reached a new field in the maze, print its progress. - REACHED_FIELDS[y][x] = '.'; - System.out.println(renderMaze(REACHED_FIELDS)); - } - }); + executeCommands( + commands, + (x, y, won) -> { + if (won) { + throw new TreasureFoundException(commands); + } + // This is the key line that makes this fuzz target work: It instructs the fuzzer to track + // every new combination of x and y as a new feature. Without it, the fuzzer would be + // completely lost in the maze as guessing an escaping path by chance is close to + // impossible. + Jazzer.exploreState((byte) Objects.hash(x, y), 0); + if (REACHED_FIELDS[y][x] == ' ') { + // Fuzzer reached a new field in the maze, print its progress. + REACHED_FIELDS[y][x] = '.'; + System.out.println(renderMaze(REACHED_FIELDS)); + } + }); } private static class TreasureFoundException extends RuntimeException { @@ -119,7 +123,7 @@ private static void executeCommands(byte[] commands, Consumer3 { - if (!won) { - mutableMaze[y][x] = '.'; - } - }); + executeCommands( + commands, + (x, y, won) -> { + if (!won) { + mutableMaze[y][x] = '.'; + } + }); return renderMaze(mutableMaze); } } diff --git a/examples/src/main/java/com/example/TurboJpegFuzzer.java b/examples/src/main/java/com/example/TurboJpegFuzzer.java index b9ea715b1..e10af0105 100644 --- a/examples/src/main/java/com/example/TurboJpegFuzzer.java +++ b/examples/src/main/java/com/example/TurboJpegFuzzer.java @@ -45,8 +45,9 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) { TJDecompressor tjd; if (data.consumeBoolean()) { TJTransformer tjt = new TJTransformer(data.consumeRemainingAsBytes()); - TJTransform tjf = new TJTransform( - 0, 0, transformWidth, transformHeight, transformOp, transformOptions, null); + 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()); diff --git a/launcher/testdata/test/ModifiedUtf8Encoder.java b/launcher/testdata/test/ModifiedUtf8Encoder.java index b460c81c6..3195a7dd9 100644 --- a/launcher/testdata/test/ModifiedUtf8Encoder.java +++ b/launcher/testdata/test/ModifiedUtf8Encoder.java @@ -19,7 +19,7 @@ final class ModifiedUtf8Encoder { // Encodes a string in the JVM's modified UTF-8 encoding. - static public byte[] encode(String value) { + public static byte[] encode(String value) { // Modified UTF-8 is almost the same as CESU-8, the only difference being that the zero // character is coded on two bytes. byte[] cesuBytes = value.getBytes(Charset.forName("CESU-8")); diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ClojureLangHooks.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ClojureLangHooks.java index 262568c7e..07c3ea9cc 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ClojureLangHooks.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ClojureLangHooks.java @@ -25,7 +25,7 @@ import java.util.WeakHashMap; @SuppressWarnings("unused") -final public class ClojureLangHooks { +public final class ClojureLangHooks { /** * Used to memoize the all clojure function objects that test for substring. These function * objects extend AFunction and do not overwrite equals(), which allows us to use a WeakHashMap. @@ -34,18 +34,23 @@ final public class ClojureLangHooks { ThreadLocal.withInitial(() -> Collections.newSetFromMap(new WeakHashMap())); static final Set stringContainsFuncNames = - new HashSet(Arrays.asList("clojure.string$includes_QMARK_", - "clojure.string$starts_with_QMARK_", "clojure.string$ends_with_QMARK_", - "clojure.string$index_of_QMARK_", "clojure.string$last_index_of_QMARK_")); + new HashSet( + Arrays.asList( + "clojure.string$includes_QMARK_", + "clojure.string$starts_with_QMARK_", + "clojure.string$ends_with_QMARK_", + "clojure.string$index_of_QMARK_", + "clojure.string$last_index_of_QMARK_")); /** * This hook checks the type of the returned clojure.lang.IFn objects and puts them into * stringContainsFuncs if they match know string-contains functions. */ @MethodHook( - type = HookType.AFTER, targetClassName = "clojure.lang.Var", targetMethod = "getRawRoot") - public static void - clojureMarkContains( + type = HookType.AFTER, + targetClassName = "clojure.lang.Var", + targetMethod = "getRawRoot") + public static void clojureMarkContains( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object result) { if (stringContainsFuncNames.contains(result.getClass().getCanonicalName())) { stringContainsFuncs.get().add(result); diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/RegexRoadblocks.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/RegexRoadblocks.java index 76c499b06..7026684ac 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/RegexRoadblocks.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/RegexRoadblocks.java @@ -32,8 +32,8 @@ /** * The hooks in this class extend the reach of Jazzer's string compare instrumentation to literals * (both strings and characters) that are part of regular expression patterns. - *

- * Internally, the Java standard library represents a compiled regular expression as a graph of + * + *

Internally, the Java standard library represents a compiled regular expression as a graph of * instances of Pattern$Node instances, each of which represents a single unit of the full * expression and provides a `match` function that takes a {@link Matcher}, a {@link CharSequence} * to match against and an index into the sequence. With a hook on this method for every subclass of @@ -51,8 +51,9 @@ public final class RegexRoadblocks { offset(UNSAFE, field(SLICE_NODE, "buffer", int[].class)); private static final Class CHAR_PREDICATE = nestedClass(Pattern.class, "CharPredicate"); private static final Class CHAR_PROPERTY = nestedClass(Pattern.class, "CharProperty"); - private static final long CHAR_PROPERTY_PREDICATE_OFFSET = offset( - UNSAFE, field(CHAR_PROPERTY, "predicate", nestedClass(Pattern.class, "CharPredicate"))); + private static final long CHAR_PROPERTY_PREDICATE_OFFSET = + offset( + UNSAFE, field(CHAR_PROPERTY, "predicate", nestedClass(Pattern.class, "CharPredicate"))); private static final Class BIT_CLASS = nestedClass(Pattern.class, "BitClass"); private static final long BIT_CLASS_BITS_OFFSET = offset(UNSAFE, field(BIT_CLASS, "bits", boolean[].class)); @@ -65,71 +66,68 @@ public final class RegexRoadblocks { private static final ThreadLocal> PREDICATE_SOLUTIONS = ThreadLocal.withInitial(WeakHashMap::new); - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern$Node", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern$Node", targetMethod = "match", targetMethodDescriptor = "(Ljava/util/regex/Matcher;ILjava/lang/CharSequence;)Z", - additionalClassesToHook = - { - "java.util.regex.Matcher", - "java.util.regex.Pattern$BackRef", - "java.util.regex.Pattern$Behind", - "java.util.regex.Pattern$BehindS", - "java.util.regex.Pattern$BmpCharProperty", - "java.util.regex.Pattern$BmpCharPropertyGreedy", - "java.util.regex.Pattern$BnM", - "java.util.regex.Pattern$BnMS", - "java.util.regex.Pattern$Bound", - "java.util.regex.Pattern$Branch", - "java.util.regex.Pattern$BranchConn", - "java.util.regex.Pattern$CharProperty", - "java.util.regex.Pattern$CharPropertyGreedy", - "java.util.regex.Pattern$CIBackRef", - "java.util.regex.Pattern$Caret", - "java.util.regex.Pattern$Curly", - "java.util.regex.Pattern$Conditional", - "java.util.regex.Pattern$First", - "java.util.regex.Pattern$GraphemeBound", - "java.util.regex.Pattern$GroupCurly", - "java.util.regex.Pattern$GroupHead", - "java.util.regex.Pattern$GroupRef", - "java.util.regex.Pattern$LastMatch", - "java.util.regex.Pattern$LazyLoop", - "java.util.regex.Pattern$LineEnding", - "java.util.regex.Pattern$Loop", - "java.util.regex.Pattern$Neg", - "java.util.regex.Pattern$NFCCharProperty", - "java.util.regex.Pattern$NotBehind", - "java.util.regex.Pattern$NotBehindS", - "java.util.regex.Pattern$Pos", - "java.util.regex.Pattern$Ques", - "java.util.regex.Pattern$Slice", - "java.util.regex.Pattern$SliceI", - "java.util.regex.Pattern$SliceIS", - "java.util.regex.Pattern$SliceS", - "java.util.regex.Pattern$SliceU", - "java.util.regex.Pattern$Start", - "java.util.regex.Pattern$StartS", - "java.util.regex.Pattern$UnixCaret", - "java.util.regex.Pattern$UnixDollar", - "java.util.regex.Pattern$XGrapheme", - }) - public static void - nodeMatchHook(MethodHandle method, Object node, Object[] args, int hookId, Boolean matched) { - if (matched || node == null) - return; + additionalClassesToHook = { + "java.util.regex.Matcher", + "java.util.regex.Pattern$BackRef", + "java.util.regex.Pattern$Behind", + "java.util.regex.Pattern$BehindS", + "java.util.regex.Pattern$BmpCharProperty", + "java.util.regex.Pattern$BmpCharPropertyGreedy", + "java.util.regex.Pattern$BnM", + "java.util.regex.Pattern$BnMS", + "java.util.regex.Pattern$Bound", + "java.util.regex.Pattern$Branch", + "java.util.regex.Pattern$BranchConn", + "java.util.regex.Pattern$CharProperty", + "java.util.regex.Pattern$CharPropertyGreedy", + "java.util.regex.Pattern$CIBackRef", + "java.util.regex.Pattern$Caret", + "java.util.regex.Pattern$Curly", + "java.util.regex.Pattern$Conditional", + "java.util.regex.Pattern$First", + "java.util.regex.Pattern$GraphemeBound", + "java.util.regex.Pattern$GroupCurly", + "java.util.regex.Pattern$GroupHead", + "java.util.regex.Pattern$GroupRef", + "java.util.regex.Pattern$LastMatch", + "java.util.regex.Pattern$LazyLoop", + "java.util.regex.Pattern$LineEnding", + "java.util.regex.Pattern$Loop", + "java.util.regex.Pattern$Neg", + "java.util.regex.Pattern$NFCCharProperty", + "java.util.regex.Pattern$NotBehind", + "java.util.regex.Pattern$NotBehindS", + "java.util.regex.Pattern$Pos", + "java.util.regex.Pattern$Ques", + "java.util.regex.Pattern$Slice", + "java.util.regex.Pattern$SliceI", + "java.util.regex.Pattern$SliceIS", + "java.util.regex.Pattern$SliceS", + "java.util.regex.Pattern$SliceU", + "java.util.regex.Pattern$Start", + "java.util.regex.Pattern$StartS", + "java.util.regex.Pattern$UnixCaret", + "java.util.regex.Pattern$UnixDollar", + "java.util.regex.Pattern$XGrapheme", + }) + public static void nodeMatchHook( + MethodHandle method, Object node, Object[] args, int hookId, Boolean matched) { + if (matched || node == null) return; Matcher matcher = (Matcher) args[0]; - if (matcher == null) - return; + if (matcher == null) return; int i = (int) args[1]; CharSequence seq = (CharSequence) args[2]; - if (seq == null) - return; + if (seq == null) return; if (SLICE_NODE != null && SLICE_NODE.isInstance(node)) { // The node encodes a match against a fixed string literal. Extract the literal and report a // comparison between it and the subsequence of seq starting at i. - if (SLICE_NODE_BUFFER_OFFSET == INVALID_OFFSET) - return; + if (SLICE_NODE_BUFFER_OFFSET == INVALID_OFFSET) return; int currentLength = limitedLength(matcher.regionEnd() - i); String current = seq.subSequence(i, i + currentLength).toString(); @@ -161,11 +159,9 @@ public final class RegexRoadblocks { } else { solutionKey = UNSAFE.getObject(node, CHAR_PROPERTY_PREDICATE_OFFSET); } - if (solutionKey == null) - return; + if (solutionKey == null) return; Character solution = predicateSolution(solutionKey); - if (solution == null) - return; + if (solution == null) return; // We report a string comparison rather than an integer comparison for two reasons: // 1. If the characters are four byte codepoints, they will be coded on six bytes (a surrogate // pair) in CESU-8, which is the encoding assumed for the fuzzer input, whereas ASCII @@ -186,77 +182,105 @@ public final class RegexRoadblocks { // This and all following hooks track the relation between a CharPredicate or CharProperty // instance and a character that matches it. We use an after hook on the factory methods so that // we have access to the parameters and the created instance at the same time. - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "Single", targetMethodDescriptor = "(I)Ljava/util/regex/Pattern$BmpCharPredicate;", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "SingleI", targetMethodDescriptor = "(II)Ljava/util/regex/Pattern$CharPredicate;", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "SingleS", targetMethodDescriptor = "(I)Ljava/util/regex/Pattern$CharPredicate;", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "SingleU", targetMethodDescriptor = "(I)Ljava/util/regex/Pattern$CharPredicate;", additionalClassesToHook = {"java.util.regex.Pattern"}) - public static void - singleHook(MethodHandle method, Object node, Object[] args, int hookId, Object predicate) { - if (predicate == null) - return; + public static void singleHook( + MethodHandle method, Object node, Object[] args, int hookId, Object predicate) { + if (predicate == null) return; PREDICATE_SOLUTIONS.get().put(predicate, (char) (int) args[0]); } // Java 8 uses classes extending CharProperty instead of lambdas implementing CharPredicate to // match single characters, so also hook those. - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern$Single", - targetMethod = "", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern$SingleI", - targetMethod = "", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern$SingleS", - targetMethod = "", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern$SingleU", - targetMethod = "", additionalClassesToHook = {"java.util.regex.Pattern"}) - public static void - java8SingleHook( + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern$Single", + targetMethod = "", + additionalClassesToHook = {"java.util.regex.Pattern"}) + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern$SingleI", + targetMethod = "", + additionalClassesToHook = {"java.util.regex.Pattern"}) + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern$SingleS", + targetMethod = "", + additionalClassesToHook = {"java.util.regex.Pattern"}) + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern$SingleU", + targetMethod = "", + additionalClassesToHook = {"java.util.regex.Pattern"}) + public static void java8SingleHook( MethodHandle method, Object property, Object[] args, int hookId, Object alwaysNull) { - if (property == null) - return; + if (property == null) return; PREDICATE_SOLUTIONS.get().put(property, (char) (int) args[0]); } - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "Range", targetMethodDescriptor = "(II)Ljava/util/regex/Pattern$CharPredicate;", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "CIRange", targetMethodDescriptor = "(II)Ljava/util/regex/Pattern$CharPredicate;", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "CIRangeU", targetMethodDescriptor = "(II)Ljava/util/regex/Pattern$CharPredicate;", additionalClassesToHook = {"java.util.regex.Pattern"}) // Java 8 uses anonymous classes extending CharProperty instead of lambdas implementing // CharPredicate to match single characters, so also hook those. - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "rangeFor", targetMethodDescriptor = "(II)Ljava/util/regex/Pattern$CharProperty;", additionalClassesToHook = {"java.util.regex.Pattern"}) - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "caseInsensitiveRangeFor", targetMethodDescriptor = "(II)Ljava/util/regex/Pattern$CharProperty;", additionalClassesToHook = {"java.util.regex.Pattern"}) - public static void - rangeHook(MethodHandle method, Object node, Object[] args, int hookId, Object predicate) { - if (predicate == null) - return; + public static void rangeHook( + MethodHandle method, Object node, Object[] args, int hookId, Object predicate) { + if (predicate == null) return; PREDICATE_SOLUTIONS.get().put(predicate, (char) (int) args[0]); } - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern$CharPredicate", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern$CharPredicate", targetMethod = "union", targetMethodDescriptor = "(Ljava/util/regex/Pattern$CharPredicate;)Ljava/util/regex/Pattern$CharPredicate;", @@ -265,38 +289,41 @@ public final class RegexRoadblocks { // CharPredicate to match single characters, so also hook union for those. Even though the classes // of the parameters will be different, the actual implementation of the hook is the same in this // case. - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Pattern", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Pattern", targetMethod = "union", targetMethodDescriptor = "(Ljava/util/regex/Pattern$CharProperty;Ljava/util/regex/Pattern$CharProperty;)Ljava/util/regex/Pattern$CharProperty;", additionalClassesToHook = {"java.util.regex.Pattern"}) - public static void - unionHook( + public static void unionHook( MethodHandle method, Object thisObject, Object[] args, int hookId, Object unionPredicate) { - if (unionPredicate == null) - return; + if (unionPredicate == null) return; Character solution = predicateSolution(thisObject); - if (solution == null) - solution = predicateSolution(args[0]); - if (solution == null) - return; + if (solution == null) solution = predicateSolution(args[0]); + if (solution == null) return; PREDICATE_SOLUTIONS.get().put(unionPredicate, solution); } private static Character predicateSolution(Object charPredicate) { - return PREDICATE_SOLUTIONS.get().computeIfAbsent(charPredicate, unused -> { - if (BIT_CLASS != null && BIT_CLASS.isInstance(charPredicate)) { - // BitClass instances have an empty bits array at construction time, so we scan their - // constants lazily when needed. - boolean[] bits = (boolean[]) UNSAFE.getObject(charPredicate, BIT_CLASS_BITS_OFFSET); - for (int i = 0; i < bits.length; i++) { - if (bits[i]) { - return (char) i; - } - } - } - return null; - }); + return PREDICATE_SOLUTIONS + .get() + .computeIfAbsent( + charPredicate, + unused -> { + if (BIT_CLASS != null && BIT_CLASS.isInstance(charPredicate)) { + // BitClass instances have an empty bits array at construction time, so we scan + // their + // constants lazily when needed. + boolean[] bits = (boolean[]) UNSAFE.getObject(charPredicate, BIT_CLASS_BITS_OFFSET); + for (int i = 0; i < bits.length; i++) { + if (bits[i]) { + return (char) i; + } + } + } + return null; + }); } // Limits a length to the maximum length libFuzzer will read up to in a callback. diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ScriptEngineInjection.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ScriptEngineInjection.java index 6f084bf96..5c5d5d73f 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ScriptEngineInjection.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ScriptEngineInjection.java @@ -26,56 +26,63 @@ /** * Detects Script Engine injections. * - *

- * The hooks in this class attempt to detect user input flowing into - * {@link javax.script.ScriptEngine#eval(String)} and the like that might lead - * to remote code executions depending on the scripting engine's capabilities. - * Before JDK 15, the Nashorn Engine was registered by default with - * ScriptEngineManager under several aliases, including "js". Nashorn allows - * access to JVM classes, for example {@link java.lang.Runtime} allowing the - * execution of arbitrary OS commands. Several other scripting engines can be - * embedded to the JVM (they must follow the - * JSR-223 - * specification). - **/ + *

The hooks in this class attempt to detect user input flowing into {@link + * javax.script.ScriptEngine#eval(String)} and the like that might lead to remote code executions + * depending on the scripting engine's capabilities. Before JDK 15, the Nashorn Engine was + * registered by default with ScriptEngineManager under several aliases, including "js". Nashorn + * allows access to JVM classes, for example {@link java.lang.Runtime} allowing the execution of + * arbitrary OS commands. Several other scripting engines can be embedded to the JVM (they must + * follow the JSR-223 specification). + */ @SuppressWarnings("unused") public final class ScriptEngineInjection { private static final String PAYLOAD = "\"jaz\"+\"zer\""; /** - * String variants of eval can be intercepted by before hooks, as the script - * content can directly be checked for the presence of the payload. + * String variants of eval can be intercepted by before hooks, as the script content can directly + * be checked for the presence of the payload. */ - @MethodHook(type = HookType.BEFORE, targetClassName = "javax.script.ScriptEngine", - targetMethod = "eval", targetMethodDescriptor = "(Ljava/lang/String;)Ljava/lang/Object;") - @MethodHook(type = HookType.BEFORE, targetClassName = "javax.script.ScriptEngine", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "javax.script.ScriptEngine", + targetMethod = "eval", + targetMethodDescriptor = "(Ljava/lang/String;)Ljava/lang/Object;") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "javax.script.ScriptEngine", targetMethod = "eval", targetMethodDescriptor = "(Ljava/lang/String;Ljavax/script/ScriptContext;)Ljava/lang/Object;") - @MethodHook(type = HookType.BEFORE, targetClassName = "javax.script.ScriptEngine", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "javax.script.ScriptEngine", targetMethod = "eval", targetMethodDescriptor = "(Ljava/lang/String;Ljavax/script/Bindings;)Ljava/lang/Object;") - public static void - checkScriptEngineExecuteString( + public static void checkScriptEngineExecuteString( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { checkScriptContent((String) arguments[0], hookId); } /** - * Reader variants of eval must be intercepted by replace hooks, as their - * contents are converted to strings, for the payload check, and back to readers - * for the actual method invocation. + * Reader variants of eval must be intercepted by replace hooks, as their contents are converted + * to strings, for the payload check, and back to readers for the actual method invocation. */ - @MethodHook(type = HookType.REPLACE, targetClassName = "javax.script.ScriptEngine", - targetMethod = "eval", targetMethodDescriptor = "(Ljava/io/Reader;)Ljava/lang/Object;") - @MethodHook(type = HookType.REPLACE, targetClassName = "javax.script.ScriptEngine", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "javax.script.ScriptEngine", + targetMethod = "eval", + targetMethodDescriptor = "(Ljava/io/Reader;)Ljava/lang/Object;") + @MethodHook( + type = HookType.REPLACE, + targetClassName = "javax.script.ScriptEngine", targetMethod = "eval", targetMethodDescriptor = "(Ljava/io/Reader;Ljavax/script/ScriptContext;)Ljava/lang/Object;") - @MethodHook(type = HookType.REPLACE, targetClassName = "javax.script.ScriptEngine", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "javax.script.ScriptEngine", targetMethod = "eval", targetMethodDescriptor = "(Ljava/io/Reader;Ljavax/script/Bindings;)Ljava/lang/Object;") - public static Object - checkScriptEngineExecute(MethodHandle method, Object thisObject, Object[] arguments, int hookId) - throws Throwable { + public static Object checkScriptEngineExecute( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable { if (arguments[0] != null) { String content = readAll((Reader) arguments[0]); checkScriptContent(content, hookId); @@ -87,9 +94,12 @@ public final class ScriptEngineInjection { private static void checkScriptContent(String content, int hookId) { if (content != null) { if (content.contains(PAYLOAD)) { - Jazzer.reportFindingFromHook(new FuzzerSecurityIssueCritical( - "Script Engine Injection: Insecure user input was used in script engine invocation.\n" - + "Depending on the script engine's capabilities this could lead to sandbox escape and remote code execution.")); + Jazzer.reportFindingFromHook( + new FuzzerSecurityIssueCritical( + "Script Engine Injection: Insecure user input was used in script engine" + + " invocation.\n" + + "Depending on the script engine's capabilities this could lead to sandbox" + + " escape and remote code execution.")); } else { Jazzer.guideTowardsContainment(content, PAYLOAD, hookId); } diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ServerSideRequestForgery.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ServerSideRequestForgery.java index 3ff48e3cc..ffcc185d6 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ServerSideRequestForgery.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ServerSideRequestForgery.java @@ -16,7 +16,6 @@ package com.code_intelligence.jazzer.sanitizers; -import com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh; import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; import com.code_intelligence.jazzer.api.HookType; import com.code_intelligence.jazzer.api.Jazzer; @@ -40,25 +39,27 @@ public class ServerSideRequestForgery { * classes are normally ignored, they have to be marked for hooking explicitly. In this case, all * internal classes calling "connect" on {@link java.net.SocketImpl} should be listed below. * Internal classes using {@link java.net.SocketImpl#connect(String, int)}: + * *

    *
  • java.net.Socket (hook required) *
  • java.net.AbstractPlainSocketImpl (no direct usage, no hook required) *
  • java.net.PlainSocketImpl (no direct usage, no hook required) *
  • java.net.HttpConnectSocketImpl (only used in Socket, which is already listed) - *
  • java.net.SocksSocketImpl (used in Socket, but also invoking super.connect directly, - * hook required) + *
  • java.net.SocksSocketImpl (used in Socket, but also invoking super.connect directly, hook + * required) *
  • java.net.ServerSocket (security check, no hook required) *
*/ - @MethodHook(type = HookType.BEFORE, targetClassName = "java.net.SocketImpl", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.net.SocketImpl", targetMethod = "connect", - additionalClassesToHook = - { - "java.net.Socket", - "java.net.SocksSocketImpl", - }) - public static void - checkSsrfSocket(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + additionalClassesToHook = { + "java.net.Socket", + "java.net.SocksSocketImpl", + }) + public static void checkSsrfSocket( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { checkSsrf(arguments); } @@ -70,15 +71,16 @@ public class ServerSideRequestForgery { * sun.nio.ch.SocketChannelImpl}. "connect" is only called in {@link * java.nio.channels.SocketChannel} itself and the two mentioned classes below. */ - @MethodHook(type = HookType.BEFORE, targetClassName = "java.nio.channels.SocketChannel", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.nio.channels.SocketChannel", targetMethod = "connect", - additionalClassesToHook = - { - "sun.nio.ch.SocketAdaptor", - "jdk.internal.net.http.PlainHttpConnection", - }) - public static void - checkSsrfHttpConnection(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + additionalClassesToHook = { + "sun.nio.ch.SocketAdaptor", + "jdk.internal.net.http.PlainHttpConnection", + }) + public static void checkSsrfHttpConnection( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { checkSsrf(arguments); } @@ -112,16 +114,18 @@ private static void checkSsrf(Object[] arguments) { } if (!connectionPermitted.get().test(host, port)) { - Jazzer.reportFindingFromHook(new FuzzerSecurityIssueMedium(String.format( - "Server Side Request Forgery (SSRF)\n" - + "Attempted connection to: %s:%d\n" - + "Requests to destinations based on untrusted data could lead to exfiltration of " - + "sensitive data or exposure of internal services.\n\n" - + "If the fuzz test is expected to perform network connections, call " - + "com.code_intelligence.jazzer.api.BugDetectors#allowNetworkConnections at the " - + "beginning of your fuzz test and optionally provide a predicate matching the " - + "expected hosts.", - host, port))); + Jazzer.reportFindingFromHook( + new FuzzerSecurityIssueMedium( + String.format( + "Server Side Request Forgery (SSRF)\n" + + "Attempted connection to: %s:%d\n" + + "Requests to destinations based on untrusted data could lead to" + + " exfiltration of sensitive data or exposure of internal services.\n\n" + + "If the fuzz test is expected to perform network connections, call" + + " com.code_intelligence.jazzer.api.BugDetectors#allowNetworkConnections at" + + " the beginning of your fuzz test and optionally provide a predicate" + + " matching the expected hosts.", + host, port))); } } } diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/SqlInjection.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/SqlInjection.java index da5beaa8d..7f515b727 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/SqlInjection.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/SqlInjection.java @@ -37,11 +37,12 @@ * otherwise all input was escaped correctly. * *

Two types of methods are hooked: + * *

    - *
  1. Methods that take an SQL query as the first argument (e.g. {@link - * java.sql.Statement#execute}]).
  2. - *
  3. Methods that don't take any arguments and execute an already prepared statement (e.g. {@link - * java.sql.PreparedStatement#execute}).
  4. + *
  5. Methods that take an SQL query as the first argument (e.g. {@link + * java.sql.Statement#execute}]). + *
  6. Methods that don't take any arguments and execute an already prepared statement (e.g. + * {@link java.sql.PreparedStatement#execute}). *
* * For 1. we validate the syntax of the query using SQL_SYNTAX_ERROR_EXCEPTIONS = unmodifiableSet( - Stream - .of("java.sql.SQLException", "java.sql.SQLNonTransientException", - "java.sql.SQLSyntaxErrorException", "org.h2.jdbc.JdbcSQLSyntaxErrorException", - "org.h2.jdbc.JdbcSQLFeatureNotSupportedException") - .collect(toSet())); + private static final Set SQL_SYNTAX_ERROR_EXCEPTIONS = + unmodifiableSet( + Stream.of( + "java.sql.SQLException", + "java.sql.SQLNonTransientException", + "java.sql.SQLSyntaxErrorException", + "org.h2.jdbc.JdbcSQLSyntaxErrorException", + "org.h2.jdbc.JdbcSQLFeatureNotSupportedException") + .collect(toSet())); @MethodHook( - type = HookType.REPLACE, targetClassName = "java.sql.Statement", targetMethod = "execute") - @MethodHook(type = HookType.REPLACE, targetClassName = "java.sql.Statement", + type = HookType.REPLACE, + targetClassName = "java.sql.Statement", + targetMethod = "execute") + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.sql.Statement", targetMethod = "executeBatch") - @MethodHook(type = HookType.REPLACE, targetClassName = "java.sql.Statement", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.sql.Statement", targetMethod = "executeLargeBatch") - @MethodHook(type = HookType.REPLACE, targetClassName = "java.sql.Statement", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.sql.Statement", targetMethod = "executeLargeUpdate") - @MethodHook(type = HookType.REPLACE, targetClassName = "java.sql.Statement", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.sql.Statement", targetMethod = "executeQuery") - @MethodHook(type = HookType.REPLACE, targetClassName = "java.sql.Statement", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.sql.Statement", targetMethod = "executeUpdate") - @MethodHook(type = HookType.REPLACE, targetClassName = "javax.persistence.EntityManager", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "javax.persistence.EntityManager", targetMethod = "createNativeQuery") - public static Object - checkSqlExecute(MethodHandle method, Object thisObject, Object[] arguments, int hookId) - throws Throwable { + public static Object checkSqlExecute( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable { boolean hasValidSqlQuery = false; if (arguments.length > 0 && arguments[0] instanceof String) { @@ -96,8 +113,9 @@ public class SqlInjection { // that we don't want to report. if (!hasValidSqlQuery && SQL_SYNTAX_ERROR_EXCEPTIONS.contains(throwable.getClass().getName())) { - Jazzer.reportFindingFromHook(new FuzzerSecurityIssueHigh( - String.format("SQL Injection%nInjected query: %s%n", arguments[0]))); + Jazzer.reportFindingFromHook( + new FuzzerSecurityIssueHigh( + String.format("SQL Injection%nInjected query: %s%n", arguments[0]))); } throw throwable; } diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/utils/ReflectionUtils.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/utils/ReflectionUtils.java index fd6ac72fa..a5527e7e0 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/utils/ReflectionUtils.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/utils/ReflectionUtils.java @@ -27,8 +27,7 @@ public static Class clazz(String className) { try { return Class.forName(className); } catch (ClassNotFoundException e) { - if (JAZZER_REFLECTION_DEBUG) - e.printStackTrace(); + if (JAZZER_REFLECTION_DEBUG) e.printStackTrace(); return null; } } @@ -38,8 +37,7 @@ public static Class nestedClass(Class parentClass, String nestedClassName) } public static Field field(Class clazz, String name, Class type) { - if (clazz == null) - return null; + if (clazz == null) return null; try { Field field = clazz.getDeclaredField(name); if (!field.getType().equals(type)) { @@ -48,15 +46,13 @@ public static Field field(Class clazz, String name, Class type) { } return field; } catch (NoSuchFieldException e) { - if (JAZZER_REFLECTION_DEBUG) - e.printStackTrace(); + if (JAZZER_REFLECTION_DEBUG) e.printStackTrace(); return null; } } public static long offset(Unsafe unsafe, Field field) { - if (unsafe == null || field == null) - return INVALID_OFFSET; + if (unsafe == null || field == null) return INVALID_OFFSET; return unsafe.objectFieldOffset(field); } } diff --git a/sanitizers/src/test/java/com/example/ClassLoaderLoadClass.java b/sanitizers/src/test/java/com/example/ClassLoaderLoadClass.java index 207f29cd6..711e723a0 100644 --- a/sanitizers/src/test/java/com/example/ClassLoaderLoadClass.java +++ b/sanitizers/src/test/java/com/example/ClassLoaderLoadClass.java @@ -15,7 +15,6 @@ package com.example; import com.code_intelligence.jazzer.api.FuzzedDataProvider; -import java.lang.reflect.InvocationTargetException; public class ClassLoaderLoadClass { public static void fuzzerTestOneInput(FuzzedDataProvider data) throws InterruptedException { diff --git a/sanitizers/src/test/java/com/example/DisabledHooksTest.java b/sanitizers/src/test/java/com/example/DisabledHooksTest.java index 763cd6377..68903d6a5 100644 --- a/sanitizers/src/test/java/com/example/DisabledHooksTest.java +++ b/sanitizers/src/test/java/com/example/DisabledHooksTest.java @@ -84,7 +84,8 @@ public void disableDeserializationSanitizer() { @Test public void disableExpressionLanguageSanitizer() throws Throwable { - System.setProperty("jazzer.disabled_hooks", + System.setProperty( + "jazzer.disabled_hooks", "com.code_intelligence.jazzer.sanitizers.ExpressionLanguageInjection"); triggerExpressionLanguageInjectionSanitizer(); } @@ -99,7 +100,8 @@ public void disableReflectiveCallAndEnableDeserialization() { @Test public void disableAllSanitizers() throws Throwable { - System.setProperty("jazzer.disabled_hooks", + System.setProperty( + "jazzer.disabled_hooks", "com.code_intelligence.jazzer.sanitizers.ReflectiveCall," + "com.code_intelligence.jazzer.sanitizers.Deserialization," + "com.code_intelligence.jazzer.sanitizers.ExpressionLanguageInjection"); diff --git a/sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java b/sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java index 7d0192abf..19d1ecbc6 100644 --- a/sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java +++ b/sanitizers/src/test/java/com/example/ExpressionLanguageInjection.java @@ -22,7 +22,7 @@ import javax.validation.Validator; public class ExpressionLanguageInjection { - final private static Validator validator = + private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); public static void fuzzerInitialize() { diff --git a/sanitizers/src/test/java/com/example/LibraryLoad.java b/sanitizers/src/test/java/com/example/LibraryLoad.java index 81411767e..4954deb15 100644 --- a/sanitizers/src/test/java/com/example/LibraryLoad.java +++ b/sanitizers/src/test/java/com/example/LibraryLoad.java @@ -22,7 +22,9 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) { try { System.loadLibrary(input); - } catch (SecurityException | UnsatisfiedLinkError | NullPointerException + } catch (SecurityException + | UnsatisfiedLinkError + | NullPointerException | IllegalArgumentException ignored) { } } diff --git a/sanitizers/src/test/java/com/example/RegexRoadblocks.java b/sanitizers/src/test/java/com/example/RegexRoadblocks.java index 21986e3d0..4172c082d 100644 --- a/sanitizers/src/test/java/com/example/RegexRoadblocks.java +++ b/sanitizers/src/test/java/com/example/RegexRoadblocks.java @@ -80,9 +80,14 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) { matchedRangeLatin1CharProperty = true; } - if (matchedLiteral && matchedQuotedLiteral && matchedCaseInsensitiveLiteral && matchedGroup - && matchedAlternative && matchedSingleLatin1CharProperty - && matchedMultipleLatin1CharProperty && matchedRangeLatin1CharProperty) { + if (matchedLiteral + && matchedQuotedLiteral + && matchedCaseInsensitiveLiteral + && matchedGroup + && matchedAlternative + && matchedSingleLatin1CharProperty + && matchedMultipleLatin1CharProperty + && matchedRangeLatin1CharProperty) { throw new FuzzerSecurityIssueLow("Fuzzer matched all regexes in " + run + " runs"); } } diff --git a/sanitizers/src/test/java/com/example/ScriptEngineInjection.java b/sanitizers/src/test/java/com/example/ScriptEngineInjection.java index 631b7ab8d..5a5236896 100644 --- a/sanitizers/src/test/java/com/example/ScriptEngineInjection.java +++ b/sanitizers/src/test/java/com/example/ScriptEngineInjection.java @@ -25,8 +25,8 @@ import javax.script.ScriptEngineFactory; public class ScriptEngineInjection { - private final static ScriptEngine engine = new DummyScriptEngine(); - private final static ScriptContext context = new DummyScriptContext(); + private static final ScriptEngine engine = new DummyScriptEngine(); + private static final ScriptContext context = new DummyScriptContext(); private static void insecureScriptEval(String input) throws Exception { engine.eval(new StringReader(input), context); diff --git a/sanitizers/src/test/java/com/example/SqlInjection.java b/sanitizers/src/test/java/com/example/SqlInjection.java index 8a16b5c89..8bd9e0dba 100644 --- a/sanitizers/src/test/java/com/example/SqlInjection.java +++ b/sanitizers/src/test/java/com/example/SqlInjection.java @@ -26,8 +26,8 @@ public static void fuzzerInitialize() throws Exception { JdbcDataSource ds = new JdbcDataSource(); ds.setURL("jdbc:h2:./test.db"); conn = ds.getConnection(); - conn.createStatement().execute( - "CREATE TABLE IF NOT EXISTS pet (id IDENTITY PRIMARY KEY, name VARCHAR(50))"); + conn.createStatement() + .execute("CREATE TABLE IF NOT EXISTS pet (id IDENTITY PRIMARY KEY, name VARCHAR(50))"); } static void insecureInsertUser(String userName) throws SQLException { diff --git a/sanitizers/src/test/java/com/example/SsrfSocketConnectToHost.java b/sanitizers/src/test/java/com/example/SsrfSocketConnectToHost.java index 3e60e503c..e3afb2580 100644 --- a/sanitizers/src/test/java/com/example/SsrfSocketConnectToHost.java +++ b/sanitizers/src/test/java/com/example/SsrfSocketConnectToHost.java @@ -16,7 +16,6 @@ import com.code_intelligence.jazzer.api.BugDetectors; import com.code_intelligence.jazzer.api.FuzzedDataProvider; -import com.code_intelligence.jazzer.api.Jazzer; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; @@ -32,10 +31,9 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) throws Exception try (AutoCloseable ignored = BugDetectors.allowNetworkConnections()) { // Verify that policies nest properly. - try (AutoCloseable ignored1 = BugDetectors.allowNetworkConnections( - (String h, Integer p) -> h.equals("localhost"))) { - try (AutoCloseable ignored2 = BugDetectors.allowNetworkConnections()) { - } + try (AutoCloseable ignored1 = + BugDetectors.allowNetworkConnections((String h, Integer p) -> h.equals("localhost"))) { + try (AutoCloseable ignored2 = BugDetectors.allowNetworkConnections()) {} try (Socket s = new Socket()) { s.connect(new InetSocketAddress(host, port), CONNECTION_TIMEOUT_MS); } catch (IOException ignored3) { diff --git a/sanitizers/src/test/java/com/example/StackOverflowRegexInjection.java b/sanitizers/src/test/java/com/example/StackOverflowRegexInjection.java index 92dfcf38a..8abb4faa6 100644 --- a/sanitizers/src/test/java/com/example/StackOverflowRegexInjection.java +++ b/sanitizers/src/test/java/com/example/StackOverflowRegexInjection.java @@ -18,14 +18,12 @@ import java.util.regex.Pattern; /** - * Compiling a regex pattern can lead to stack overflows and thus is caught - * in the constructor of {@link java.util.regex.Pattern} and rethrown as a - * {@link java.util.regex.PatternSyntaxException}. - * The {@link com.code_intelligence.jazzer.sanitizers.RegexInjection} sanitizer - * uses this exception to detect injections and would incorrectly report a - * finding. Exceptions caused by stack overflows should not be handled in the - * hook as it's very unlikely that the fuzzer generates a pattern causing a - * stack overflow before it generates an invalid one. + * Compiling a regex pattern can lead to stack overflows and thus is caught in the constructor of + * {@link java.util.regex.Pattern} and rethrown as a {@link java.util.regex.PatternSyntaxException}. + * The {@link com.code_intelligence.jazzer.sanitizers.RegexInjection} sanitizer uses this exception + * to detect injections and would incorrectly report a finding. Exceptions caused by stack overflows + * should not be handled in the hook as it's very unlikely that the fuzzer generates a pattern + * causing a stack overflow before it generates an invalid one. */ @SuppressWarnings({"ReplaceOnLiteralHasNoEffect", "ResultOfMethodCallIgnored"}) public class StackOverflowRegexInjection { diff --git a/sanitizers/src/test/java/com/example/el/UserData.java b/sanitizers/src/test/java/com/example/el/UserData.java index 305e78eeb..b11afe1ca 100644 --- a/sanitizers/src/test/java/com/example/el/UserData.java +++ b/sanitizers/src/test/java/com/example/el/UserData.java @@ -36,6 +36,8 @@ public UserData(String email) { @Retention(RetentionPolicy.RUNTIME) @interface ValidEmailConstraint { String message() default "Invalid email address"; + Class[] groups() default {}; + Class[] payload() default {}; } diff --git a/sanitizers/src/test/java/com/example/ldap/MockLdapContext.java b/sanitizers/src/test/java/com/example/ldap/MockLdapContext.java index a51fadcdf..10cb02f5e 100644 --- a/sanitizers/src/test/java/com/example/ldap/MockLdapContext.java +++ b/sanitizers/src/test/java/com/example/ldap/MockLdapContext.java @@ -25,7 +25,7 @@ /** * Mock LdapContex implementation to test LdapInjection hook configuration. * - * Only {@code com.example.ldap.MockLdapContext#search(java.lang.String, java.lang.String, + *

Only {@code com.example.ldap.MockLdapContext#search(java.lang.String, java.lang.String, * javax.naming.directory.SearchControls)} is implemented to validate DN and filer query. */ public class MockLdapContext implements LdapContext { @@ -135,14 +135,16 @@ public DirContext getSchemaClassDefinition(String name) throws NamingException { } @Override - public NamingEnumeration search(Name name, Attributes matchingAttributes, - String[] attributesToReturn) throws NamingException { + public NamingEnumeration search( + Name name, Attributes matchingAttributes, String[] attributesToReturn) + throws NamingException { return null; } @Override - public NamingEnumeration search(String name, Attributes matchingAttributes, - String[] attributesToReturn) throws NamingException { + public NamingEnumeration search( + String name, Attributes matchingAttributes, String[] attributesToReturn) + throws NamingException { return null; } @@ -180,14 +182,16 @@ public NamingEnumeration search(String name, String filter, Search } @Override - public NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, - SearchControls cons) throws NamingException { + public NamingEnumeration search( + Name name, String filterExpr, Object[] filterArgs, SearchControls cons) + throws NamingException { return null; } @Override - public NamingEnumeration search(String name, String filterExpr, Object[] filterArgs, - SearchControls cons) throws NamingException { + public NamingEnumeration search( + String name, String filterExpr, Object[] filterArgs, SearchControls cons) + throws NamingException { return null; } diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/driver/FuzzedDataProviderImplFuzzTest.java b/selffuzz/src/test/java/com/code_intelligence/selffuzz/driver/FuzzedDataProviderImplFuzzTest.java index a320bd0b1..52eb5c17e 100644 --- a/selffuzz/src/test/java/com/code_intelligence/selffuzz/driver/FuzzedDataProviderImplFuzzTest.java +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/driver/FuzzedDataProviderImplFuzzTest.java @@ -28,7 +28,7 @@ public class FuzzedDataProviderImplFuzzTest { @FuzzTest - void fuzzedDataProviderTest(byte @NotNull[] buf) { + void fuzzedDataProviderTest(byte @NotNull [] buf) { try (FuzzedDataProviderImpl data = FuzzedDataProviderImpl.withJavaData(buf)) { List> actionList = getActionList(); @@ -40,53 +40,55 @@ void fuzzedDataProviderTest(byte @NotNull[] buf) { } List> getActionList() { - return Collections.unmodifiableList(Arrays.asList( - // clang-format off - // clang-format would compress this into multiple functions per line which I think looks worse - this::testBoolean, - this::testBooleans, - this::testByte, - this::testByteMinMax, - this::testBytes, - this::testRemainingAsBytes, - this::testShort, - this::testShortMinMax, - this::testShorts, - this::testInt, - this::testIntMinMax, - this::testInts, - this::testLong, - this::testLongMinMax, - this::testLongs, - this::testFloat, - this::testRegularFloat, - this::testRegularFloatMinMax, - this::testProbabilityFloat, - this::testDouble, - this::testRegularDouble, - this::testRegularDoubleMinMax, - this::testProbabilityDouble, - this::testChar, - this::testConsumeCharMinMax, - this::testCharNoSurrogates, - this::testString, - this::testRemainingAsString, - this::testAsciiString, - this::testRemainingAsAsciiString, - this::testPickValueCollection, - this::testPickValueArray, - this::testPickValueBoolean, - this::testPickValueByte, - this::testPickValueShort, - this::testPickValueInt, - this::testPickValueLong, - this::testPickValueDouble, - this::testPickValueFloat, - this::testPickValueChar, - this::testPickValuesCollection, - this::testPickValuesArray - // clang-format on - )); + return Collections.unmodifiableList( + Arrays.asList( + // clang-format off + // clang-format would compress this into multiple functions per line which I think looks + // worse + this::testBoolean, + this::testBooleans, + this::testByte, + this::testByteMinMax, + this::testBytes, + this::testRemainingAsBytes, + this::testShort, + this::testShortMinMax, + this::testShorts, + this::testInt, + this::testIntMinMax, + this::testInts, + this::testLong, + this::testLongMinMax, + this::testLongs, + this::testFloat, + this::testRegularFloat, + this::testRegularFloatMinMax, + this::testProbabilityFloat, + this::testDouble, + this::testRegularDouble, + this::testRegularDoubleMinMax, + this::testProbabilityDouble, + this::testChar, + this::testConsumeCharMinMax, + this::testCharNoSurrogates, + this::testString, + this::testRemainingAsString, + this::testAsciiString, + this::testRemainingAsAsciiString, + this::testPickValueCollection, + this::testPickValueArray, + this::testPickValueBoolean, + this::testPickValueByte, + this::testPickValueShort, + this::testPickValueInt, + this::testPickValueLong, + this::testPickValueDouble, + this::testPickValueFloat, + this::testPickValueChar, + this::testPickValuesCollection, + this::testPickValuesArray + // clang-format on + )); } void testBoolean(FuzzedDataProvider data) { diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/lang/FloatingPointMutatorFuzzTests.java b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/lang/FloatingPointMutatorFuzzTests.java index 289086c6f..79ed94fcf 100644 --- a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/lang/FloatingPointMutatorFuzzTests.java +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/lang/FloatingPointMutatorFuzzTests.java @@ -33,24 +33,30 @@ @SuppressWarnings("unchecked") class FloatingPointMutatorFuzzTests { @FuzzTest(maxDuration = "10m") - public void doubleMutatorTest(double min, double max, long seed, byte @NotNull[] data) + public void doubleMutatorTest(double min, double max, long seed, byte @NotNull [] data) throws IOException { DoubleInRange range = rndDoubleInRange(min, max); assumeTrue(range != null); SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - withExtraAnnotations(new TypeHolder<@NotNull Double>() {}.annotatedType(), range)); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + withExtraAnnotations( + new TypeHolder<@NotNull Double>() {}.annotatedType(), range)); assertMutator(mutator, data, seed); } @FuzzTest(maxDuration = "10m") - public void floatMutatorTest(float min, float max, long seed, byte @NotNull[] data) + public void floatMutatorTest(float min, float max, long seed, byte @NotNull [] data) throws IOException { FloatInRange range = rndFloatInRange(min, max); assumeTrue(range != null); SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - withExtraAnnotations(new TypeHolder<@NotNull Float>() {}.annotatedType(), range)); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + withExtraAnnotations( + new TypeHolder<@NotNull Float>() {}.annotatedType(), range)); assertMutator(mutator, data, seed); } @@ -88,7 +94,8 @@ public boolean equals(Object o) { return false; } DoubleInRange other = (DoubleInRange) o; - return this.min() == other.min() && this.max() == other.max() + return this.min() == other.min() + && this.max() == other.max() && this.allowNaN() == other.allowNaN(); } @@ -137,7 +144,8 @@ public boolean equals(Object o) { return false; } FloatInRange other = (FloatInRange) o; - return this.min() == other.min() && this.max() == other.max() + return this.min() == other.min() + && this.max() == other.max() && this.allowNaN() == other.allowNaN(); } diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/lang/StringMutatorFuzzTest.java b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/lang/StringMutatorFuzzTest.java index d609c9859..1afbdee36 100644 --- a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/lang/StringMutatorFuzzTest.java +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/lang/StringMutatorFuzzTest.java @@ -28,10 +28,10 @@ @SuppressWarnings("unchecked") class StringMutatorFuzzTest { @FuzzTest(maxDuration = "10m") - void stringMutatorTest(long seed, byte @NotNull[] data) throws IOException { + void stringMutatorTest(long seed, byte @NotNull [] data) throws IOException { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory().createOrThrow(new TypeHolder() {}.annotatedType()); assertMutator(mutator, data, seed); } } diff --git a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/proto/ProtobufMutatorFuzzTest.java b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/proto/ProtobufMutatorFuzzTest.java index 976b140b4..c315b1186 100644 --- a/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/proto/ProtobufMutatorFuzzTest.java +++ b/selffuzz/src/test/java/com/code_intelligence/selffuzz/mutation/mutator/proto/ProtobufMutatorFuzzTest.java @@ -31,7 +31,6 @@ import com.google.protobuf.DynamicMessage; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.util.regex.Matcher; import java.util.regex.Pattern; @SuppressWarnings("unchecked") @@ -44,7 +43,7 @@ class ProtobufMutatorFuzzTest { @SuppressWarnings({"unchecked", "unused"}) @FuzzTest - void protobufMutatorTest(long seed, @NotNull DescriptorProto messageType, byte @NotNull[] bytes) + void protobufMutatorTest(long seed, @NotNull DescriptorProto messageType, byte @NotNull [] bytes) throws IOException { protoName = messageType.getName(); // the name has to be valid to create the filedescriptor, other invalid names will be caught @@ -52,18 +51,22 @@ void protobufMutatorTest(long seed, @NotNull DescriptorProto messageType, byte @ if (!protoNamePattern.matcher(protoName).matches()) { return; } - file = FileDescriptorProto.newBuilder() - .setName("my_protos.proto") - .addMessageType(messageType) - .build(); + file = + FileDescriptorProto.newBuilder() + .setName("my_protos.proto") + .addMessageType(messageType) + .build(); SerializingMutator mutator; try { - mutator = (SerializingMutator) ProtoMutators.newFactory().createOrThrow( - new TypeHolder<@WithDefaultInstance( - "com.code_intelligence.selffuzz.mutation.mutator.proto.ProtobufMutatorFuzzTest#getDefaultInstance") - DynamicMessage>() { - }.annotatedType()); + mutator = + (SerializingMutator) + ProtoMutators.newFactory() + .createOrThrow( + new TypeHolder< + @WithDefaultInstance( + "com.code_intelligence.selffuzz.mutation.mutator.proto.ProtobufMutatorFuzzTest#getDefaultInstance") + DynamicMessage>() {}.annotatedType()); } catch (IllegalArgumentException e) { // an invalid proto descriptor will throw a DescriptorValidationException below but by the // time it gets here it'll be wrapped in a couple layers of other exceptions. We peel it apart @@ -72,7 +75,7 @@ void protobufMutatorTest(long seed, @NotNull DescriptorProto messageType, byte @ if (e.getCause() instanceof InvocationTargetException) { InvocationTargetException invocationException = (InvocationTargetException) e.getCause(); if (invocationException.getTargetException().getCause() - instanceof Descriptors.DescriptorValidationException) { + instanceof Descriptors.DescriptorValidationException) { return; } } diff --git a/src/jmh/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationBenchmark.java b/src/jmh/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationBenchmark.java index 4401da7dd..a0a508fb0 100644 --- a/src/jmh/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationBenchmark.java +++ b/src/jmh/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationBenchmark.java @@ -29,12 +29,10 @@ * This benchmark compares the throughput of a typical fuzz target when instrumented with different * edge coverage instrumentation strategies and coverage map implementations. * - * The benchmark currently uses the OWASP json-sanitizer as its target, which has the following - * desirable properties for a benchmark: - * - It is a reasonably sized project that does not consist of many different classes. - * - It is very heavy on computation with a high density of branching. - * - It is entirely CPU bound with no IO and does not call expensive methods from the standard - * library. + *

The benchmark currently uses the OWASP json-sanitizer as its target, which has the following + * desirable properties for a benchmark: - It is a reasonably sized project that does not consist of + * many different classes. - It is very heavy on computation with a high density of branching. - It + * is entirely CPU bound with no IO and does not call expensive methods from the standard library. * With these properties, results obtained from this benchmark should provide reasonable lower * bounds on the relative slowdown introduced by the various approaches to instrumentations. */ @@ -82,8 +80,9 @@ public static MethodHandle instrumentWithStrategy( public void instrumentWithStrategies() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException { uninstrumented_sanitize = instrumentWithStrategy(null, null); - local_DirectByteBuffer_NeverZero_sanitize = instrumentWithStrategy( - DirectByteBufferStrategy.INSTANCE, DirectByteBufferCoverageMap.class); + local_DirectByteBuffer_NeverZero_sanitize = + instrumentWithStrategy( + DirectByteBufferStrategy.INSTANCE, DirectByteBufferCoverageMap.class); staticMethod_DirectByteBuffer_NeverZero_sanitize = instrumentWithStrategy(new StaticMethodStrategy(), DirectByteBufferCoverageMap.class); staticMethod_DirectByteBuffer2_NeverZero_sanitize = diff --git a/src/jmh/java/com/code_intelligence/jazzer/mutation/MutatorBenchmark.java b/src/jmh/java/com/code_intelligence/jazzer/mutation/MutatorBenchmark.java index f2cc13583..a3331c6ad 100644 --- a/src/jmh/java/com/code_intelligence/jazzer/mutation/MutatorBenchmark.java +++ b/src/jmh/java/com/code_intelligence/jazzer/mutation/MutatorBenchmark.java @@ -50,14 +50,16 @@ public static void fuzzMinimal(@NotNull List<@NotNull Byte> bytes) { @State(Scope.Benchmark) public static class BenchmarkState { - @Param({"10", "100", "1000"}) public int mutations; + @Param({"10", "100", "1000"}) + public int mutations; public ArgumentsMutator mutator; @Setup(Level.Iteration) public void setUp() throws NoSuchMethodException { - mutator = ArgumentsMutator.forStaticMethodOrThrow( - MutatorBenchmark.class.getMethod("fuzzMinimal", List.class)); + mutator = + ArgumentsMutator.forStaticMethodOrThrow( + MutatorBenchmark.class.getMethod("fuzzMinimal", List.class)); } } diff --git a/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacks.java b/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacks.java index 6e8343ce5..c3a9702cd 100644 --- a/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacks.java +++ b/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacks.java @@ -22,8 +22,10 @@ public final class FuzzerCallbacks { } static native void traceCmpInt(int arg1, int arg2, int pc); + static native void traceSwitch(long val, long[] cases, int pc); static native void traceMemcmp(byte[] b1, byte[] b2, int result, int pc); + static native void traceStrstr(String s1, String s2, int pc); } diff --git a/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacksBenchmark.java b/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacksBenchmark.java index 81a8f56e0..4395dacc4 100644 --- a/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacksBenchmark.java +++ b/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacksBenchmark.java @@ -71,7 +71,8 @@ public void traceCmpIntOptimizedCritical(TraceCmpIntState state) { @State(Scope.Benchmark) public static class TraceSwitchState { - @Param({"5", "10"}) int numCases; + @Param({"5", "10"}) + int numCases; long val; long[] cases; @@ -81,16 +82,18 @@ public static class TraceSwitchState { public void setup() { cases = new long[2 + numCases]; Random random = ThreadLocalRandom.current(); - Arrays.setAll(cases, i -> { - switch (i) { - case 0: - return numCases; - case 1: - return 32; - default: - return random.nextInt(); - } - }); + Arrays.setAll( + cases, + i -> { + switch (i) { + case 0: + return numCases; + case 1: + return 32; + default: + return random.nextInt(); + } + }); Arrays.sort(cases, 2, cases.length); val = random.nextInt(); } @@ -128,7 +131,8 @@ public void traceSwitchOptimizedNonCritical(TraceSwitchState state) { @State(Scope.Benchmark) public static class TraceMemcmpState { - @Param({"10", "100", "1000"}) int length; + @Param({"10", "100", "1000"}) + int length; byte[] array1; byte[] array2; @@ -166,8 +170,11 @@ public void traceMemcmpOptimizedNonCritical(TraceMemcmpState state) { @State(Scope.Benchmark) public static class TraceStrstrState { - @Param({"10", "100", "1000"}) int length; - @Param({"true", "false"}) boolean asciiOnly; + @Param({"10", "100", "1000"}) + int length; + + @Param({"true", "false"}) + boolean asciiOnly; String haystack; String needle; diff --git a/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacksPanama.java b/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacksPanama.java index ce3d62902..e8ed4ccad 100644 --- a/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacksPanama.java +++ b/src/jmh/java/com/code_intelligence/jazzer/runtime/FuzzerCallbacksPanama.java @@ -26,23 +26,27 @@ import jdk.incubator.foreign.SymbolLookup; /** - * Pure-Java implementation of the fuzzer callbacks backed by Project Panama (requires JDK 16+). - * To include the implementation in the benchmark on a supported JDK, uncomment the relevant lines - * in BUILD.bazel. + * Pure-Java implementation of the fuzzer callbacks backed by Project Panama (requires JDK 16+). To + * include the implementation in the benchmark on a supported JDK, uncomment the relevant lines in + * BUILD.bazel. */ public class FuzzerCallbacksPanama { static { RulesJni.loadLibrary("fuzzer_callbacks", FuzzerCallbacks.class); } - private static final MethodHandle traceCmp4 = CLinker.getInstance().downcallHandle( - SymbolLookup.loaderLookup().lookup("__sanitizer_cov_trace_cmp4").get(), - MethodType.methodType(void.class, int.class, int.class), - FunctionDescriptor.ofVoid(CLinker.C_INT, CLinker.C_INT)); - private static final MethodHandle traceSwitch = CLinker.getInstance().downcallHandle( - SymbolLookup.loaderLookup().lookup("__sanitizer_cov_trace_switch").get(), - MethodType.methodType(void.class, long.class, MemoryAddress.class), - FunctionDescriptor.ofVoid(CLinker.C_LONG, CLinker.C_POINTER)); + private static final MethodHandle traceCmp4 = + CLinker.getInstance() + .downcallHandle( + SymbolLookup.loaderLookup().lookup("__sanitizer_cov_trace_cmp4").get(), + MethodType.methodType(void.class, int.class, int.class), + FunctionDescriptor.ofVoid(CLinker.C_INT, CLinker.C_INT)); + private static final MethodHandle traceSwitch = + CLinker.getInstance() + .downcallHandle( + SymbolLookup.loaderLookup().lookup("__sanitizer_cov_trace_switch").get(), + MethodType.methodType(void.class, long.class, MemoryAddress.class), + FunctionDescriptor.ofVoid(CLinker.C_LONG, CLinker.C_POINTER)); static void traceCmpInt(int arg1, int arg2, int pc) throws Throwable { traceCmp4.invokeExact(arg1, arg2); @@ -50,8 +54,9 @@ static void traceCmpInt(int arg1, int arg2, int pc) throws Throwable { static void traceCmpSwitch(long val, long[] cases, int pc) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment nativeCopy = MemorySegment.allocateNative( - MemoryLayout.sequenceLayout(cases.length, CLinker.C_LONG), scope); + MemorySegment nativeCopy = + MemorySegment.allocateNative( + MemoryLayout.sequenceLayout(cases.length, CLinker.C_LONG), scope); nativeCopy.copyFrom(MemorySegment.ofArray(cases)); traceSwitch.invokeExact(val, nativeCopy.address()); } diff --git a/src/main/java/com/code_intelligence/jazzer/Jazzer.java b/src/main/java/com/code_intelligence/jazzer/Jazzer.java index b01df0ba1..1188b37a6 100644 --- a/src/main/java/com/code_intelligence/jazzer/Jazzer.java +++ b/src/main/java/com/code_intelligence/jazzer/Jazzer.java @@ -57,9 +57,9 @@ * The libFuzzer-compatible CLI entrypoint for Jazzer. * *

Arguments to Jazzer are passed as command-line arguments or {@code jazzer.*} system - * properties. For example, setting the property {@code jazzer.target_class} to - * {@code com.example.FuzzTest} is equivalent to passing the argument - * {@code --target_class=com.example.FuzzTest}. + * properties. For example, setting the property {@code jazzer.target_class} to {@code + * com.example.FuzzTest} is equivalent to passing the argument {@code + * --target_class=com.example.FuzzTest}. * *

Arguments to libFuzzer are passed as command-line arguments. */ @@ -71,9 +71,10 @@ public static void main(String[] args) throws IOException, InterruptedException // Accessed by jazzer_main.cpp. @SuppressWarnings("unused") private static void main(byte[][] nativeArgs) throws IOException, InterruptedException { - start(Arrays.stream(nativeArgs) - .map(bytes -> new String(bytes, StandardCharsets.UTF_8)) - .collect(toList())); + start( + Arrays.stream(nativeArgs) + .map(bytes -> new String(bytes, StandardCharsets.UTF_8)) + .collect(toList())); } private static void start(List args) throws IOException, InterruptedException { @@ -130,30 +131,40 @@ private static void start(List args) throws IOException, InterruptedExce // strong and weak symbols. preloadLibs.add(RulesJni.extractLibrary("jazzer_preload", Jazzer.class)); if (loadASan) { - processBuilder.environment().compute("ASAN_OPTIONS", - (name, currentValue) - -> appendWithPathListSeparator(name, - // The JVM produces an extremely large number of false positive leaks, which makes - // it impossible to use LeakSanitizer. - // TODO: Investigate whether we can hook malloc/free only for JNI shared - // libraries, not the JVM itself. - "detect_leaks=0", - // We load jazzer_preload first. - "verify_asan_link_order=0")); + processBuilder + .environment() + .compute( + "ASAN_OPTIONS", + (name, currentValue) -> + appendWithPathListSeparator( + name, + // The JVM produces an extremely large number of false positive leaks, which + // makes + // it impossible to use LeakSanitizer. + // TODO: Investigate whether we can hook malloc/free only for JNI shared + // libraries, not the JVM itself. + "detect_leaks=0", + // We load jazzer_preload first. + "verify_asan_link_order=0")); Log.warn("Jazzer is not compatible with LeakSanitizer. Leaks are not reported."); preloadLibs.add(findLibrary(asanLibNames())); } if (loadHWASan) { - processBuilder.environment().compute("HWASAN_OPTIONS", - (name, currentValue) - -> appendWithPathListSeparator(name, - // The JVM produces an extremely large number of false positive leaks, which makes - // it impossible to use LeakSanitizer. - // TODO: Investigate whether we can hook malloc/free only for JNI shared - // libraries, not the JVM itself. - "detect_leaks=0", - // We load jazzer_preload first. - "verify_asan_link_order=0")); + processBuilder + .environment() + .compute( + "HWASAN_OPTIONS", + (name, currentValue) -> + appendWithPathListSeparator( + name, + // The JVM produces an extremely large number of false positive leaks, which + // makes + // it impossible to use LeakSanitizer. + // TODO: Investigate whether we can hook malloc/free only for JNI shared + // libraries, not the JVM itself. + "detect_leaks=0", + // We load jazzer_preload first. + "verify_asan_link_order=0")); Log.warn("Jazzer is not compatible with LeakSanitizer. Leaks are not reported."); preloadLibs.add(findLibrary(hwasanLibNames())); } @@ -168,12 +179,13 @@ private static void start(List args) throws IOException, InterruptedExce // preloaded. processBuilder.environment().remove(preloadVariable()); Map additionalEnvironment = new HashMap<>(); - additionalEnvironment.put(preloadVariable(), + additionalEnvironment.put( + preloadVariable(), appendWithPathListSeparator( - preloadVariable(), preloadLibs.stream().map(Path::toString).toArray(String[] ::new))); + preloadVariable(), preloadLibs.stream().map(Path::toString).toArray(String[]::new))); List subProcessArgs = - Stream - .concat(Stream.of(prepareArgv0(additionalEnvironment)), + Stream.concat( + Stream.of(prepareArgv0(additionalEnvironment)), // Prevent a "fork bomb" by stripping all args that trigger this code path. args.stream().filter(arg -> !argsToFilter.contains(arg.split("=")[0]))) .collect(toList()); @@ -228,7 +240,8 @@ static boolean isLibFuzzerOptionEnabled(String option, List args) { private static String prepareArgv0(Map additionalEnvironment) throws IOException { if (!isPosixOrAndroid() && !additionalEnvironment.isEmpty()) { throw new IllegalArgumentException( - "Setting environment variables in the wrapper is only supported on POSIX systems and Android"); + "Setting environment variables in the wrapper is only supported on POSIX systems and" + + " Android"); } char shellQuote = isPosixOrAndroid() ? '\'' : '"'; String launcherTemplate; @@ -241,17 +254,18 @@ private static String prepareArgv0(Map additionalEnvironment) th } String launcherExtension = isPosix() ? ".sh" : ".bat"; - FileAttribute[] launcherScriptAttributes = isPosixOrAndroid() - ? new FileAttribute[] {PosixFilePermissions.asFileAttribute( - PosixFilePermissions.fromString("rwx------"))} - : new FileAttribute[] {}; - String env = additionalEnvironment.entrySet() - .stream() - .map(e -> e.getKey() + "='" + e.getValue() + "'") - .collect(joining(" ")); + FileAttribute[] launcherScriptAttributes = + isPosixOrAndroid() + ? new FileAttribute[] { + PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------")) + } + : new FileAttribute[] {}; + String env = + additionalEnvironment.entrySet().stream() + .map(e -> e.getKey() + "='" + e.getValue() + "'") + .collect(joining(" ")); String command = - Stream - .concat(Stream.of(IS_ANDROID ? "exec" : javaBinary().toString()), javaBinaryArgs()) + Stream.concat(Stream.of(IS_ANDROID ? "exec" : javaBinary().toString()), javaBinaryArgs()) // Escape individual arguments for the shell. .map(str -> shellQuote + str + shellQuote) .collect(joining(" ")); @@ -266,8 +280,12 @@ private static String prepareArgv0(Map additionalEnvironment) th String exportCommand = AndroidRuntime.getClassPathsCommand(); String ldLibraryPath = AndroidRuntime.getLdLibraryPath(); launcherContent = String.format(launcherTemplate, exportCommand, ldLibraryPath, invocation); - launcher = Files.createTempFile( - Paths.get("/data/local/tmp/"), "jazzer-", launcherExtension, launcherScriptAttributes); + launcher = + Files.createTempFile( + Paths.get("/data/local/tmp/"), + "jazzer-", + launcherExtension, + launcherScriptAttributes); } else { launcherContent = String.format(launcherTemplate, invocation); launcher = Files.createTempFile("jazzer-", launcherExtension, launcherScriptAttributes); @@ -316,24 +334,34 @@ private static Stream javaBinaryArgs() throws IOException { } // ManagementFactory wont work with Android - Stream stream = Stream.of("app_process", "-Djdk.attach.allowAttachSelf=true", - "-Xplugin:libopenjdkjvmti.so", - "-agentpath:" + agentPath.toString() + "=" + nativeAgentOptions, "-Xcompiler-option", - "--debuggable", "/system/bin", Jazzer.class.getName()); + Stream stream = + Stream.of( + "app_process", + "-Djdk.attach.allowAttachSelf=true", + "-Xplugin:libopenjdkjvmti.so", + "-agentpath:" + agentPath.toString() + "=" + nativeAgentOptions, + "-Xcompiler-option", + "--debuggable", + "/system/bin", + Jazzer.class.getName()); return stream; } - Stream stream = Stream.of("-cp", System.getProperty("java.class.path"), - // Make ByteBuddyAgent's job simpler by allowing it to attach directly to the JVM - // rather than relying on an external helper. The latter fails on macOS 12 with JDK 11+ - // (but not 8) and UBSan preloaded with: - // Caused by: java.io.IOException: Cannot run program - // "/Users/runner/hostedtoolcache/Java_Zulu_jdk/17.0.4-8/x64/bin/java": error=0, Failed - // to exec spawn helper: pid: 8227, signal: 9 - // Presumably, this issue is caused by codesigning and the exec helper missing the - // entitlements required for library insertion. - "-Djdk.attach.allowAttachSelf=true", Jazzer.class.getName()); + Stream stream = + Stream.of( + "-cp", + System.getProperty("java.class.path"), + // Make ByteBuddyAgent's job simpler by allowing it to attach directly to the JVM + // rather than relying on an external helper. The latter fails on macOS 12 with JDK 11+ + // (but not 8) and UBSan preloaded with: + // Caused by: java.io.IOException: Cannot run program + // "/Users/runner/hostedtoolcache/Java_Zulu_jdk/17.0.4-8/x64/bin/java": error=0, Failed + // to exec spawn helper: pid: 8227, signal: 9 + // Presumably, this issue is caused by codesigning and the exec helper missing the + // entitlements required for library insertion. + "-Djdk.attach.allowAttachSelf=true", + Jazzer.class.getName()); return Stream.concat(ManagementFactory.getRuntimeMXBean().getInputArguments().stream(), stream); } @@ -419,8 +447,9 @@ private static Optional tryFindLibraryUsingClang(String name) { try { Process process = processBuilder.start(); if (process.waitFor() != 0) { - Log.error(String.format( - "'%s' exited with exit code %d", String.join(" ", command), process.exitValue())); + Log.error( + String.format( + "'%s' exited with exit code %d", String.join(" ", command), process.exitValue())); copy(process.getInputStream(), System.out); copy(process.getErrorStream(), System.err); exit(1); @@ -454,8 +483,9 @@ private static List hwasanLibNames() { private static List asanLibNames() { if (isLinux()) { if (IS_ANDROID) { - Log.error("ASan is not supported for Android at this time. Use --hwasan for Address " - + "Sanitization on Android"); + Log.error( + "ASan is not supported for Android at this time. Use --hwasan for Address " + + "Sanitization on Android"); exit(1); } diff --git a/src/main/java/com/code_intelligence/jazzer/agent/AgentInstaller.java b/src/main/java/com/code_intelligence/jazzer/agent/AgentInstaller.java index 5dd041ac3..99056b99d 100644 --- a/src/main/java/com/code_intelligence/jazzer/agent/AgentInstaller.java +++ b/src/main/java/com/code_intelligence/jazzer/agent/AgentInstaller.java @@ -50,7 +50,9 @@ public static void install(boolean enableAgent) { Class agent = Class.forName("com.code_intelligence.jazzer.agent.Agent"); Method install = agent.getMethod("install", Instrumentation.class); install.invoke(null, instrumentation); - } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException + } catch (ClassNotFoundException + | InvocationTargetException + | NoSuchMethodException | IllegalAccessException e) { throw new IllegalStateException("Failed to run Agent.install", e); } diff --git a/src/main/java/com/code_intelligence/jazzer/android/AndroidRuntime.java b/src/main/java/com/code_intelligence/jazzer/android/AndroidRuntime.java index 8c893591e..6c2ead5a3 100644 --- a/src/main/java/com/code_intelligence/jazzer/android/AndroidRuntime.java +++ b/src/main/java/com/code_intelligence/jazzer/android/AndroidRuntime.java @@ -20,9 +20,7 @@ import com.code_intelligence.jazzer.utils.Log; import com.github.fmeum.rules_jni.RulesJni; -/** - * Loads Android tooling library and registers native functions. - */ +/** Loads Android tooling library and registers native functions. */ public class AndroidRuntime { private static final String DO_NOT_INITIALIZE = "use_none"; private static final String INIT_JAVA_ART = "use_platform_libs"; @@ -56,12 +54,15 @@ public static void initialize() { break; default: - Log.error(String.format( - "%s is not a valid options for android_init_options. Valid Options: [use_none, use_platform_libs]", - androidInitOptions)); + Log.error( + String.format( + "%s is not a valid options for android_init_options. Valid Options: [use_none," + + " use_platform_libs]", + androidInitOptions)); System.exit(1); } - }; + } + ; /** * Returns a command to set the classpath for fuzzing. diff --git a/src/main/java/com/code_intelligence/jazzer/android/DexFileManager.java b/src/main/java/com/code_intelligence/jazzer/android/DexFileManager.java index 23d2eeec2..34711e616 100644 --- a/src/main/java/com/code_intelligence/jazzer/android/DexFileManager.java +++ b/src/main/java/com/code_intelligence/jazzer/android/DexFileManager.java @@ -17,27 +17,21 @@ package com.code_intelligence.jazzer.android; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; -import java.lang.Math; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class DexFileManager { - private final static int MAX_READ_LENGTH = 2000000; + private static final int MAX_READ_LENGTH = 2000000; public static byte[] getBytecodeFromDex(String jarPath, String dexFile) throws IOException { try (JarFile jarFile = new JarFile(jarPath)) { - JarEntry entry = jarFile.stream() - .filter(jarEntry -> jarEntry.getName().equals(dexFile)) - .findFirst() - .orElse(null); + JarEntry entry = + jarFile.stream() + .filter(jarEntry -> jarEntry.getName().equals(dexFile)) + .findFirst() + .orElse(null); if (entry == null) { throw new IOException("Could not find dex file: " + dexFile); @@ -62,7 +56,7 @@ public static String[] getDexFilesForJar(String jarpath) throws IOException { return jarFile.stream() .map(JarEntry::getName) .filter(entry -> entry.endsWith(".dex")) - .toArray(String[] ::new); + .toArray(String[]::new); } } } diff --git a/src/main/java/com/code_intelligence/jazzer/api/Autofuzz.java b/src/main/java/com/code_intelligence/jazzer/api/Autofuzz.java index 77711ee9a..2bc407006 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/Autofuzz.java +++ b/src/main/java/com/code_intelligence/jazzer/api/Autofuzz.java @@ -18,10 +18,8 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -/** - * Static helper functions that allow Jazzer fuzz targets to use Autofuzz. - */ -final public class Autofuzz { +/** Static helper functions that allow Jazzer fuzz targets to use Autofuzz. */ +public final class Autofuzz { private static final MethodHandle CONSUME; private static final MethodHandle AUTOFUZZ_FUNCTION_1; private static final MethodHandle AUTOFUZZ_FUNCTION_2; @@ -52,26 +50,66 @@ final public class Autofuzz { MethodType.methodType(Object.class, FuzzedDataProvider.class, Class.class); consume = MethodHandles.publicLookup().findStatic(metaClass, "consume", consumeType); - autofuzzFunction1 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(Object.class, FuzzedDataProvider.class, Function1.class)); - autofuzzFunction2 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(Object.class, FuzzedDataProvider.class, Function2.class)); - autofuzzFunction3 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(Object.class, FuzzedDataProvider.class, Function3.class)); - autofuzzFunction4 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(Object.class, FuzzedDataProvider.class, Function4.class)); - autofuzzFunction5 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(Object.class, FuzzedDataProvider.class, Function5.class)); - autofuzzConsumer1 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer1.class)); - autofuzzConsumer2 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer2.class)); - autofuzzConsumer3 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer3.class)); - autofuzzConsumer4 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer4.class)); - autofuzzConsumer5 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz", - MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer5.class)); + autofuzzFunction1 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(Object.class, FuzzedDataProvider.class, Function1.class)); + autofuzzFunction2 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(Object.class, FuzzedDataProvider.class, Function2.class)); + autofuzzFunction3 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(Object.class, FuzzedDataProvider.class, Function3.class)); + autofuzzFunction4 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(Object.class, FuzzedDataProvider.class, Function4.class)); + autofuzzFunction5 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(Object.class, FuzzedDataProvider.class, Function5.class)); + autofuzzConsumer1 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer1.class)); + autofuzzConsumer2 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer2.class)); + autofuzzConsumer3 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer3.class)); + autofuzzConsumer4 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer4.class)); + autofuzzConsumer5 = + MethodHandles.publicLookup() + .findStatic( + metaClass, + "autofuzz", + MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer5.class)); } catch (ClassNotFoundException ignore) { // Not running in the context of the agent. This is fine as long as no methods are called on // this class. @@ -102,8 +140,8 @@ private Autofuzz() {} /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - *

- * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. @@ -132,8 +170,8 @@ public static R autofuzz(FuzzedDataProvider data, Function1 func) /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - *

- * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. @@ -162,8 +200,8 @@ public static R autofuzz(FuzzedDataProvider data, Function2 - * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. @@ -192,8 +230,8 @@ public static R autofuzz(FuzzedDataProvider data, Function3 - * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. @@ -223,8 +261,8 @@ public static R autofuzz( /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - *

- * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. @@ -254,14 +292,14 @@ public static R autofuzz( /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - *

- * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. * @param func a method reference for the function to autofuzz. If there are multiple overloads, * resolve ambiguities by explicitly casting to {@link Consumer1} with explicitly specified - * type variable. + * type variable. * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call. * The {@link Throwable} is thrown unchecked. @@ -279,14 +317,14 @@ public static void autofuzz(FuzzedDataProvider data, Consumer1 func) { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - *

- * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. * @param func a method reference for the function to autofuzz. If there are multiple overloads, * resolve ambiguities by explicitly casting to {@link Consumer2} with (partially) specified - * type variables. + * type variables. * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call. * The {@link Throwable} is thrown unchecked. @@ -304,14 +342,14 @@ public static void autofuzz(FuzzedDataProvider data, Consumer2 /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - *

- * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. * @param func a method reference for the function to autofuzz. If there are multiple overloads, * resolve ambiguities by explicitly casting to {@link Consumer3} with (partially) specified - * type variables. + * type variables. * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call. * The {@link Throwable} is thrown unchecked. @@ -329,14 +367,14 @@ public static void autofuzz(FuzzedDataProvider data, Consumer3 - * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. * @param func a method reference for the function to autofuzz. If there are multiple overloads, * resolve ambiguities by explicitly casting to {@link Consumer4} with (partially) specified - * type variables. + * type variables. * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call. * The {@link Throwable} is thrown unchecked. @@ -355,14 +393,14 @@ public static void autofuzz( /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - *

- * Note: This function is inherently heuristic and may fail to execute {@code func} in + * + *

Note: This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. * @param func a method reference for the function to autofuzz. If there are multiple overloads, * resolve ambiguities by explicitly casting to {@link Consumer5} with (partially) specified - * type variables. + * type variables. * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call. * The {@link Throwable} is thrown unchecked. @@ -381,9 +419,9 @@ public static void autofuzz( /** * Attempts to construct an instance of {@code type} from the fuzzer input using only public * methods available on the classpath. - *

- * Note: This function is inherently heuristic and may fail to return meaningful values for - * a variety of reasons. + * + *

Note: This function is inherently heuristic and may fail to return meaningful values + * for a variety of reasons. * * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}. * @param type the {@link Class} to construct an instance of. @@ -406,6 +444,6 @@ public static T consume(FuzzedDataProvider data, Class type) { // Rethrows a (possibly checked) exception while avoiding a throws declaration. @SuppressWarnings("unchecked") private static void rethrowUnchecked(Throwable t) throws T { - throw(T) t; + throw (T) t; } } diff --git a/src/main/java/com/code_intelligence/jazzer/api/AutofuzzConstructionException.java b/src/main/java/com/code_intelligence/jazzer/api/AutofuzzConstructionException.java index 93340ee82..9e9c9b648 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/AutofuzzConstructionException.java +++ b/src/main/java/com/code_intelligence/jazzer/api/AutofuzzConstructionException.java @@ -16,16 +16,16 @@ // An exception wrapping a Throwable thrown during the construction of parameters for, but not the // actual invocation of an autofuzzed method. -/** - * Only used internally. - */ +/** Only used internally. */ public class AutofuzzConstructionException extends RuntimeException { public AutofuzzConstructionException() { super(); } + public AutofuzzConstructionException(String message) { super(message); } + public AutofuzzConstructionException(Throwable cause) { super(cause); } diff --git a/src/main/java/com/code_intelligence/jazzer/api/AutofuzzInvocationException.java b/src/main/java/com/code_intelligence/jazzer/api/AutofuzzInvocationException.java index eb936630e..fb47f2a58 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/AutofuzzInvocationException.java +++ b/src/main/java/com/code_intelligence/jazzer/api/AutofuzzInvocationException.java @@ -16,9 +16,7 @@ // An exception wrapping a {@link Throwable} thrown during the actual invocation of, but not the // construction of parameters for an autofuzzed method. -/** - * Only used internally. - */ +/** Only used internally. */ public class AutofuzzInvocationException extends RuntimeException { public AutofuzzInvocationException() { super(); diff --git a/src/main/java/com/code_intelligence/jazzer/api/BugDetectors.java b/src/main/java/com/code_intelligence/jazzer/api/BugDetectors.java index 64f019315..1e5eb5dae 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/BugDetectors.java +++ b/src/main/java/com/code_intelligence/jazzer/api/BugDetectors.java @@ -19,9 +19,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiPredicate; -/** - * Provides static functions that configure the behavior of bug detectors provided by Jazzer. - */ +/** Provides static functions that configure the behavior of bug detectors provided by Jazzer. */ public final class BugDetectors { private static final AtomicReference> currentPolicy = getConnectionPermittedReference(); @@ -35,16 +33,16 @@ public final class BugDetectors { *

By default, all attempted network connections are considered unexpected and result in a * finding being reported. * - *

By wrapping the call into a try-with-resources statement, network connection permissions - * can be configured to apply to individual parts of the fuzz test only: + *

By wrapping the call into a try-with-resources statement, network connection permissions can + * be configured to apply to individual parts of the fuzz test only: * *

{@code
-   *   Image image = parseImage(bytes);
-   *   Response response;
-   *   try (SilentCloseable unused = BugDetectors.allowNetworkConnections()) {
-   *     response = uploadImage(image);
-   *   }
-   *   handleResponse(response);
+   * Image image = parseImage(bytes);
+   * Response response;
+   * try (SilentCloseable unused = BugDetectors.allowNetworkConnections()) {
+   *   response = uploadImage(image);
+   * }
+   * handleResponse(response);
    * }
* * @return a {@link SilentCloseable} that restores the previously set permissions when closed @@ -59,21 +57,21 @@ public static SilentCloseable allowNetworkConnections() { *

By default, all attempted network connections are considered unexpected and result in a * finding being reported. * - *

By wrapping the call into a try-with-resources statement, network connection permissions - * can be configured to apply to individual parts of the fuzz test only: + *

By wrapping the call into a try-with-resources statement, network connection permissions can + * be configured to apply to individual parts of the fuzz test only: * *

{@code
-   *   Image image = parseImage(bytes);
-   *   Response response;
-   *   try (SilentCloseable unused = BugDetectors.allowNetworkConnections(
-   *       (host, port) -> host.equals("example.org"))) {
-   *     response = uploadImage(image, "example.org");
-   *   }
-   *   handleResponse(response);
+   * Image image = parseImage(bytes);
+   * Response response;
+   * try (SilentCloseable unused = BugDetectors.allowNetworkConnections(
+   *     (host, port) -> host.equals("example.org"))) {
+   *   response = uploadImage(image, "example.org");
+   * }
+   * handleResponse(response);
    * }
* * @param connectionPermitted a predicate that evaluate to {@code true} if network connections to - * the provided combination of host and port are permitted + * the provided combination of host and port are permitted * @return a {@link SilentCloseable} that restores the previously set predicate when closed */ public static SilentCloseable allowNetworkConnections( @@ -88,7 +86,8 @@ public static SilentCloseable allowNetworkConnections( return () -> { if (!currentPolicy.compareAndSet(connectionPermitted, previousPolicy)) { throw new IllegalStateException( - "Failed to reset network connection policy - using try-with-resources is highly recommended"); + "Failed to reset network connection policy - using try-with-resources is highly" + + " recommended"); } }; } @@ -97,9 +96,8 @@ private static AtomicReference> getConnectionPermit try { Class ssrfSanitizer = Class.forName("com.code_intelligence.jazzer.sanitizers.ServerSideRequestForgery"); - return (AtomicReference>) ssrfSanitizer - .getField("connectionPermitted") - .get(null); + return (AtomicReference>) + ssrfSanitizer.getField("connectionPermitted").get(null); } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { System.err.println("WARNING: "); e.printStackTrace(); diff --git a/src/main/java/com/code_intelligence/jazzer/api/CannedFuzzedDataProvider.java b/src/main/java/com/code_intelligence/jazzer/api/CannedFuzzedDataProvider.java index 7209a4978..15ff6d6db 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/CannedFuzzedDataProvider.java +++ b/src/main/java/com/code_intelligence/jazzer/api/CannedFuzzedDataProvider.java @@ -21,10 +21,10 @@ import java.util.List; /** - * Replays recorded FuzzedDataProvider invocations that were executed while fuzzing. - * Note: This class is only meant to be used by Jazzer's generated reproducers. + * Replays recorded FuzzedDataProvider invocations that were executed while fuzzing. Note: This + * class is only meant to be used by Jazzer's generated reproducers. */ -final public class CannedFuzzedDataProvider implements FuzzedDataProvider { +public final class CannedFuzzedDataProvider implements FuzzedDataProvider { private final Iterator nextReply; public CannedFuzzedDataProvider(String can) { diff --git a/src/main/java/com/code_intelligence/jazzer/api/Consumer1.java b/src/main/java/com/code_intelligence/jazzer/api/Consumer1.java index 472c2efdb..070041f52 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/Consumer1.java +++ b/src/main/java/com/code_intelligence/jazzer/api/Consumer1.java @@ -18,5 +18,6 @@ @FunctionalInterface public interface Consumer1 extends Consumer { - @Override void accept(T1 t1); + @Override + void accept(T1 t1); } diff --git a/src/main/java/com/code_intelligence/jazzer/api/Consumer2.java b/src/main/java/com/code_intelligence/jazzer/api/Consumer2.java index d951ade76..f3edb0f1a 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/Consumer2.java +++ b/src/main/java/com/code_intelligence/jazzer/api/Consumer2.java @@ -18,5 +18,6 @@ @FunctionalInterface public interface Consumer2 extends BiConsumer { - @Override void accept(T1 t1, T2 t2); + @Override + void accept(T1 t1, T2 t2); } diff --git a/src/main/java/com/code_intelligence/jazzer/api/Function1.java b/src/main/java/com/code_intelligence/jazzer/api/Function1.java index 43d68cc70..593b9b029 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/Function1.java +++ b/src/main/java/com/code_intelligence/jazzer/api/Function1.java @@ -18,5 +18,6 @@ @FunctionalInterface public interface Function1 extends Function { - @Override R apply(T1 t1); + @Override + R apply(T1 t1); } diff --git a/src/main/java/com/code_intelligence/jazzer/api/Function2.java b/src/main/java/com/code_intelligence/jazzer/api/Function2.java index 6e733b1ca..cd5ef8ec0 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/Function2.java +++ b/src/main/java/com/code_intelligence/jazzer/api/Function2.java @@ -18,5 +18,6 @@ @FunctionalInterface public interface Function2 extends BiFunction { - @Override R apply(T1 t1, T2 t2); + @Override + R apply(T1 t1, T2 t2); } diff --git a/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java b/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java index 7b4b5444f..751c31288 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java +++ b/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java @@ -36,6 +36,7 @@ public interface FuzzedDataProvider { /** * Consumes a {@code boolean} array from the fuzzer input. + * *

The array will usually have length {@code length}, but might be shorter if the fuzzer input * is not sufficiently long. * @@ -62,6 +63,7 @@ public interface FuzzedDataProvider { /** * Consumes a {@code byte} array from the fuzzer input. + * *

The array will usually have length {@code length}, but might be shorter if the fuzzer input * is not sufficiently long. * @@ -72,6 +74,7 @@ public interface FuzzedDataProvider { /** * Consumes the remaining fuzzer input as a {@code byte} array. + * *

Note: After calling this method, further calls to methods of this interface will * return fixed values only. * @@ -97,6 +100,7 @@ public interface FuzzedDataProvider { /** * Consumes a {@code short} array from the fuzzer input. + * *

The array will usually have length {@code length}, but might be shorter if the fuzzer input * is not sufficiently long. * @@ -123,6 +127,7 @@ public interface FuzzedDataProvider { /** * Consumes an {@code int} array from the fuzzer input. + * *

The array will usually have length {@code length}, but might be shorter if the fuzzer input * is not sufficiently long. * @@ -149,6 +154,7 @@ public interface FuzzedDataProvider { /** * Consumes a {@code long} array from the fuzzer input. + * *

The array will usually have length {@code length}, but might be shorter if the fuzzer input * is not sufficiently long. * @@ -213,9 +219,7 @@ public interface FuzzedDataProvider { */ double consumeProbabilityDouble(); - /** - * Consumes a {@code char} from the fuzzer input. - */ + /** Consumes a {@code char} from the fuzzer input. */ char consumeChar(); /** @@ -227,13 +231,12 @@ public interface FuzzedDataProvider { */ char consumeChar(char min, char max); - /** - * Consumes a {@code char} from the fuzzer input that is never a UTF-16 surrogate character. - */ + /** Consumes a {@code char} from the fuzzer input that is never a UTF-16 surrogate character. */ char consumeCharNoSurrogates(); /** * Consumes a {@link String} from the fuzzer input. + * *

The returned string may be of any length between 0 and {@code maxLength}, even if there is * more fuzzer input available. * @@ -244,6 +247,7 @@ public interface FuzzedDataProvider { /** * Consumes the remaining fuzzer input as a {@link String}. + * *

Note: After calling this method, further calls to methods of this interface will * return fixed values only. * @@ -253,17 +257,19 @@ public interface FuzzedDataProvider { /** * Consumes an ASCII-only {@link String} from the fuzzer input. + * *

The returned string may be of any length between 0 and {@code maxLength}, even if there is * more fuzzer input available. * * @param maxLength the maximum length of the string * @return a {@link String} of length between 0 and {@code maxLength} (inclusive) that contains - * only ASCII characters + * only ASCII characters */ String consumeAsciiString(int maxLength); /** * Consumes the remaining fuzzer input as an ASCII-only {@link String}. + * *

Note: After calling this method, further calls to methods of this interface will * return fixed values only. * @@ -280,6 +286,7 @@ public interface FuzzedDataProvider { /** * Picks an element from {@code collection} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param collection the {@link Collection} to pick an element from. @@ -287,7 +294,7 @@ public interface FuzzedDataProvider { * @return an element from {@code collection} chosen based on the fuzzer input */ @SuppressWarnings("unchecked") - default T pickValue(Collection collection) { + default T pickValue(Collection collection) { int size = collection.size(); if (size == 0) { throw new IllegalArgumentException("collection is empty"); @@ -301,13 +308,14 @@ default T pickValue(Collection collection) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. * @param the type of the array element * @return an element from {@code array} chosen based on the fuzzer input */ - default T pickValue(T[] array) { + default T pickValue(T[] array) { if (array.length == 0) { throw new IllegalArgumentException("array is empty"); } @@ -316,6 +324,7 @@ default T pickValue(T[] array) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -330,6 +339,7 @@ default boolean pickValue(boolean[] array) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -344,6 +354,7 @@ default byte pickValue(byte[] array) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -358,6 +369,7 @@ default short pickValue(short[] array) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -372,6 +384,7 @@ default int pickValue(int[] array) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -386,6 +399,7 @@ default long pickValue(long[] array) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -400,6 +414,7 @@ default double pickValue(double[] array) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -414,6 +429,7 @@ default float pickValue(float[] array) { /** * Picks an element from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -428,6 +444,7 @@ default char pickValue(char[] array) { /** * Picks {@code numOfElements} elements from {@code collection} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param collection the {@link Collection} to pick an element from. @@ -436,7 +453,7 @@ default char pickValue(char[] array) { * @return an array of size {@code numOfElements} from {@code collection} chosen based on the * fuzzer input */ - default List pickValues(Collection collection, int numOfElements) { + default List pickValues(Collection collection, int numOfElements) { int size = collection.size(); if (size == 0) { throw new IllegalArgumentException("collection is empty"); @@ -457,6 +474,7 @@ default List pickValues(Collection collection, int numOfElements) { /** * Picks {@code numOfElements} elements from {@code array} based on the fuzzer input. + * *

Note: The distribution of picks is not perfectly uniform. * * @param array the array to pick an element from. @@ -465,7 +483,7 @@ default List pickValues(Collection collection, int numOfElements) { * @return an array of size {@code numOfElements} from {@code array} chosen based on the fuzzer * input */ - default List pickValues(T[] array, int numOfElements) { + default List pickValues(T[] array, int numOfElements) { if (array.length == 0) { throw new IllegalArgumentException("array is empty"); } diff --git a/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueCritical.java b/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueCritical.java index fbde853b1..1e7059957 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueCritical.java +++ b/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueCritical.java @@ -17,10 +17,10 @@ /** * Thrown to indicate that a fuzz target has detected a critical severity security issue rather than * a normal bug. - *

- * There is only a semantical but no functional difference between throwing exceptions of this type - * or any other. However, automated fuzzing platforms can use the extra information to handle the - * detected issues appropriately. + * + *

There is only a semantical but no functional difference between throwing exceptions of this + * type or any other. However, automated fuzzing platforms can use the extra information to handle + * the detected issues appropriately. */ public class FuzzerSecurityIssueCritical extends RuntimeException { public FuzzerSecurityIssueCritical() {} diff --git a/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueHigh.java b/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueHigh.java index 05837b0e7..cd158ee4d 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueHigh.java +++ b/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueHigh.java @@ -17,10 +17,10 @@ /** * Thrown to indicate that a fuzz target has detected a high severity security issue rather than a * normal bug. - *

- * There is only a semantical but no functional difference between throwing exceptions of this type - * or any other. However, automated fuzzing platforms can use the extra information to handle the - * detected issues appropriately. + * + *

There is only a semantical but no functional difference between throwing exceptions of this + * type or any other. However, automated fuzzing platforms can use the extra information to handle + * the detected issues appropriately. */ public class FuzzerSecurityIssueHigh extends RuntimeException { public FuzzerSecurityIssueHigh() {} diff --git a/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueLow.java b/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueLow.java index 364b3afb6..122937af9 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueLow.java +++ b/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueLow.java @@ -18,9 +18,9 @@ * Thrown to indicate that a fuzz target has detected a low severity security issue rather than a * normal bug. * - * There is only a semantical but no functional difference between throwing exceptions of this type - * or any other. However, automated fuzzing platforms can use the extra information to handle the - * detected issues appropriately. + *

There is only a semantical but no functional difference between throwing exceptions of this + * type or any other. However, automated fuzzing platforms can use the extra information to handle + * the detected issues appropriately. */ public class FuzzerSecurityIssueLow extends RuntimeException { public FuzzerSecurityIssueLow() {} diff --git a/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueMedium.java b/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueMedium.java index be7c8c8f7..2acd7df72 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueMedium.java +++ b/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueMedium.java @@ -17,10 +17,10 @@ /** * Thrown to indicate that a fuzz target has detected a medium severity security issue rather than a * normal bug. - *

- * There is only a semantical but no functional difference between throwing exceptions of this type - * or any other. However, automated fuzzing platforms can use the extra information to handle the - * detected issues appropriately. + * + *

There is only a semantical but no functional difference between throwing exceptions of this + * type or any other. However, automated fuzzing platforms can use the extra information to handle + * the detected issues appropriately. */ public class FuzzerSecurityIssueMedium extends RuntimeException { public FuzzerSecurityIssueMedium() {} diff --git a/src/main/java/com/code_intelligence/jazzer/api/HookType.java b/src/main/java/com/code_intelligence/jazzer/api/HookType.java index 8ed4337fa..ef25b580f 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/HookType.java +++ b/src/main/java/com/code_intelligence/jazzer/api/HookType.java @@ -14,9 +14,7 @@ package com.code_intelligence.jazzer.api; -/** - * The type of a {@link MethodHook}. - */ +/** The type of a {@link MethodHook}. */ // Note: The order of entries is important and is used during instrumentation. public enum HookType { BEFORE, diff --git a/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java b/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java index aad9ae012..f5459bd56 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java +++ b/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java @@ -20,9 +20,7 @@ import java.lang.reflect.InvocationTargetException; import java.security.SecureRandom; -/** - * Static helper methods that hooks can use to provide feedback to the fuzzer. - */ +/** Static helper methods that hooks can use to provide feedback to the fuzzer. */ public final class Jazzer { private static final Class JAZZER_INTERNAL; @@ -43,27 +41,33 @@ public final class Jazzer { try { jazzerInternal = Class.forName("com.code_intelligence.jazzer.runtime.JazzerInternal"); MethodType onFuzzTargetReadyType = MethodType.methodType(void.class, Runnable.class); - onFuzzTargetReady = MethodHandles.publicLookup().findStatic( - jazzerInternal, "registerOnFuzzTargetReadyCallback", onFuzzTargetReadyType); + onFuzzTargetReady = + MethodHandles.publicLookup() + .findStatic( + jazzerInternal, "registerOnFuzzTargetReadyCallback", onFuzzTargetReadyType); Class traceDataFlowNativeCallbacks = Class.forName("com.code_intelligence.jazzer.runtime.TraceDataFlowNativeCallbacks"); // Use method handles for hints as the calls are potentially performance critical. MethodType traceStrcmpType = MethodType.methodType(void.class, String.class, String.class, int.class, int.class); - traceStrcmp = MethodHandles.publicLookup().findStatic( - traceDataFlowNativeCallbacks, "traceStrcmp", traceStrcmpType); + traceStrcmp = + MethodHandles.publicLookup() + .findStatic(traceDataFlowNativeCallbacks, "traceStrcmp", traceStrcmpType); MethodType traceStrstrType = MethodType.methodType(void.class, String.class, String.class, int.class); - traceStrstr = MethodHandles.publicLookup().findStatic( - traceDataFlowNativeCallbacks, "traceStrstr", traceStrstrType); + traceStrstr = + MethodHandles.publicLookup() + .findStatic(traceDataFlowNativeCallbacks, "traceStrstr", traceStrstrType); MethodType traceMemcmpType = MethodType.methodType(void.class, byte[].class, byte[].class, int.class, int.class); - traceMemcmp = MethodHandles.publicLookup().findStatic( - traceDataFlowNativeCallbacks, "traceMemcmp", traceMemcmpType); + traceMemcmp = + MethodHandles.publicLookup() + .findStatic(traceDataFlowNativeCallbacks, "traceMemcmp", traceMemcmpType); MethodType tracePcIndirType = MethodType.methodType(void.class, int.class, int.class); - tracePcIndir = MethodHandles.publicLookup().findStatic( - traceDataFlowNativeCallbacks, "tracePcIndir", tracePcIndirType); + tracePcIndir = + MethodHandles.publicLookup() + .findStatic(traceDataFlowNativeCallbacks, "tracePcIndir", tracePcIndirType); } catch (ClassNotFoundException ignore) { // Not running in the context of the agent. This is fine as long as no methods are called on // this class. @@ -85,23 +89,21 @@ public final class Jazzer { private Jazzer() {} /** - * A 32-bit random number that hooks can use to make pseudo-random choices - * between multiple possible mutations they could guide the fuzzer towards. - * Hooks must not base the decision whether or not to report a finding - * on this number as this will make findings non-reproducible. - *

- * This is the same number that libFuzzer uses as a seed internally, which - * makes it possible to deterministically reproduce a previous fuzzing run by - * supplying the seed value printed by libFuzzer as the value of the - * {@code -seed}. + * A 32-bit random number that hooks can use to make pseudo-random choices between multiple + * possible mutations they could guide the fuzzer towards. Hooks must not base the decision + * whether or not to report a finding on this number as this will make findings non-reproducible. + * + *

This is the same number that libFuzzer uses as a seed internally, which makes it possible to + * deterministically reproduce a previous fuzzing run by supplying the seed value printed by + * libFuzzer as the value of the {@code -seed}. */ public static final int SEED = getLibFuzzerSeed(); /** * Instructs the fuzzer to guide its mutations towards making {@code current} equal to {@code * target}. - *

- * If the relation between the raw fuzzer input and the value of {@code current} is relatively + * + *

If the relation between the raw fuzzer input and the value of {@code current} is relatively * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to * achieve equality. * @@ -123,8 +125,8 @@ public static void guideTowardsEquality(String current, String target, int id) { /** * Instructs the fuzzer to guide its mutations towards making {@code current} equal to {@code * target}. - *

- * If the relation between the raw fuzzer input and the value of {@code current} is relatively + * + *

If the relation between the raw fuzzer input and the value of {@code current} is relatively * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to * achieve equality. * @@ -146,8 +148,8 @@ public static void guideTowardsEquality(byte[] current, byte[] target, int id) { /** * Instructs the fuzzer to guide its mutations towards making {@code haystack} contain {@code * needle} as a substring. - *

- * If the relation between the raw fuzzer input and the value of {@code haystack} is relatively + * + *

If the relation between the raw fuzzer input and the value of {@code haystack} is relatively * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to * satisfy the substring check. * @@ -170,14 +172,14 @@ public static void guideTowardsContainment(String haystack, String needle, int i /** * Instructs the fuzzer to attain as many possible values for the absolute value of {@code state} * as possible. - *

- * Call this function from a fuzz target or a hook to help the fuzzer track partial progress + * + *

Call this function from a fuzz target or a hook to help the fuzzer track partial progress * (e.g. by passing the length of a common prefix of two lists that should become equal) or * explore different values of state that is not directly related to code coverage (see the * MazeFuzzer example). - *

- * Note: This hint only takes effect if the fuzzer is run with the argument - * {@code -use_value_profile=1}. + * + *

Note: This hint only takes effect if the fuzzer is run with the argument {@code + * -use_value_profile=1}. * * @param state a numeric encoding of a state that should be varied by the fuzzer * @param id a (probabilistically) unique identifier for this particular state hint @@ -215,9 +217,10 @@ public static void exploreState(byte state, int id) { /** * Make Jazzer report the provided {@link Throwable} as a finding. - *

- * Note: This method must only be called from a method hook. In a - * fuzz target, simply throw an exception to trigger a finding. + * + *

Note: This method must only be called from a method hook. In a fuzz target, simply + * throw an exception to trigger a finding. + * * @param finding the finding that Jazzer should report */ public static void reportFindingFromHook(Throwable finding) { @@ -234,8 +237,8 @@ public static void reportFindingFromHook(Throwable finding) { /** * Register a callback to be executed right before the fuzz target is executed for the first time. - *

- * This can be used to disable hooks until after Jazzer has been fully initializing, e.g. to + * + *

This can be used to disable hooks until after Jazzer has been fully initializing, e.g. to * prevent Jazzer internals from triggering hooks on Java standard library classes. * * @param callback the callback to execute @@ -263,6 +266,6 @@ private static int getLibFuzzerSeed() { // Rethrows a (possibly checked) exception while avoiding a throws declaration. @SuppressWarnings("unchecked") private static void rethrowUnchecked(Throwable t) throws T { - throw(T) t; + throw (T) t; } } diff --git a/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java b/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java index 3a1c5f396..1be8af961 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java +++ b/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java @@ -23,105 +23,100 @@ import java.lang.invoke.MethodType; /** - * Registers the annotated method as a hook that should run before, instead or - * after the method specified by the annotation parameters. - *

- * Depending on {@link #type()} this method will be called after, instead or - * before every call to the target method and has - * access to its parameters and return value. The target method is specified by - * {@link #targetClassName()} and {@link #targetMethod()}. In case of an - * overloaded method, {@link #targetMethodDescriptor()} can be used to restrict - * the application of the hook to a particular overload. - *

- * The signature of the annotated method must be as follows (this does not - * restrict the method name and parameter names, which are arbitrary), - * depending on the value of {@link #type()}: + * Registers the annotated method as a hook that should run before, instead or after the method + * specified by the annotation parameters. + * + *

Depending on {@link #type()} this method will be called after, instead or before every call to + * the target method and has access to its parameters and return value. The target method is + * specified by {@link #targetClassName()} and {@link #targetMethod()}. In case of an overloaded + * method, {@link #targetMethodDescriptor()} can be used to restrict the application of the hook to + * a particular overload. + * + *

The signature of the annotated method must be as follows (this does not restrict the method + * name and parameter names, which are arbitrary), depending on the value of {@link #type()}: * *

- *
{@link HookType#BEFORE} - *
- *
{@code
+ *   
{@link HookType#BEFORE} + *
+ *
{@code
  * public static void hook(MethodHandle method, Object thisObject, Object[] arguments, int hookId)
  * }
- * Arguments: - *

    - *
  • {@code method}: A {@link java.lang.invoke.MethodHandle} representing the - * original method. The original method can be invoked via - * {@link java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This - * requires passing {@code thisObject} as the first argument if the method is - * not static. This argument can be {@code null}. - *
  • {@code thisObject}: An {@link Object} containing the implicit - * {@code this} argument to the original method. If the original method is - * static, this argument will be {@code null}. - *
  • {@code arguments}: An array of {@link Object}s containing the arguments - * passed to the original method. Primitive types (e.g. {@code boolean}) will be - * wrapped into their corresponding wrapper type (e.g. {@link Boolean}). - *
  • {@code hookId}: A random {@code int} identifying the particular call - * site.This can be used to derive additional coverage information. - *
- * - *
{@link HookType#REPLACE} - *
- *
{@code
+ *       Arguments:
+ *       

+ *

    + *
  • {@code method}: A {@link java.lang.invoke.MethodHandle} representing the original + * method. The original method can be invoked via {@link + * java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This requires passing + * {@code thisObject} as the first argument if the method is not static. This argument + * can be {@code null}. + *
  • {@code thisObject}: An {@link Object} containing the implicit {@code this} argument + * to the original method. If the original method is static, this argument will be + * {@code null}. + *
  • {@code arguments}: An array of {@link Object}s containing the arguments passed to the + * original method. Primitive types (e.g. {@code boolean}) will be wrapped into their + * corresponding wrapper type (e.g. {@link Boolean}). + *
  • {@code hookId}: A random {@code int} identifying the particular call site.This can be + * used to derive additional coverage information. + *
+ *
{@link HookType#REPLACE} + *
+ *
{@code
  * public static Object hook(MethodHandle method, Object thisObject, Object[] arguments, int hookId)
  * }
- * The return type may alternatively be taken to be the exact return type of - * target method or a wrapper type thereof. The returned object will be casted - * and unwrapped automatically. - *

- * Arguments: - *

    - *
  • {@code method}: A {@link java.lang.invoke.MethodHandle} representing the - * original method. The original method can be invoked via - * {@link java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This - * requires passing {@code thisObject} as the first argument if the method is - * not static. This argument can be {@code null}. - *
  • {@code thisObject}: An {@link Object} containing the implicit - * {@code this} argument to the original method. If the original method is - * static, this argument will be {@code null}. - *
  • {@code arguments}: An array of {@link Object}s containing the arguments - * passed to the original method. Primitive types (e.g. {@code boolean}) will be - * wrapped into their corresponding wrapper type (e.g. {@link Boolean}). - *
  • {@code hookId}: A random {@code int} identifying the particular call - * site.This can be used to derive additional coverage information. - *

- *

- * Return value: the value that should take the role of the value the target - * method would have returned - *

- *

{@link HookType#AFTER} - *
- *
{@code
+ *       The return type may alternatively be taken to be the exact return type of target method or
+ *       a wrapper type thereof. The returned object will be casted and unwrapped automatically.
+ *       

Arguments: + *

+ *

    + *
  • {@code method}: A {@link java.lang.invoke.MethodHandle} representing the original + * method. The original method can be invoked via {@link + * java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This requires passing + * {@code thisObject} as the first argument if the method is not static. This argument + * can be {@code null}. + *
  • {@code thisObject}: An {@link Object} containing the implicit {@code this} argument + * to the original method. If the original method is static, this argument will be + * {@code null}. + *
  • {@code arguments}: An array of {@link Object}s containing the arguments passed to the + * original method. Primitive types (e.g. {@code boolean}) will be wrapped into their + * corresponding wrapper type (e.g. {@link Boolean}). + *
  • {@code hookId}: A random {@code int} identifying the particular call site.This can be + * used to derive additional coverage information. + *
+ *

+ *

Return value: the value that should take the role of the value the target method would + * have returned + *

+ *

{@link HookType#AFTER} + *
+ *
{@code
  * public static void hook(MethodHandle method, Object thisObject, Object[] arguments, int hookId,
  * Object returnValue)
  * }
- * Arguments: - *

    - *
  • {@code method}: A {@link java.lang.invoke.MethodHandle} representing the - * original method. The original method can be invoked via - * {@link java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This - * requires passing {@code thisObject} as the first argument if the method is - * not static. This argument can be {@code null}. - *
  • {@code thisObject}: An {@link Object} containing the implicit - * {@code this} argument to the original method. If the original method is - * static, this argument will be {@code null}. - *
  • {@code arguments}: An array of {@link Object}s containing the arguments - * passed to the original method. Primitive types (e.g. {@code boolean}) will be - * wrapped into their corresponding wrapper type (e.g. {@link Boolean}). - *
  • {@code hookId}: A random {@code int} identifying the particular call - * site.This can be used to derive additional coverage information. - *
  • {@code returnValue}: An {@link Object} containing the return value of the - * invocation of the original method. Primitive types (e.g. {@code boolean}) - * will be wrapped into their corresponding wrapper type (e.g. {@link Boolean}). - * If the original method has return type {@code void}, this value will be - * {@code null}. - *

    - * Multiple {@link HookType#BEFORE} and {@link HookType#AFTER} hooks are - * allowed to reference the same target method. Exclusively one - * {@link HookType#REPLACE} hook may reference a target method, no other types - * allowed. Attention must be paid to not guide the Fuzzer in different - * directions via {@link Jazzer}'s {@code guideTowardsXY} methods in the - * different hooks. + * Arguments: + *

    + *

      + *
    • {@code method}: A {@link java.lang.invoke.MethodHandle} representing the original + * method. The original method can be invoked via {@link + * java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This requires passing + * {@code thisObject} as the first argument if the method is not static. This argument + * can be {@code null}. + *
    • {@code thisObject}: An {@link Object} containing the implicit {@code this} argument + * to the original method. If the original method is static, this argument will be + * {@code null}. + *
    • {@code arguments}: An array of {@link Object}s containing the arguments passed to the + * original method. Primitive types (e.g. {@code boolean}) will be wrapped into their + * corresponding wrapper type (e.g. {@link Boolean}). + *
    • {@code hookId}: A random {@code int} identifying the particular call site.This can be + * used to derive additional coverage information. + *
    • {@code returnValue}: An {@link Object} containing the return value of the invocation + * of the original method. Primitive types (e.g. {@code boolean}) will be wrapped into + * their corresponding wrapper type (e.g. {@link Boolean}). If the original method has + * return type {@code void}, this value will be {@code null}. + *

      Multiple {@link HookType#BEFORE} and {@link HookType#AFTER} hooks are allowed to + * reference the same target method. Exclusively one {@link HookType#REPLACE} hook may + * reference a target method, no other types allowed. Attention must be paid to not + * guide the Fuzzer in different directions via {@link Jazzer}'s {@code guideTowardsXY} + * methods in the different hooks. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @@ -130,64 +125,69 @@ public @interface MethodHook { /** * The time at which the annotated method should be called. - *

      - * If this is {@link HookType#BEFORE}, the annotated method will be called - * before the target method and has access to its arguments. - *

      - * If this is {@link HookType#REPLACE}, the annotated method will be called - * instead of the target method. It has access to its arguments and can - * return a value that will replace the target method's return value. - *

      - * If this is {@link HookType#AFTER}, the annotated method will be called - * after the target method and has access to its arguments and return - * value. + * + *

      If this is {@link HookType#BEFORE}, the annotated method will be called before the target + * method and has access to its arguments. + * + *

      If this is {@link HookType#REPLACE}, the annotated method will be called instead of the + * target method. It has access to its arguments and can return a value that will replace the + * target method's return value. + * + *

      If this is {@link HookType#AFTER}, the annotated method will be called after the target + * method and has access to its arguments and return value. * * @return when the hook should be called */ HookType type(); /** - * The name of the class that contains the method that should be hooked, - * as returned by {@link Class#getName()}. + * The name of the class that contains the method that should be hooked, as returned by {@link + * Class#getName()}. + * + *

      If an interface or abstract class is specified, also calls to all implementations and + * subclasses available on the classpath during startup are hooked, respectively. Interfaces and + * subclasses are not taken into account for concrete classes. + * + *

      Examples: + * *

      - * If an interface or abstract class is specified, also calls to all - * implementations and subclasses available on the classpath during startup - * are hooked, respectively. Interfaces and subclasses are not taken into - * account for concrete classes. + * + *

        + *
      • {@link String}: {@code "java.lang.String"} + *
      • {@link java.nio.file.FileSystem}: {@code "java.nio.file.FileSystem"} + *
      + * *

      - * Examples: - *

        - *
      • {@link String}: {@code "java.lang.String"} - *
      • {@link java.nio.file.FileSystem}: {@code "java.nio.file.FileSystem"} - *

      * * @return the name of the class containing the method to be hooked */ String targetClassName(); /** - * The name of the method to be hooked. Use {@code ""} for - * constructors. + * The name of the method to be hooked. Use {@code ""} for constructors. + * + *

      Examples: + * + *

      + * + *

        + *
      • {@link String#equals(Object)}: {@code "equals"} + *
      • {@link String#String()}: {@code ""} + *
      + * *

      - * Examples: - *

        - *
      • {@link String#equals(Object)}: {@code "equals"} - *
      • {@link String#String()}: {@code ""} - *

      * * @return the name of the method to be hooked */ String targetMethod(); /** - * The descriptor of the method to be hooked. This is only needed if there - * are multiple methods with the same name and not all of them should be - * hooked. - *

      - * The descriptor of a method is an internal representation of the method's - * signature, which includes the types of its parameters and its return - * value. For more information on descriptors, see the - * JVM + * The descriptor of the method to be hooked. This is only needed if there are multiple methods + * with the same name and not all of them should be hooked. + * + *

      The descriptor of a method is an internal representation of the method's signature, which + * includes the types of its parameters and its return value. For more information on descriptors, + * see the JVM * Specification, Section 4.3.3 and {@link MethodType#toMethodDescriptorString()} * * @return the descriptor of the method to be hooked @@ -196,10 +196,10 @@ /** * Array of additional classes to hook. - *

      - * Hooks are applied on call sites. This means that classes calling the one - * defined in this annotation need to be instrumented to actually execute - * the hook. This property can be used to hook normally ignored classes. + * + *

      Hooks are applied on call sites. This means that classes calling the one defined in this + * annotation need to be instrumented to actually execute the hook. This property can be used to + * hook normally ignored classes. * * @return fully qualified class names to hook */ diff --git a/src/main/java/com/code_intelligence/jazzer/api/MethodHooks.java b/src/main/java/com/code_intelligence/jazzer/api/MethodHooks.java index 7eec24b37..30218ba62 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/MethodHooks.java +++ b/src/main/java/com/code_intelligence/jazzer/api/MethodHooks.java @@ -20,9 +20,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Internal helper allowing to apply multiple {@link MethodHook} annotations to the same method. - */ +/** Internal helper allowing to apply multiple {@link MethodHook} annotations to the same method. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented diff --git a/src/main/java/com/code_intelligence/jazzer/api/SilentCloseable.java b/src/main/java/com/code_intelligence/jazzer/api/SilentCloseable.java index 3f2d6e3f3..4ab741c94 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/SilentCloseable.java +++ b/src/main/java/com/code_intelligence/jazzer/api/SilentCloseable.java @@ -17,9 +17,10 @@ package com.code_intelligence.jazzer.api; /** - * A specialization of {@link AutoCloseable} without a {@code throws} declarations on - * {@link #close()}. + * A specialization of {@link AutoCloseable} without a {@code throws} declarations on {@link + * #close()}. */ public interface SilentCloseable extends AutoCloseable { - @Override void close(); + @Override + void close(); } diff --git a/src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java b/src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java index a45a474b7..17ca2c49d 100644 --- a/src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java +++ b/src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java @@ -27,13 +27,16 @@ class AccessibleObjectLookup { private static final Comparator> STABLE_CLASS_COMPARATOR = Comparator.comparing(Class::getName); private static final Comparator STABLE_EXECUTABLE_COMPARATOR = - Comparator.comparing(Executable::getName).thenComparing(executable -> { - if (executable instanceof Method) { - return org.objectweb.asm.Type.getMethodDescriptor((Method) executable); - } else { - return org.objectweb.asm.Type.getConstructorDescriptor((Constructor) executable); - } - }); + Comparator.comparing(Executable::getName) + .thenComparing( + executable -> { + if (executable instanceof Method) { + return org.objectweb.asm.Type.getMethodDescriptor((Method) executable); + } else { + return org.objectweb.asm.Type.getConstructorDescriptor( + (Constructor) executable); + } + }); private final Class referenceClass; @@ -46,30 +49,30 @@ Class[] getAccessibleClasses(Class type) { .distinct() .filter(this::isAccessible) .sorted(STABLE_CLASS_COMPARATOR) - .toArray(Class[] ::new); + .toArray(Class[]::new); } Constructor[] getAccessibleConstructors(Class type) { // Neither of getDeclaredConstructors and getConstructors is a superset of the other: While // getDeclaredConstructors returns constructors with all visibility modifiers, it does not // return the implicit default constructor. - return Stream - .concat( + return Stream.concat( Arrays.stream(type.getDeclaredConstructors()), Arrays.stream(type.getConstructors())) .distinct() .filter(this::isAccessible) .sorted(STABLE_EXECUTABLE_COMPARATOR) - .filter(constructor -> { - try { - constructor.setAccessible(true); - return true; - } catch (Exception e) { - // Can't make the constructor accessible, e.g. because it is in a standard library - // module. We can't do anything about this, so we skip the constructor. - return false; - } - }) - .toArray(Constructor[] ::new); + .filter( + constructor -> { + try { + constructor.setAccessible(true); + return true; + } catch (Exception e) { + // Can't make the constructor accessible, e.g. because it is in a standard library + // module. We can't do anything about this, so we skip the constructor. + return false; + } + }) + .toArray(Constructor[]::new); } Method[] getAccessibleMethods(Class type) { @@ -77,17 +80,19 @@ Method[] getAccessibleMethods(Class type) { .distinct() .filter(this::isAccessible) .sorted(STABLE_EXECUTABLE_COMPARATOR) - .filter(method -> { - try { - method.setAccessible(true); - return true; - } catch (Exception e) { - // Can't make the method accessible, e.g. because it is in a standard library module. We - // can't do anything about this, so we skip the method. - return false; - } - }) - .toArray(Method[] ::new); + .filter( + method -> { + try { + method.setAccessible(true); + return true; + } catch (Exception e) { + // Can't make the method accessible, e.g. because it is in a standard library + // module. We + // can't do anything about this, so we skip the method. + return false; + } + }) + .toArray(Method[]::new); } boolean isAccessible(Class clazz, int modifiers) { diff --git a/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzCodegenVisitor.java b/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzCodegenVisitor.java index 2ea4e9b07..debe70c84 100644 --- a/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzCodegenVisitor.java +++ b/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzCodegenVisitor.java @@ -72,7 +72,8 @@ public String uniqueVariableName() { static String escapeForLiteral(String string) { // The list of escape sequences is taken from: // https://docs.oracle.com/javase/tutorial/java/data/characters.html - return string.replace("\\", "\\\\") + return string + .replace("\\", "\\\\") .replace("\t", "\\t") .replace("\b", "\\b") .replace("\n", "\\n") diff --git a/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java b/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java index a94b385d2..aa7c91042 100644 --- a/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java +++ b/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java @@ -14,12 +14,12 @@ package com.code_intelligence.jazzer.autofuzz; -/** - * An error indicating an internal error in the autofuzz functionality. - */ +/** An error indicating an internal error in the autofuzz functionality. */ public class AutofuzzError extends Error { - private static final String MESSAGE_TRAILER = String.format( - "%nPlease file an issue at:%n https://github.com/CodeIntelligenceTesting/jazzer/issues/new/choose"); + private static final String MESSAGE_TRAILER = + String.format( + "%nPlease file an issue at:%n " + + " https://github.com/CodeIntelligenceTesting/jazzer/issues/new/choose"); public AutofuzzError(String message) { super(message + MESSAGE_TRAILER); diff --git a/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java b/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java index 885ebfbfd..eda961858 100644 --- a/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java +++ b/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java @@ -40,12 +40,13 @@ import java.util.stream.Stream; public final class FuzzTarget { - private static final String AUTOFUZZ_REPRODUCER_TEMPLATE = "public class Crash_%1$s {\n" - + " public static void main(String[] args) throws Throwable {\n" - + " Crash_%1$s.class.getClassLoader().setDefaultAssertionStatus(true);\n" - + " %2$s;\n" - + " }\n" - + "}"; + private static final String AUTOFUZZ_REPRODUCER_TEMPLATE = + "public class Crash_%1$s {\n" + + " public static void main(String[] args) throws Throwable {\n" + + " Crash_%1$s.class.getClassLoader().setDefaultAssertionStatus(true);\n" + + " %2$s;\n" + + " }\n" + + "}"; private static final long MAX_EXECUTIONS_WITHOUT_INVOCATION = 100; private static Meta meta; @@ -59,7 +60,8 @@ public final class FuzzTarget { public static void fuzzerInitialize(String[] args) { if (args.length == 0 || !args[0].contains("::")) { Log.error( - "Expected the argument to --autofuzz to be a method reference (e.g. System.out::println)"); + "Expected the argument to --autofuzz to be a method reference (e.g." + + " System.out::println)"); System.exit(1); } String methodSignature = args[0]; @@ -95,9 +97,11 @@ public static void fuzzerInitialize(String[] args) { } catch (ClassNotFoundException e) { int classSeparatorIndex = targetClassName.lastIndexOf("."); if (classSeparatorIndex == -1) { - Log.error(String.format( - "Failed to find class %s for autofuzz, please ensure it is contained in the classpath specified with --cp and specify the full package name", - className)); + Log.error( + String.format( + "Failed to find class %s for autofuzz, please ensure it is contained in the" + + " classpath specified with --cp and specify the full package name", + className)); System.exit(1); return; } @@ -118,20 +122,24 @@ public static void fuzzerInitialize(String[] args) { // methods. This is necessary e.g. when using Autofuzz on a package-private JUnit @FuzzTest // method. if (isConstructor) { - executables = Arrays.stream(lookup.getAccessibleConstructors(targetClass)) - .filter(constructor -> constructor.getDeclaringClass().equals(targetClass)) - .filter(constructor - -> (descriptor == null && Modifier.isPublic(constructor.getModifiers())) - || Utils.getReadableDescriptor(constructor).equals(descriptor)) - .toArray(Executable[] ::new); + executables = + Arrays.stream(lookup.getAccessibleConstructors(targetClass)) + .filter(constructor -> constructor.getDeclaringClass().equals(targetClass)) + .filter( + constructor -> + (descriptor == null && Modifier.isPublic(constructor.getModifiers())) + || Utils.getReadableDescriptor(constructor).equals(descriptor)) + .toArray(Executable[]::new); } else { - executables = Arrays.stream(lookup.getAccessibleMethods(targetClass)) - .filter(method -> method.getDeclaringClass().equals(targetClass)) - .filter(method - -> method.getName().equals(methodName) - && ((descriptor == null && Modifier.isPublic(method.getModifiers())) - || Utils.getReadableDescriptor(method).equals(descriptor))) - .toArray(Executable[] ::new); + executables = + Arrays.stream(lookup.getAccessibleMethods(targetClass)) + .filter(method -> method.getDeclaringClass().equals(targetClass)) + .filter( + method -> + method.getName().equals(methodName) + && ((descriptor == null && Modifier.isPublic(method.getModifiers())) + || Utils.getReadableDescriptor(method).equals(descriptor))) + .toArray(Executable[]::new); } if (executables.length == 0) { if (isConstructor) { @@ -139,80 +147,101 @@ public static void fuzzerInitialize(String[] args) { Log.error( String.format("Failed to find constructors in class %s for autofuzz.%n", className)); } else { - Log.error(String.format( - "Failed to find constructors with signature %s in class %s for autofuzz.%n" - + "Public constructors declared by the class:%n%s", - descriptor, className, - Arrays.stream(lookup.getAccessibleConstructors(targetClass)) - .filter(constructor -> Modifier.isPublic(constructor.getModifiers())) - .filter(constructor -> constructor.getDeclaringClass().equals(targetClass)) - .map(method - -> String.format("%s::new%s", method.getDeclaringClass().getName(), - Utils.getReadableDescriptor(method))) - .distinct() - .collect(Collectors.joining(System.lineSeparator())))); + Log.error( + String.format( + "Failed to find constructors with signature %s in class %s for autofuzz.%n" + + "Public constructors declared by the class:%n%s", + descriptor, + className, + Arrays.stream(lookup.getAccessibleConstructors(targetClass)) + .filter(constructor -> Modifier.isPublic(constructor.getModifiers())) + .filter(constructor -> constructor.getDeclaringClass().equals(targetClass)) + .map( + method -> + String.format( + "%s::new%s", + method.getDeclaringClass().getName(), + Utils.getReadableDescriptor(method))) + .distinct() + .collect(Collectors.joining(System.lineSeparator())))); } } else { if (descriptor == null) { - Log.error(String.format("Failed to find methods named %s in class %s for autofuzz.%n" - + "Public methods declared by the class:%n%s", - methodName, className, - Arrays.stream(lookup.getAccessibleMethods(targetClass)) - .filter(method -> Modifier.isPublic(method.getModifiers())) - .filter(method -> method.getDeclaringClass().equals(targetClass)) - .map(method - -> String.format( - "%s::%s", method.getDeclaringClass().getName(), method.getName())) - .distinct() - .collect(Collectors.joining(System.lineSeparator())))); + Log.error( + String.format( + "Failed to find methods named %s in class %s for autofuzz.%n" + + "Public methods declared by the class:%n%s", + methodName, + className, + Arrays.stream(lookup.getAccessibleMethods(targetClass)) + .filter(method -> Modifier.isPublic(method.getModifiers())) + .filter(method -> method.getDeclaringClass().equals(targetClass)) + .map( + method -> + String.format( + "%s::%s", method.getDeclaringClass().getName(), method.getName())) + .distinct() + .collect(Collectors.joining(System.lineSeparator())))); } else { - Log.error(String.format( - "Failed to find public methods named %s with signature %s in class %s for autofuzz.%n" - + "Public methods with that name:%n%s", - methodName, descriptor, className, - Arrays.stream(lookup.getAccessibleMethods(targetClass)) - .filter(method -> Modifier.isPublic(method.getModifiers())) - .filter(method -> method.getDeclaringClass().equals(targetClass)) - .filter(method -> method.getName().equals(methodName)) - .map(method - -> String.format("%s::%s%s", method.getDeclaringClass().getName(), - method.getName(), Utils.getReadableDescriptor(method))) - .distinct() - .collect(Collectors.joining(System.lineSeparator())))); + Log.error( + String.format( + "Failed to find public methods named %s with signature %s in class %s for" + + " autofuzz.%nPublic methods with that name:%n%s", + methodName, + descriptor, + className, + Arrays.stream(lookup.getAccessibleMethods(targetClass)) + .filter(method -> Modifier.isPublic(method.getModifiers())) + .filter(method -> method.getDeclaringClass().equals(targetClass)) + .filter(method -> method.getName().equals(methodName)) + .map( + method -> + String.format( + "%s::%s%s", + method.getDeclaringClass().getName(), + method.getName(), + Utils.getReadableDescriptor(method))) + .distinct() + .collect(Collectors.joining(System.lineSeparator())))); } } System.exit(1); } - Set ignoredExceptionGlobMatchers = Arrays.stream(args) - .skip(1) - .filter(s -> s.contains("*")) - .map(SimpleGlobMatcher::new) - .collect(Collectors.toSet()); + Set ignoredExceptionGlobMatchers = + Arrays.stream(args) + .skip(1) + .filter(s -> s.contains("*")) + .map(SimpleGlobMatcher::new) + .collect(Collectors.toSet()); List> alwaysIgnore = Arrays.stream(args) .skip(1) .filter(s -> !s.contains("*")) - .map(name -> { - try { - return ClassLoader.getSystemClassLoader().loadClass(name); - } catch (ClassNotFoundException e) { - Log.error(String.format( - "Failed to find class '%s' specified in --autofuzz_ignore", name)); - System.exit(1); - } - throw new Error("Not reached"); - }) + .map( + name -> { + try { + return ClassLoader.getSystemClassLoader().loadClass(name); + } catch (ClassNotFoundException e) { + Log.error( + String.format( + "Failed to find class '%s' specified in --autofuzz_ignore", name)); + System.exit(1); + } + throw new Error("Not reached"); + }) .collect(Collectors.toList()); Map[]> ignoredExceptionClasses = Arrays.stream(executables) - .collect(Collectors.toMap(method - -> method, - method - -> Stream.concat(Arrays.stream(method.getExceptionTypes()), alwaysIgnore.stream()) - .toArray(Class[] ::new))); + .collect( + Collectors.toMap( + method -> method, + method -> + Stream.concat( + Arrays.stream(method.getExceptionTypes()), alwaysIgnore.stream()) + .toArray(Class[]::new))); setTarget( executables, null, methodSignature, ignoredExceptionGlobMatchers, ignoredExceptionClasses); @@ -222,8 +251,11 @@ public static void fuzzerInitialize(String[] args) { * Set the target executables to (auto-)fuzz. This method is primarily used by the JUnit * integration to set the target class and method passed in by the test framework. */ - public static void setTarget(Executable[] targetExecutables, Object targetInstance, - String methodReference, Set ignoredExceptionMatchers, + public static void setTarget( + Executable[] targetExecutables, + Object targetInstance, + String methodReference, + Set ignoredExceptionMatchers, Map[]> throwsDeclarations) { Class targetClass = null; for (Executable executable : targetExecutables) { @@ -267,8 +299,9 @@ public static void dumpReproducer(FuzzedDataProvider data, String reproducerPath } catch (IOException e) { Log.error(String.format("Failed to write Java reproducer to %s%n", javaPath), e); } - Log.println(String.format( - "reproducer_path='%s'; Java reproducer written to %s%n", reproducerPath, javaPath)); + Log.println( + String.format( + "reproducer_path='%s'; Java reproducer written to %s%n", reproducerPath, javaPath)); } private static void fuzzerTestOneInput( @@ -304,7 +337,8 @@ private static void fuzzerTestOneInput( executionsSinceLastInvocation++; if (executionsSinceLastInvocation >= MAX_EXECUTIONS_WITHOUT_INVOCATION) { Log.error( - String.format("Failed to generate valid arguments to '%s' in %d attempts; giving up", + String.format( + "Failed to generate valid arguments to '%s' in %d attempts; giving up", methodReference, executionsSinceLastInvocation)); System.exit(1); } else if (executionsSinceLastInvocation == MAX_EXECUTIONS_WITHOUT_INVOCATION / 2) { @@ -347,8 +381,9 @@ private static void cleanStackTraces(Throwable t) { while (cause != null) { StackTraceElement[] elements = cause.getStackTrace(); int firstInterestingPos; - for (firstInterestingPos = elements.length - 1; firstInterestingPos > 0; - firstInterestingPos--) { + for (firstInterestingPos = elements.length - 1; + firstInterestingPos > 0; + firstInterestingPos--) { String className = elements[firstInterestingPos].getClassName(); if (!className.startsWith("com.code_intelligence.jazzer.autofuzz.") && !className.startsWith("java.lang.reflect.") diff --git a/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java b/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java index 543284f14..983f03156 100644 --- a/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java +++ b/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java @@ -84,14 +84,16 @@ public static void autofuzz(FuzzedDataProvider data, Consumer1 func) { @SuppressWarnings("unchecked") public static void autofuzz(FuzzedDataProvider data, Consumer2 func) { Class[] types = TypeResolver.resolveRawArguments(Consumer2.class, func.getClass()); - func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), + func.accept( + (T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1)); } @SuppressWarnings("unchecked") public static void autofuzz(FuzzedDataProvider data, Consumer3 func) { Class[] types = TypeResolver.resolveRawArguments(Consumer3.class, func.getClass()); - func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), + func.accept( + (T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1), (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2)); } @@ -100,7 +102,8 @@ public static void autofuzz(FuzzedDataProvider data, Consumer3 void autofuzz( FuzzedDataProvider data, Consumer4 func) { Class[] types = TypeResolver.resolveRawArguments(Consumer4.class, func.getClass()); - func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), + func.accept( + (T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1), (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2), (T4) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 3)); @@ -110,7 +113,8 @@ public static void autofuzz( public static void autofuzz( FuzzedDataProvider data, Consumer5 func) { Class[] types = TypeResolver.resolveRawArguments(Consumer5.class, func.getClass()); - func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), + func.accept( + (T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1), (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2), (T4) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 3), @@ -126,14 +130,16 @@ public static R autofuzz(FuzzedDataProvider data, Function1 func) @SuppressWarnings("unchecked") public static R autofuzz(FuzzedDataProvider data, Function2 func) { Class[] types = TypeResolver.resolveRawArguments(Function2.class, func.getClass()); - return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), + return func.apply( + (T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1)); } @SuppressWarnings("unchecked") public static R autofuzz(FuzzedDataProvider data, Function3 func) { Class[] types = TypeResolver.resolveRawArguments(Function3.class, func.getClass()); - return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), + return func.apply( + (T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1), (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2)); } @@ -142,7 +148,8 @@ public static R autofuzz(FuzzedDataProvider data, Function3 R autofuzz( FuzzedDataProvider data, Function4 func) { Class[] types = TypeResolver.resolveRawArguments(Function4.class, func.getClass()); - return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), + return func.apply( + (T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1), (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2), (T4) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 3)); @@ -152,7 +159,8 @@ public static R autofuzz( public static R autofuzz( FuzzedDataProvider data, Function5 func) { Class[] types = TypeResolver.resolveRawArguments(Function5.class, func.getClass()); - return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), + return func.apply( + (T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0), (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1), (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2), (T4) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 3), @@ -189,17 +197,21 @@ private static String deepToString(Object obj) { return "null"; } if (obj.getClass().isArray()) { - return String.format("(%s[]) %s", obj.getClass().getComponentType().getName(), - Arrays.deepToString((Object[]) obj)); + return String.format( + "(%s[]) %s", + obj.getClass().getComponentType().getName(), Arrays.deepToString((Object[]) obj)); } return obj.toString(); } private static String getDebugSummary( Executable executable, Object thisObject, Object[] arguments) { - return String.format("%nMethod: %s::%s%s%nthis: %s%nArguments: %s", - executable.getDeclaringClass().getName(), executable.getName(), - Utils.getReadableDescriptor(executable), thisObject, + return String.format( + "%nMethod: %s::%s%s%nthis: %s%nArguments: %s", + executable.getDeclaringClass().getName(), + executable.getName(), + Utils.getReadableDescriptor(executable), + thisObject, Arrays.stream(arguments).map(Meta::deepToString).collect(Collectors.joining(", "))); } @@ -214,8 +226,8 @@ static Class getRawType(Type genericType) { } else if (genericType instanceof TypeVariable) { throw new AutofuzzError("Did not expect genericType to be a TypeVariable: " + genericType); } else if (genericType instanceof GenericArrayType) { - return Array - .newInstance(getRawType(((GenericArrayType) genericType).getGenericComponentType()), 0) + return Array.newInstance( + getRawType(((GenericArrayType) genericType).getGenericComponentType()), 0) .getClass(); } else { throw new AutofuzzError("Got unexpected class implementing Type: " + genericType); @@ -439,33 +451,37 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor if (type == byte[].class) { byte[] result = data.consumeBytes(consumeArrayLength(data, Byte.BYTES)); if (visitor != null) { - visitor.pushElement(IntStream.range(0, result.length) - .mapToObj(i -> "(byte) " + result[i]) - .collect(Collectors.joining(", ", "new byte[]{", "}"))); + visitor.pushElement( + IntStream.range(0, result.length) + .mapToObj(i -> "(byte) " + result[i]) + .collect(Collectors.joining(", ", "new byte[]{", "}"))); } return result; } else if (type == int[].class) { int[] result = data.consumeInts(consumeArrayLength(data, Integer.BYTES)); if (visitor != null) { - visitor.pushElement(Arrays.stream(result) - .mapToObj(String::valueOf) - .collect(Collectors.joining(", ", "new int[]{", "}"))); + visitor.pushElement( + Arrays.stream(result) + .mapToObj(String::valueOf) + .collect(Collectors.joining(", ", "new int[]{", "}"))); } return result; } else if (type == short[].class) { short[] result = data.consumeShorts(consumeArrayLength(data, Short.BYTES)); if (visitor != null) { - visitor.pushElement(IntStream.range(0, result.length) - .mapToObj(i -> "(short) " + result[i]) - .collect(Collectors.joining(", ", "new short[]{", "}"))); + visitor.pushElement( + IntStream.range(0, result.length) + .mapToObj(i -> "(short) " + result[i]) + .collect(Collectors.joining(", ", "new short[]{", "}"))); } return result; } else if (type == long[].class) { long[] result = data.consumeLongs(consumeArrayLength(data, Long.BYTES)); if (visitor != null) { - visitor.pushElement(Arrays.stream(result) - .mapToObj(e -> e + "L") - .collect(Collectors.joining(", ", "new long[]{", "}"))); + visitor.pushElement( + Arrays.stream(result) + .mapToObj(e -> e + "L") + .collect(Collectors.joining(", ", "new long[]{", "}"))); } return result; } else if (type == boolean[].class) { @@ -485,8 +501,9 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor int remainingBytesAfterFirstElementCreation = data.remainingBytes(); int sizeOfElementEstimate = remainingBytesBeforeFirstElementCreation - remainingBytesAfterFirstElementCreation; - Object array = Array.newInstance( - type.getComponentType(), consumeArrayLength(data, sizeOfElementEstimate)); + Object array = + Array.newInstance( + type.getComponentType(), consumeArrayLength(data, sizeOfElementEstimate)); for (int i = 0; i < Array.getLength(array); i++) { if (i == 0) { Array.set(array, i, firstElement); @@ -507,10 +524,12 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor } else if (type == ByteArrayInputStream.class || type == InputStream.class) { byte[] array = data.consumeBytes(consumeArrayLength(data, Byte.BYTES)); if (visitor != null) { - visitor.pushElement(IntStream.range(0, array.length) - .mapToObj(i -> "(byte) " + array[i]) - .collect(Collectors.joining( - ", ", "new java.io.ByteArrayInputStream(new byte[]{", "})"))); + visitor.pushElement( + IntStream.range(0, array.length) + .mapToObj(i -> "(byte) " + array[i]) + .collect( + Collectors.joining( + ", ", "new java.io.ByteArrayInputStream(new byte[]{", "})"))); } return new ByteArrayInputStream(array); } else if (type == Map.class) { @@ -526,10 +545,12 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor // Also annotate the type of the entry stream since it might be empty, in which case type // inference on the accumulator could fail. visitor.pushGroup( - String.format("java.util.stream.Stream.>of(", + String.format( + "java.util.stream.Stream.>of(", keyType.getTypeName(), valueType.getTypeName()), ", ", - ").collect(java.util.HashMap::new, (map, e) -> map.put(e.getKey(), e.getValue()), java.util.HashMap::putAll)"); + ").collect(java.util.HashMap::new, (map, e) -> map.put(e.getKey(), e.getValue())," + + " java.util.HashMap::putAll)"); } int remainingBytesBeforeFirstEntryCreation = data.remainingBytes(); if (visitor != null) { @@ -593,32 +614,36 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor if (implementingClasses == null) { // TODO: We may be scanning multiple times. Instead, we should keep the ScanResult around // for as long as there is enough memory. - ClassGraph classGraph = new ClassGraph() - .enableClassInfo() - .ignoreClassVisibility() - .ignoreMethodVisibility() - .enableInterClassDependencies() - .rejectPackages("jaz"); + ClassGraph classGraph = + new ClassGraph() + .enableClassInfo() + .ignoreClassVisibility() + .ignoreMethodVisibility() + .enableInterClassDependencies() + .rejectPackages("jaz"); if (!IS_TEST) { classGraph.rejectPackages("com.code_intelligence.jazzer"); } try (ScanResult result = classGraph.scan()) { ClassInfoList children = type.isInterface() ? result.getClassesImplementing(type) : result.getSubclasses(type); - implementingClasses = children.getStandardClasses() - .filter(info -> !Modifier.isAbstract(info.getModifiers())) - .filter(info -> lookup.isAccessible(info, info.getModifiers())) - // Filter out anonymous and local classes, which can't be - // instantiated in reproducers. - .filter(info -> info.getName() != null) - .loadClasses(); + implementingClasses = + children + .getStandardClasses() + .filter(info -> !Modifier.isAbstract(info.getModifiers())) + .filter(info -> lookup.isAccessible(info, info.getModifiers())) + // Filter out anonymous and local classes, which can't be + // instantiated in reproducers. + .filter(info -> info.getName() != null) + .loadClasses(); implementingClassesCache.put(type, implementingClasses); } } if (implementingClasses.isEmpty()) { if (IS_DEBUG) { - throw new AutofuzzConstructionException(String.format( - "Could not find classes implementing %s on the classpath", type.getName())); + throw new AutofuzzConstructionException( + String.format( + "Could not find classes implementing %s on the classpath", type.getName())); } else { throw new AutofuzzConstructionException(); } @@ -646,8 +671,10 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor // Embed the instance creation and setters into an immediately invoked lambda expression to // turn them into an expression. String uniqueVariableName = visitor.uniqueVariableName(); - visitor.pushGroup(String.format("((java.util.function.Supplier<%1$s>) (() -> {%1$s %2$s = ", - type.getCanonicalName(), uniqueVariableName), + visitor.pushGroup( + String.format( + "((java.util.function.Supplier<%1$s>) (() -> {%1$s %2$s = ", + type.getCanonicalName(), uniqueVariableName), String.format("; %s.", uniqueVariableName), String.format("; return %s;})).get()", uniqueVariableName)); } @@ -686,8 +713,9 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor // Group for the chain of builder methods. visitor.pushGroup("", ".", ""); } - Object builderObj = autofuzzForConsume( - data, data.pickValue(lookup.getAccessibleConstructors(pickedBuilder)), visitor); + Object builderObj = + autofuzzForConsume( + data, data.pickValue(lookup.getAccessibleConstructors(pickedBuilder)), visitor); for (Method method : pickedMethods) { builderObj = autofuzzForConsume(data, method, builderObj, visitor); } @@ -706,15 +734,17 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor // We ran out of ways to construct an instance of the requested type. If in debug mode, report // more detailed information. if (IS_DEBUG) { - String summary = String.format( - "Failed to generate instance of %s:%nAccessible constructors: %s%nNested subclasses: %s%n", - type.getName(), - Arrays.stream(lookup.getAccessibleConstructors(type)) - .map(Utils::getReadableDescriptor) - .collect(Collectors.joining(", ")), - Arrays.stream(lookup.getAccessibleClasses(type)) - .map(Class::getName) - .collect(Collectors.joining(", "))); + String summary = + String.format( + "Failed to generate instance of %s:%nAccessible constructors: %s%nNested subclasses:" + + " %s%n", + type.getName(), + Arrays.stream(lookup.getAccessibleConstructors(type)) + .map(Utils::getReadableDescriptor) + .collect(Collectors.joining(", ")), + Arrays.stream(lookup.getAccessibleClasses(type)) + .map(Class::getName) + .collect(Collectors.joining(", "))); throw new AutofuzzConstructionException(summary); } else { throw new AutofuzzConstructionException(); @@ -724,10 +754,11 @@ Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor private List> getNestedBuilderClasses(Class type) { List> nestedBuilderClasses = nestedBuilderClassesCache.get(type); if (nestedBuilderClasses == null) { - nestedBuilderClasses = Arrays.stream(lookup.getAccessibleClasses(type)) - .filter(cls -> cls.getName().endsWith("Builder")) - .filter(cls -> !getOriginalObjectCreationMethods(cls).isEmpty()) - .collect(Collectors.toList()); + nestedBuilderClasses = + Arrays.stream(lookup.getAccessibleClasses(type)) + .filter(cls -> cls.getName().endsWith("Builder")) + .filter(cls -> !getOriginalObjectCreationMethods(cls).isEmpty()) + .collect(Collectors.toList()); nestedBuilderClassesCache.put(type, nestedBuilderClasses); } return nestedBuilderClasses; @@ -748,9 +779,10 @@ private List getOriginalObjectCreationMethods(Class builder) { private List getCascadingBuilderMethods(Class builder) { List cascadingBuilderMethods = cascadingBuilderMethodsCache.get(builder); if (cascadingBuilderMethods == null) { - cascadingBuilderMethods = Arrays.stream(lookup.getAccessibleMethods(builder)) - .filter(m -> m.getReturnType() == builder) - .collect(Collectors.toList()); + cascadingBuilderMethods = + Arrays.stream(lookup.getAccessibleMethods(builder)) + .filter(m -> m.getReturnType() == builder) + .collect(Collectors.toList()); cascadingBuilderMethodsCache.put(builder, cascadingBuilderMethods); } return cascadingBuilderMethods; @@ -768,9 +800,10 @@ public Object[] consumeArguments( FuzzedDataProvider data, Executable executable, AutofuzzCodegenVisitor visitor) { Object[] result; try { - result = Arrays.stream(executable.getGenericParameterTypes()) - .map(type -> consume(data, type, visitor)) - .toArray(); + result = + Arrays.stream(executable.getGenericParameterTypes()) + .map(type -> consume(data, type, visitor)) + .toArray(); return result; } catch (AutofuzzConstructionException e) { // Do not nest AutofuzzConstructionExceptions. diff --git a/src/main/java/com/code_intelligence/jazzer/autofuzz/YourAverageJavaClass.java b/src/main/java/com/code_intelligence/jazzer/autofuzz/YourAverageJavaClass.java index 452ca8781..e9dff8aac 100644 --- a/src/main/java/com/code_intelligence/jazzer/autofuzz/YourAverageJavaClass.java +++ b/src/main/java/com/code_intelligence/jazzer/autofuzz/YourAverageJavaClass.java @@ -42,8 +42,17 @@ public class YourAverageJavaClass implements Cloneable, Closeable, Serializable // Everything below has been automatically generated (apart from a minor modification to clone()); - public YourAverageJavaClass(byte aByte, boolean aBoolean, double aDouble, float aFloat, int anInt, - int transientInt, long aLong, short aShort, short volatileShort, String string) { + public YourAverageJavaClass( + byte aByte, + boolean aBoolean, + double aDouble, + float aFloat, + int anInt, + int transientInt, + long aLong, + short aShort, + short volatileShort, + String string) { this.aByte = aByte; this.aBoolean = aBoolean; this.aDouble = aDouble; @@ -58,9 +67,20 @@ public YourAverageJavaClass(byte aByte, boolean aBoolean, double aDouble, float public YourAverageJavaClass() {} - public YourAverageJavaClass(byte aByte, boolean aBoolean, double aDouble, float aFloat, int anInt, - int transientInt, long aLong, short aShort, short volatileShort, String string, byte[] bytes, - List list, Map map) { + public YourAverageJavaClass( + byte aByte, + boolean aBoolean, + double aDouble, + float aFloat, + int anInt, + int transientInt, + long aLong, + short aShort, + short volatileShort, + String string, + byte[] bytes, + List list, + Map map) { this.aByte = aByte; this.aBoolean = aBoolean; this.aDouble = aDouble; @@ -194,23 +214,40 @@ public YourAverageJavaClass clone() { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof YourAverageJavaClass)) - return false; + if (this == o) return true; + if (!(o instanceof YourAverageJavaClass)) return false; YourAverageJavaClass that = (YourAverageJavaClass) o; - return aByte == that.aByte && aBoolean == that.aBoolean - && Double.compare(that.aDouble, aDouble) == 0 && Float.compare(that.aFloat, aFloat) == 0 - && anInt == that.anInt && transientInt == that.transientInt && aLong == that.aLong - && aShort == that.aShort && volatileShort == that.volatileShort - && Objects.equals(string, that.string) && Arrays.equals(bytes, that.bytes) - && Objects.equals(list, that.list) && Objects.equals(map, that.map); + return aByte == that.aByte + && aBoolean == that.aBoolean + && Double.compare(that.aDouble, aDouble) == 0 + && Float.compare(that.aFloat, aFloat) == 0 + && anInt == that.anInt + && transientInt == that.transientInt + && aLong == that.aLong + && aShort == that.aShort + && volatileShort == that.volatileShort + && Objects.equals(string, that.string) + && Arrays.equals(bytes, that.bytes) + && Objects.equals(list, that.list) + && Objects.equals(map, that.map); } @Override public int hashCode() { - int result = Objects.hash(aByte, aBoolean, aDouble, aFloat, anInt, transientInt, aLong, aShort, - volatileShort, string, list, map); + int result = + Objects.hash( + aByte, + aBoolean, + aDouble, + aFloat, + anInt, + transientInt, + aLong, + aShort, + volatileShort, + string, + list, + map); result = 31 * result + Arrays.hashCode(bytes); return result; } @@ -218,10 +255,34 @@ public int hashCode() { @Override public String toString() { return "YourAverageJavaClass{" - + "aByte=" + aByte + ", aBoolean=" + aBoolean + ", aDouble=" + aDouble + ", aFloat=" - + aFloat + ", anInt=" + anInt + ", transientInt=" + transientInt + ", aLong=" + aLong - + ", aShort=" + aShort + ", volatileShort=" + volatileShort + ", string='" + string + '\'' - + ", bytes=" + Arrays.toString(bytes) + ", list=" + list + ", map=" + map + '}'; + + "aByte=" + + aByte + + ", aBoolean=" + + aBoolean + + ", aDouble=" + + aDouble + + ", aFloat=" + + aFloat + + ", anInt=" + + anInt + + ", transientInt=" + + transientInt + + ", aLong=" + + aLong + + ", aShort=" + + aShort + + ", volatileShort=" + + volatileShort + + ", string='" + + string + + '\'' + + ", bytes=" + + Arrays.toString(bytes) + + ", list=" + + list + + ", map=" + + map + + '}'; } @Override diff --git a/src/main/java/com/code_intelligence/jazzer/driver/Constants.java b/src/main/java/com/code_intelligence/jazzer/driver/Constants.java index 80d476d8c..95e9840cd 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/Constants.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/Constants.java @@ -20,5 +20,6 @@ public final class Constants { // Default value of the libFuzzer -error_exitcode flag. public static final int JAZZER_FINDING_EXIT_CODE = 77; - private Constants(){}; + private Constants() {} + ; } diff --git a/src/main/java/com/code_intelligence/jazzer/driver/Driver.java b/src/main/java/com/code_intelligence/jazzer/driver/Driver.java index 7445e17ca..de9234524 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/Driver.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/Driver.java @@ -83,7 +83,7 @@ public static int start(List args, boolean spawnsSubprocesses) throws IO idSyncFile.toFile().deleteOnExit(); } - if (args.stream().anyMatch("-merge_inner=1" ::equals)) { + if (args.stream().anyMatch("-merge_inner=1"::equals)) { Opt.mergeInner.setIfDefault(true); } @@ -91,8 +91,11 @@ public static int start(List args, boolean spawnsSubprocesses) throws IO // occurrence of a "-seed" argument as that is the one that is used by libFuzzer. If none is // set, generate one and pass it to libFuzzer so that a fuzzing run can be reproduced simply by // setting the seed printed by libFuzzer. - String seed = args.stream().reduce( - null, (prev, cur) -> cur.startsWith("-seed=") ? cur.substring("-seed=".length()) : prev); + String seed = + args.stream() + .reduce( + null, + (prev, cur) -> cur.startsWith("-seed=") ? cur.substring("-seed=".length()) : prev); if (seed == null) { seed = Integer.toUnsignedString(new SecureRandom().nextInt()); // Only add the -seed argument to the command line if not running in a mode diff --git a/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetFinder.java b/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetFinder.java index f4db00879..10d869a9c 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetFinder.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetFinder.java @@ -56,9 +56,11 @@ static FuzzTarget findFuzzTarget(String targetClassName) { fuzzTargetClass = Class.forName(targetClassName, false, FuzzTargetFinder.class.getClassLoader()); } catch (ClassNotFoundException e) { - Log.error(String.format( - "'%s' not found on classpath:%n%n%s%n%nAll required classes must be on the classpath specified via --cp.", - targetClassName, System.getProperty("java.class.path"))); + Log.error( + String.format( + "'%s' not found on classpath:%n%n%s%n%nAll required classes must be on the classpath" + + " specified via --cp.", + targetClassName, System.getProperty("java.class.path"))); exit(1); throw new IllegalStateException("Not reached"); } @@ -77,7 +79,8 @@ private static FuzzTarget findFuzzTargetByMethodName(Class clazz) { .collect(Collectors.toList()); if (fuzzTargetMethods.size() != 1) { throw new IllegalArgumentException( - String.format("%s must define exactly one function of this form:%n" + String.format( + "%s must define exactly one function of this form:%n" + "public static void fuzzerTestOneInput(...)%n", clazz.getName())); } @@ -88,13 +91,16 @@ private static FuzzTarget findFuzzTargetByMethodName(Class clazz) { Optional dataFuzzTarget = targetPublicStaticMethod(clazz, FUZZER_TEST_ONE_INPUT, FuzzedDataProvider.class); if (bytesFuzzTarget.isPresent() == dataFuzzTarget.isPresent()) { - throw new IllegalArgumentException(String.format( - "%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 be thrown instead of returning true.%n" - + "Note: When using the @FuzzTest annotation, you will need to set up JUnit 5, which can be as simple as adding a dependency on org.junit.jupiter:junit-jupiter-engine.", - clazz.getName())); + throw new IllegalArgumentException( + String.format( + "%s must define exactly one of the following two functions:%npublic static void" + + " fuzzerTestOneInput(byte[] ...)%npublic static void" + + " fuzzerTestOneInput(FuzzedDataProvider ...)%nNote: Fuzz targets returning" + + " boolean are no longer supported; exceptions should be thrown instead of" + + " returning true.%nNote: When using the @FuzzTest annotation, you will need" + + " to set up JUnit 5, which can be as simple as adding a dependency on" + + " org.junit.jupiter:junit-jupiter-engine.", + clazz.getName())); } fuzzTargetMethod = dataFuzzTarget.orElseGet(bytesFuzzTarget::get); } diff --git a/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetHolder.java b/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetHolder.java index 1c833c61a..fc604ae7a 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetHolder.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetHolder.java @@ -18,14 +18,14 @@ import com.code_intelligence.jazzer.api.FuzzedDataProvider; import java.lang.reflect.Method; -import java.util.Optional; import java.util.concurrent.Callable; public class FuzzTargetHolder { public static FuzzTarget autofuzzFuzzTarget(Callable newInstance) { try { - Method fuzzerTestOneInput = com.code_intelligence.jazzer.autofuzz.FuzzTarget.class.getMethod( - "fuzzerTestOneInput", FuzzedDataProvider.class); + Method fuzzerTestOneInput = + com.code_intelligence.jazzer.autofuzz.FuzzTarget.class.getMethod( + "fuzzerTestOneInput", FuzzedDataProvider.class); return new FuzzTargetHolder.FuzzTarget( fuzzerTestOneInput, newInstance, LifecycleMethodsInvoker.NOOP); } catch (NoSuchMethodException e) { @@ -33,15 +33,15 @@ public static FuzzTarget autofuzzFuzzTarget(Callable newInstance) { } } - public static final FuzzTarget AUTOFUZZ_FUZZ_TARGET = autofuzzFuzzTarget(() -> { - com.code_intelligence.jazzer.autofuzz.FuzzTarget.fuzzerInitialize( - Opt.targetArgs.get().toArray(new String[0])); - return null; - }); + public static final FuzzTarget AUTOFUZZ_FUZZ_TARGET = + autofuzzFuzzTarget( + () -> { + com.code_intelligence.jazzer.autofuzz.FuzzTarget.fuzzerInitialize( + Opt.targetArgs.get().toArray(new String[0])); + return null; + }); - /** - * The fuzz target that {@link FuzzTargetRunner} should fuzz. - */ + /** The fuzz target that {@link FuzzTargetRunner} should fuzz. */ public static FuzzTarget fuzzTarget; public static class FuzzTarget { @@ -49,7 +49,9 @@ public static class FuzzTarget { public final Callable newInstance; public final LifecycleMethodsInvoker lifecycleMethodsInvoker; - public FuzzTarget(Method method, Callable newInstance, + public FuzzTarget( + Method method, + Callable newInstance, LifecycleMethodsInvoker lifecycleMethodsInvoker) { this.method = method; this.newInstance = newInstance; diff --git a/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetRunner.java b/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetRunner.java index 0dc2e6101..cda28ad9c 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetRunner.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetRunner.java @@ -72,7 +72,8 @@ public final class FuzzTargetRunner { Log.error("--target_class and --autofuzz cannot be specified together"); exit(1); } - if (!Opt.targetArgs.setIfDefault(unmodifiableList( + if (!Opt.targetArgs.setIfDefault( + unmodifiableList( concat(Stream.of(Opt.autofuzz.get()), Opt.autofuzzIgnore.get().stream()) .collect(toList())))) { Log.error("--target_args and --autofuzz cannot be specified together"); @@ -103,10 +104,10 @@ public final class FuzzTargetRunner { // Keep these options used in runOne (and thus the critical path) in static final fields so that // they can be constant-folded by the JIT. - private static final Set ignoredTokens = Opt.ignore.get() - .stream() - .map(s -> Long.parseUnsignedLong(s, 16)) - .collect(toCollection(HashSet::new)); + private static final Set ignoredTokens = + Opt.ignore.get().stream() + .map(s -> Long.parseUnsignedLong(s, 16)) + .collect(toCollection(HashSet::new)); private static final boolean useExperimentalMutator = Opt.experimentalMutator.get(); private static final boolean optimizeMergeInner = Opt.mergeInner.get(); private static final boolean useHooks = Opt.hooks.get(); @@ -182,9 +183,7 @@ public final class FuzzTargetRunner { } } - /** - * A test-only convenience wrapper around {@link #runOne(long, int)}. - */ + /** A test-only convenience wrapper around {@link #runOne(long, int)}. */ static int runOne(byte[] data) { long dataPtr = UNSAFE.allocateMemory(data.length); UNSAFE.copyMemory(data, BYTE_ARRAY_OFFSET, null, dataPtr, data.length); @@ -198,11 +197,11 @@ static int runOne(byte[] data) { /** * Executes the user-provided fuzz target once. * - * @param dataPtr a native pointer to beginning of the input provided by the fuzzer for this - * execution + * @param dataPtr a native pointer to beginning of the input provided by the fuzzer for this + * execution * @param dataLength length of the fuzzer input * @return the value that the native LLVMFuzzerTestOneInput function should return. The function - * may exit the process instead of returning. + * may exit the process instead of returning. */ private static int runOne(long dataPtr, int dataLength) { Throwable finding = null; @@ -222,8 +221,10 @@ private static int runOne(long dataPtr, int dataLength) { // once. if (!(invalidCorpusFileWarningShown || readExactly || isFixedLibFuzzerInput(buf))) { invalidCorpusFileWarningShown = true; - Log.warn("Some files in the seed corpus do not match the fuzz target signature. " - + "This indicates that they were generated with a different signature and may cause issues reproducing previous findings."); + Log.warn( + "Some files in the seed corpus do not match the fuzz target signature. This indicates" + + " that they were generated with a different signature and may cause issues" + + " reproducing previous findings."); } data = null; argument = null; @@ -328,17 +329,18 @@ private static int runOne(long dataPtr, int dataLength) { // Reached the maximum amount of findings to keep going for, crash after shutdown. if (!Opt.autofuzz.get().isEmpty() && Opt.dedup.get()) { Log.println(""); - Log.info(String.format( - "To continue fuzzing past this particular finding, rerun with the following additional argument:" - + "%n%n --ignore=%s%n%n" - + "To ignore all findings of this kind, rerun with the following additional argument:" - + "%n%n --autofuzz_ignore=%s", - ignoredTokens.stream() - .map(token -> Long.toUnsignedString(token, 16)) - .collect(joining(",")), - Stream - .concat(Opt.autofuzzIgnore.get().stream(), Stream.of(finding.getClass().getName())) - .collect(joining(",")))); + Log.info( + String.format( + "To continue fuzzing past this particular finding, rerun with the following" + + " additional argument:%n%n --ignore=%s%n%nTo ignore all findings of this" + + " kind, rerun with the following additional argument:%n%n " + + " --autofuzz_ignore=%s", + ignoredTokens.stream() + .map(token -> Long.toUnsignedString(token, 16)) + .collect(joining(",")), + Stream.concat( + Opt.autofuzzIgnore.get().stream(), Stream.of(finding.getClass().getName())) + .collect(joining(",")))); } System.exit(JAZZER_FINDING_EXIT_CODE); throw new IllegalStateException("Not reached"); @@ -386,8 +388,10 @@ private static int crossOver( // much and is reduced to a configurable frequency, default 1/100, here, // mutate is used in the other cases. if (crossOverFrequency != 0 && crossOverCount++ % crossOverFrequency == 0) { - mutator.crossOver(new ByteArrayInputStream(copyToArray(data1, size1)), - new ByteArrayInputStream(copyToArray(data2, size2)), seed); + mutator.crossOver( + new ByteArrayInputStream(copyToArray(data1, size1)), + new ByteArrayInputStream(copyToArray(data2, size2)), + seed); } else { mutate(data1, size1, seed); } @@ -435,15 +439,15 @@ public static int startLibFuzzer(List args) { SignalHandler.initialize(); } return startLibFuzzer( - args.stream().map(str -> str.getBytes(StandardCharsets.UTF_8)).toArray(byte[][] ::new)); + args.stream().map(str -> str.getBytes(StandardCharsets.UTF_8)).toArray(byte[][]::new)); } /** * Registers a custom handler for findings. * * @param findingHandler a consumer for the finding that returns true if the fuzzer should - * continue fuzzing and false if it should return from - * {@link FuzzTargetRunner#startLibFuzzer(List)}. + * continue fuzzing and false if it should return from {@link + * FuzzTargetRunner#startLibFuzzer(List)}. */ public static void registerFindingHandler(Predicate findingHandler) { FuzzTargetRunner.findingHandler = findingHandler; @@ -501,8 +505,9 @@ private static void dumpReproducer(byte[] data) { // Expected. } try { - base64Data = RecordingFuzzedDataProvider.serializeFuzzedDataProviderProxy( - recordingFuzzedDataProvider); + base64Data = + RecordingFuzzedDataProvider.serializeFuzzedDataProviderProxy( + recordingFuzzedDataProvider); } catch (IOException e) { Log.error("Failed to create reproducer", e); // Don't let libFuzzer print a native stack trace. @@ -562,17 +567,14 @@ public static void printCrashingInput() { FuzzTargetRunnerNatives.printCrashingInput(); } - /** - * Returns the debug string of the current mutator. - * If no mutator is used, returns null. - */ + /** Returns the debug string of the current mutator. If no mutator is used, returns null. */ public static String mutatorDebugString() { return mutator != null ? mutator.toString() : null; } /** - * Returns whether the current mutator has detected invalid corpus files. - * If no mutator is used, returns false. + * Returns whether the current mutator has detected invalid corpus files. If no mutator is used, + * returns false. */ public static boolean invalidCorpusFilesPresent() { return mutator != null && invalidCorpusFileWarningShown; diff --git a/src/main/java/com/code_intelligence/jazzer/driver/FuzzedDataProviderImpl.java b/src/main/java/com/code_intelligence/jazzer/driver/FuzzedDataProviderImpl.java index 6c742c67a..41c715027 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/FuzzedDataProviderImpl.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/FuzzedDataProviderImpl.java @@ -47,11 +47,10 @@ private FuzzedDataProviderImpl(long dataPtr, int remainingBytes, byte[] javaData * Creates a {@link FuzzedDataProvider} that consumes bytes from an already existing native array. * *
        - *
      • {@link #close()} must be called on instances created with this method to free the - * native copy of the Java - * {@code byte} array. - *
      • {@link #setNativeData(long, int)} must not be called on instances created with this - * method. + *
      • {@link #close()} must be called on instances created with this method to free the + * native copy of the Java {@code byte} array. + *
      • {@link #setNativeData(long, int)} must not be called on instances created with + * this method. * * @param data the raw bytes used as input * @return a {@link FuzzedDataProvider} backed by {@code data} @@ -136,11 +135,14 @@ private static long allocateNativeCopy(byte[] data) { return nativeCopy; } - @Override public native boolean consumeBoolean(); + @Override + public native boolean consumeBoolean(); - @Override public native boolean[] consumeBooleans(int maxLength); + @Override + public native boolean[] consumeBooleans(int maxLength); - @Override public native byte consumeByte(); + @Override + public native byte consumeByte(); @Override public byte consumeByte(byte min, byte max) { @@ -151,7 +153,8 @@ public byte consumeByte(byte min, byte max) { return consumeByteUnchecked(min, max); } - @Override public native short consumeShort(); + @Override + public native short consumeShort(); @Override public short consumeShort(short min, short max) { @@ -162,9 +165,11 @@ public short consumeShort(short min, short max) { return consumeShortUnchecked(min, max); } - @Override public native short[] consumeShorts(int maxLength); + @Override + public native short[] consumeShorts(int maxLength); - @Override public native int consumeInt(); + @Override + public native int consumeInt(); @Override public int consumeInt(int min, int max) { @@ -175,9 +180,11 @@ public int consumeInt(int min, int max) { return consumeIntUnchecked(min, max); } - @Override public native int[] consumeInts(int maxLength); + @Override + public native int[] consumeInts(int maxLength); - @Override public native long consumeLong(); + @Override + public native long consumeLong(); @Override public long consumeLong(long min, long max) { @@ -188,11 +195,14 @@ public long consumeLong(long min, long max) { return consumeLongUnchecked(min, max); } - @Override public native long[] consumeLongs(int maxLength); + @Override + public native long[] consumeLongs(int maxLength); - @Override public native float consumeFloat(); + @Override + public native float consumeFloat(); - @Override public native float consumeRegularFloat(); + @Override + public native float consumeRegularFloat(); @Override public float consumeRegularFloat(float min, float max) { @@ -209,9 +219,11 @@ public float consumeRegularFloat(float min, float max) { return consumeRegularFloatUnchecked(min, max); } - @Override public native float consumeProbabilityFloat(); + @Override + public native float consumeProbabilityFloat(); - @Override public native double consumeDouble(); + @Override + public native double consumeDouble(); @Override public double consumeRegularDouble(double min, double max) { @@ -228,11 +240,14 @@ public double consumeRegularDouble(double min, double max) { return consumeRegularDoubleUnchecked(min, max); } - @Override public native double consumeRegularDouble(); + @Override + public native double consumeRegularDouble(); - @Override public native double consumeProbabilityDouble(); + @Override + public native double consumeProbabilityDouble(); - @Override public native char consumeChar(); + @Override + public native char consumeChar(); @Override public char consumeChar(char min, char max) { @@ -243,27 +258,41 @@ public char consumeChar(char min, char max) { return consumeCharUnchecked(min, max); } - @Override public native char consumeCharNoSurrogates(); + @Override + public native char consumeCharNoSurrogates(); - @Override public native String consumeAsciiString(int maxLength); + @Override + public native String consumeAsciiString(int maxLength); - @Override public native String consumeString(int maxLength); + @Override + public native String consumeString(int maxLength); - @Override public native String consumeRemainingAsAsciiString(); + @Override + public native String consumeRemainingAsAsciiString(); - @Override public native String consumeRemainingAsString(); + @Override + public native String consumeRemainingAsString(); - @Override public native byte[] consumeBytes(int maxLength); + @Override + public native byte[] consumeBytes(int maxLength); - @Override public native byte[] consumeRemainingAsBytes(); + @Override + public native byte[] consumeRemainingAsBytes(); - @Override public native int remainingBytes(); + @Override + public native int remainingBytes(); private native byte consumeByteUnchecked(byte min, byte max); + private native short consumeShortUnchecked(short min, short max); + private native char consumeCharUnchecked(char min, char max); + private native int consumeIntUnchecked(int min, int max); + private native long consumeLongUnchecked(long min, long max); + private native float consumeRegularFloatUnchecked(float min, float max); + private native double consumeRegularDoubleUnchecked(double min, double max); } diff --git a/src/main/java/com/code_intelligence/jazzer/driver/LibFuzzerLifecycleMethodsInvoker.java b/src/main/java/com/code_intelligence/jazzer/driver/LibFuzzerLifecycleMethodsInvoker.java index 862c1700d..6bf9ea25b 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/LibFuzzerLifecycleMethodsInvoker.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/LibFuzzerLifecycleMethodsInvoker.java @@ -38,19 +38,23 @@ private LibFuzzerLifecycleMethodsInvoker( static LifecycleMethodsInvoker of(Class clazz) { Optional fuzzerInitialize = - Stream - .of(targetPublicStaticMethod(clazz, FUZZER_INITIALIZE, String[].class) - .map(init - -> (ThrowingRunnable) () - -> init.invoke( - null, (Object) Opt.targetArgs.get().toArray(new String[] {}))), + Stream.of( + targetPublicStaticMethod(clazz, FUZZER_INITIALIZE, String[].class) + .map( + init -> + (ThrowingRunnable) + () -> + init.invoke( + null, + (Object) Opt.targetArgs.get().toArray(new String[] {}))), targetPublicStaticMethod(clazz, FUZZER_INITIALIZE) .map(init -> (ThrowingRunnable) () -> init.invoke(null))) .filter(Optional::isPresent) .map(Optional::get) .findFirst(); - Optional fuzzerTearDown = targetPublicStaticMethod(clazz, FUZZER_TEAR_DOWN) - .map(tearDown -> () -> tearDown.invoke(null)); + Optional fuzzerTearDown = + targetPublicStaticMethod(clazz, FUZZER_TEAR_DOWN) + .map(tearDown -> () -> tearDown.invoke(null)); return new LibFuzzerLifecycleMethodsInvoker(fuzzerInitialize, fuzzerTearDown); } diff --git a/src/main/java/com/code_intelligence/jazzer/driver/LifecycleMethodsInvoker.java b/src/main/java/com/code_intelligence/jazzer/driver/LifecycleMethodsInvoker.java index a791ab3ed..71ee676c6 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/LifecycleMethodsInvoker.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/LifecycleMethodsInvoker.java @@ -21,23 +21,20 @@ * execution of a fuzz target. */ public interface LifecycleMethodsInvoker { - /** - * An implementation of {@link LifecycleMethodsInvoker} with empty implementations. - */ - LifecycleMethodsInvoker NOOP = new LifecycleMethodsInvoker() { - @Override - public void beforeFirstExecution() {} + /** An implementation of {@link LifecycleMethodsInvoker} with empty implementations. */ + LifecycleMethodsInvoker NOOP = + new LifecycleMethodsInvoker() { + @Override + public void beforeFirstExecution() {} - @Override - public void beforeEachExecution() {} + @Override + public void beforeEachExecution() {} - @Override - public void afterLastExecution() {} - }; + @Override + public void afterLastExecution() {} + }; - /** - * Invoked before the first execution of the fuzz target. - */ + /** Invoked before the first execution of the fuzz target. */ void beforeFirstExecution() throws Throwable; /** @@ -48,8 +45,7 @@ public void afterLastExecution() {} void beforeEachExecution() throws Throwable; /** - * Invoked after the last execution of the fuzz target, regardless of whether there was a - * finding. + * Invoked after the last execution of the fuzz target, regardless of whether there was a finding. */ void afterLastExecution() throws Throwable; diff --git a/src/main/java/com/code_intelligence/jazzer/driver/OfflineInstrumentor.java b/src/main/java/com/code_intelligence/jazzer/driver/OfflineInstrumentor.java index ecdf32b84..82e149404 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/OfflineInstrumentor.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/OfflineInstrumentor.java @@ -18,15 +18,10 @@ import com.code_intelligence.jazzer.utils.Log; import com.code_intelligence.jazzer.utils.ZipUtils; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.lang.UnsupportedClassVersionError; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; @@ -38,8 +33,8 @@ public class OfflineInstrumentor { /** - * Create a new jar file at /.instrumented.jar - * for each jar in passed in, with classes that have Jazzer instrumentation. + * Create a new jar file at /.instrumented.jar for each jar in passed + * in, with classes that have Jazzer instrumentation. * * @param jarLists list of jars to instrument * @return a boolean representing the success status @@ -59,8 +54,9 @@ public static boolean instrumentJars(List jarLists) { for (String jarPath : jarLists) { String outputBaseName = jarPath; if (outputBaseName.contains(File.separator)) { - outputBaseName = outputBaseName.substring( - outputBaseName.lastIndexOf(File.separator) + 1, outputBaseName.length()); + outputBaseName = + outputBaseName.substring( + outputBaseName.lastIndexOf(File.separator) + 1, outputBaseName.length()); } if (outputBaseName.contains(".jar")) { @@ -72,13 +68,18 @@ public static boolean instrumentJars(List jarLists) { try { errorMessages = createInstrumentedClasses(jarPath); } catch (IOException e) { - errorMessages.add("Failed to instrument jar: " + jarPath - + ". Please ensure the file at this location is a jar file. Error Message: " + e); + errorMessages.add( + "Failed to instrument jar: " + + jarPath + + ". Please ensure the file at this location is a jar file. Error Message: " + + e); continue; } try { - createInstrumentedJar(jarPath, Opt.dumpClassesDir.get() + File.separator + outputBaseName, + createInstrumentedJar( + jarPath, + Opt.dumpClassesDir.get() + File.separator + outputBaseName, outputBaseName + ".instrumented.jar"); } catch (Exception e) { errorMessages.add("Failed to instrument jar: " + jarPath + ". Error: " + e); @@ -94,8 +95,8 @@ public static boolean instrumentJars(List jarLists) { } /** - * Loops over all classes in jar file and adds instrumentation. The output - * of the instrumented classes will be at --dump-classes-dir + * Loops over all classes in jar file and adds instrumentation. The output of the instrumented + * classes will be at --dump-classes-dir * * @param jarPath a path to a jar file to instrument. * @return a list of errors that were hit when trying to instrument all classes in jar diff --git a/src/main/java/com/code_intelligence/jazzer/driver/Opt.java b/src/main/java/com/code_intelligence/jazzer/driver/Opt.java index 9e1495da8..0e3bb4cb0 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/Opt.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/Opt.java @@ -32,16 +32,18 @@ /** * Options that determine the runtime behavior of the fuzzer, set via (in increasing order of * preference): + * *
          - *
        • the default value;
        • - *
        • {@code META-INF/MANIFEST.MF} attributes {@code Jazzer-Some-Opt} on the classpath;
        • - *
        • the {@code JAZZER_SOME_OPT} environment variable;
        • - *
        • the {@code jazzer.some_opt} system property;
        • - *
        • the {@code jazzer.some_opt} JUnit configuration parameter (for JUnit fuzz tests);
        • - *
        • the {@code --some_opt} command-line argument (if running from the command line).
        • + *
        • the default value; + *
        • {@code META-INF/MANIFEST.MF} attributes {@code Jazzer-Some-Opt} on the classpath; + *
        • the {@code JAZZER_SOME_OPT} environment variable; + *
        • the {@code jazzer.some_opt} system property; + *
        • the {@code jazzer.some_opt} JUnit configuration parameter (for JUnit fuzz tests); + *
        • the {@code --some_opt} command-line argument (if running from the command line). *
        * *

        Option values are locked in the first time they are accessed. + * *

        Every public field should be static and final. */ public final class Opt { @@ -54,96 +56,163 @@ public final class Opt { static { // The following arguments are interpreted by the native launcher only. They do appear in the // help text, but aren't read by the driver. - stringListSetting("additional_jvm_args", - "Additional arguments to pass to the JVM (separator can be escaped with '\\', native launcher only)"); - stringListSetting("jvm_args", + stringListSetting( + "additional_jvm_args", + "Additional arguments to pass to the JVM (separator can be escaped with '\\', native" + + " launcher only)"); + stringListSetting( + "jvm_args", "Arguments to pass to the JVM (separator can be escaped with '\\', native launcher only)"); } public static final OptItem> additionalClassesExcludes = - stringListSetting("additional_classes_excludes", + stringListSetting( + "additional_classes_excludes", "Glob patterns matching names of classes from Java that are not in your jar file, " + "but may be included in your program"); - public static final OptItem agentPath = stringSetting( - "agent_path", "", "Custom path to jazzer_agent_deploy.jar (native launcher only)"); - public static final OptItem androidBootclassClassesOverrides = stringSetting( - "android_bootpath_classes_overrides", "", - "Used for fuzzing classes loaded in through the bootstrap class loader on Android. Full path to jar file with the instrumented versions of the classes you want to override."); - public static final OptItem androidInitOptions = stringSetting("android_init_options", "", - "Which libraries to use when initializing ART (native launcher only)"); - public static final OptItem autofuzz = stringSetting("autofuzz", "", - "Fully qualified reference (optionally with a Javadoc-style signature) to a " - + "method on the class path to be fuzzed with automatically generated arguments " - + "(examples: java.lang.System.out::println, java.lang.String::new(byte[]))"); - public static final OptItem asan = boolSetting( - "asan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=address'"); - public static final OptItem> autofuzzIgnore = stringListSetting("autofuzz_ignore", - ',', "Fully qualified names of exception classes to ignore during fuzzing"); - public static final OptItem coverageDump = stringSetting("coverage_dump", "", - "Path to write a JaCoCo .exec file to when the fuzzer exits (if non-empty)"); - public static final OptItem coverageReport = stringSetting("coverage_report", "", - "Path to write a human-readable coverage report to when the fuzzer exits (if non-empty)"); + public static final OptItem agentPath = + stringSetting( + "agent_path", "", "Custom path to jazzer_agent_deploy.jar (native launcher only)"); + public static final OptItem androidBootclassClassesOverrides = + stringSetting( + "android_bootpath_classes_overrides", + "", + "Used for fuzzing classes loaded in through the bootstrap class loader on Android. Full" + + " path to jar file with the instrumented versions of the classes you want to" + + " override."); + public static final OptItem androidInitOptions = + stringSetting( + "android_init_options", + "", + "Which libraries to use when initializing ART (native launcher only)"); + public static final OptItem autofuzz = + stringSetting( + "autofuzz", + "", + "Fully qualified reference (optionally with a Javadoc-style signature) to a " + + "method on the class path to be fuzzed with automatically generated arguments " + + "(examples: java.lang.System.out::println, java.lang.String::new(byte[]))"); + public static final OptItem asan = + boolSetting( + "asan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=address'"); + public static final OptItem> autofuzzIgnore = + stringListSetting( + "autofuzz_ignore", + ',', + "Fully qualified names of exception classes to ignore during fuzzing"); + public static final OptItem coverageDump = + stringSetting( + "coverage_dump", + "", + "Path to write a JaCoCo .exec file to when the fuzzer exits (if non-empty)"); + public static final OptItem coverageReport = + stringSetting( + "coverage_report", + "", + "Path to write a human-readable coverage report to when the fuzzer exits (if non-empty)"); public static final OptItem> cp = stringListSetting("cp", "The class path to use for fuzzing (native launcher only)"); - public static final OptItem> customHookExcludes = OptParser.stringListSetting( - "custom_hook_excludes", - "Glob patterns matching names of classes that should not be instrumented with hooks (custom and built-in)"); + public static final OptItem> customHookExcludes = + OptParser.stringListSetting( + "custom_hook_excludes", + "Glob patterns matching names of classes that should not be instrumented with hooks" + + " (custom and built-in)"); public static final OptItem> customHooks = stringListSetting("custom_hooks", "Names of classes to load custom hooks from"); public static final OptItem> customHookIncludes = - OptParser.stringListSetting("custom_hook_includes", + OptParser.stringListSetting( + "custom_hook_includes", "Glob patterns matching names of classes to instrument with hooks (custom and built-in)"); public static final OptItem dedup = boolSetting("dedup", true, "Compute and print a deduplication token for every finding"); - public static final OptItem> disabledHooks = stringListSetting("disabled_hooks", - "Names of classes from which hooks (custom or built-in) should not be loaded from"); - public static final OptItem dumpClassesDir = stringSetting( - "dump_classes_dir", "", "Directory to dump instrumented .class files into (if non-empty)"); + public static final OptItem> disabledHooks = + stringListSetting( + "disabled_hooks", + "Names of classes from which hooks (custom or built-in) should not be loaded from"); + public static final OptItem dumpClassesDir = + stringSetting( + "dump_classes_dir", + "", + "Directory to dump instrumented .class files into (if non-empty)"); public static final OptItem experimentalMutator = boolSetting("experimental_mutator", false, "Use an experimental structured mutator"); - public static final OptItem experimentalCrossOverFrequency = uint64Setting( - "experimental_cross_over_frequency", 100, - "(Used in experimental mutator) Frequency of cross-over mutations actually being executed " - + "when the cross-over function is picked by the underlying fuzzing engine (~1/2 of all mutations), " - + "other invocations perform type specific mutations via the experimental mutator. " - + "(0 = disabled, 1 = every call, 2 = every other call, etc.)."); - public static final OptItem fuzzNative = boolSetting("native", false, - "Allow fuzzing of native libraries compiled with '-fsanitize=fuzzer' (implied by --asan and --ubsan)"); - public static final OptItem hooks = boolSetting( - "hooks", true, "Apply fuzzing instrumentation (use 'trace' for finer-grained control)"); + public static final OptItem experimentalCrossOverFrequency = + uint64Setting( + "experimental_cross_over_frequency", + 100, + "(Used in experimental mutator) Frequency of cross-over mutations actually being executed" + + " when the cross-over function is picked by the underlying fuzzing engine (~1/2 of" + + " all mutations), other invocations perform type specific mutations via the" + + " experimental mutator. (0 = disabled, 1 = every call, 2 = every other call," + + " etc.)."); + public static final OptItem fuzzNative = + boolSetting( + "native", + false, + "Allow fuzzing of native libraries compiled with '-fsanitize=fuzzer' (implied by --asan" + + " and --ubsan)"); + public static final OptItem hooks = + boolSetting( + "hooks", true, "Apply fuzzing instrumentation (use 'trace' for finer-grained control)"); public static final OptItem hwasan = boolSetting("hwasan", false, "Allow fuzzing of native libraries compiled with hwasan"); - public static final OptItem idSyncFile = stringSetting("id_sync_file", "", - "A file used by Jazzer subprocesses to coordinate coverage instrumented. If not set, " - + "Jazzer will create a temporary file and pass it to subprocesses."); - public static final OptItem> ignore = stringListSetting("ignore", ',', - "Hex strings representing deduplication tokens of findings that should be ignored"); - public static final OptItem> instrument = OptParser.stringListSetting("instrument", - ',', - "Glob patterns matching names of classes that should be instrumented for fuzzing. This " - + "sets both instrumentation_includes and custom_hook_includes, depending on the mode " - + "(regression test or fuzzing). Only used for JUnit fuzz tests"); + public static final OptItem idSyncFile = + stringSetting( + "id_sync_file", + "", + "A file used by Jazzer subprocesses to coordinate coverage instrumented. If not set, " + + "Jazzer will create a temporary file and pass it to subprocesses."); + public static final OptItem> ignore = + stringListSetting( + "ignore", + ',', + "Hex strings representing deduplication tokens of findings that should be ignored"); + public static final OptItem> instrument = + OptParser.stringListSetting( + "instrument", + ',', + "Glob patterns matching names of classes that should be instrumented for fuzzing. This" + + " sets both instrumentation_includes and custom_hook_includes, depending on the" + + " mode (regression test or fuzzing). Only used for JUnit fuzz tests"); public static final OptItem> instrumentationExcludes = - OptParser.stringListSetting("instrumentation_excludes", + OptParser.stringListSetting( + "instrumentation_excludes", "Glob patterns matching names of classes that should not be instrumented for fuzzing"); public static final OptItem> instrumentationIncludes = - OptParser.stringListSetting("instrumentation_includes", + OptParser.stringListSetting( + "instrumentation_includes", "Glob patterns matching names of classes to instrument for fuzzing"); - public static final OptItem keepGoing = uint64Setting( - "keep_going", 1, "Number of distinct findings after which the fuzzer should stop"); - public static final OptItem reproducerPath = stringSetting("reproducer_path", ".", - "Directory in which stand-alone Java reproducers are stored for each finding"); - public static final OptItem> targetArgs = stringListSetting( - "target_args", ' ', "Arguments to pass to the fuzz target's fuzzerInitialize method"); - public static final OptItem targetClass = stringSetting("target_class", "", - "Fully qualified name of the fuzz target class (required unless --autofuzz is specified)"); + public static final OptItem keepGoing = + uint64Setting( + "keep_going", 1, "Number of distinct findings after which the fuzzer should stop"); + public static final OptItem reproducerPath = + stringSetting( + "reproducer_path", + ".", + "Directory in which stand-alone Java reproducers are stored for each finding"); + public static final OptItem> targetArgs = + stringListSetting( + "target_args", ' ', "Arguments to pass to the fuzz target's fuzzerInitialize method"); + public static final OptItem targetClass = + stringSetting( + "target_class", + "", + "Fully qualified name of the fuzz target class (required unless --autofuzz is" + + " specified)"); // Used to disambiguate between multiple methods annotated with @FuzzTest in the target class. - public static final OptItem targetMethod = stringSetting("target_method", "", - "The name of the @FuzzTest to execute in the class specified by --target_class"); - public static final OptItem> trace = stringListSetting("trace", - "Types of instrumentation to apply: cmp, cov, div, gep (disabled by default), indir, native"); - public static final OptItem ubsan = boolSetting( - "ubsan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=undefined'"); + public static final OptItem targetMethod = + stringSetting( + "target_method", + "", + "The name of the @FuzzTest to execute in the class specified by --target_class"); + public static final OptItem> trace = + stringListSetting( + "trace", + "Types of instrumentation to apply: cmp, cov, div, gep (disabled by default), indir," + + " native"); + public static final OptItem ubsan = + boolSetting( + "ubsan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=undefined'"); // Internal options: @@ -159,8 +228,11 @@ public final class Opt { private static final OptItem help = boolSetting("help", false, "Show this list of all available arguments"); - public static final OptItem> instrumentOnly = stringListSetting("instrument_only", - ',', "Comma separated list of jar files to instrument. No fuzzing is performed."); + public static final OptItem> instrumentOnly = + stringListSetting( + "instrument_only", + ',', + "Comma separated list of jar files to instrument. No fuzzing is performed."); private static final OptItem version = boolSetting("version", false, "Print version information"); diff --git a/src/main/java/com/code_intelligence/jazzer/driver/OptItem.java b/src/main/java/com/code_intelligence/jazzer/driver/OptItem.java index c40d67252..2f8ef580b 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/OptItem.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/OptItem.java @@ -31,7 +31,6 @@ import java.net.URL; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Map; @@ -42,9 +41,7 @@ import java.util.jar.Manifest; import java.util.stream.Stream; -/** - * A typed option that is evaluated lazily (see {@link #get()}). - */ +/** A typed option that is evaluated lazily (see {@link #get()}). */ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public abstract class OptItem implements Supplier { private static final String ROOT_SEGMENT = "jazzer"; @@ -75,10 +72,12 @@ static void registerCommandLineArgs(List> cliArgs) { if (OptItem.cliArgs.isPresent()) { throw new IllegalStateException("Command-line arguments have already been set"); } - OptItem.cliArgs = Optional.of( - unmodifiableList(cliArgs.stream() - .map(e -> new SimpleImmutableEntry<>(e.getKey(), e.getValue())) - .collect(toList()))); + OptItem.cliArgs = + Optional.of( + unmodifiableList( + cliArgs.stream() + .map(e -> new SimpleImmutableEntry<>(e.getKey(), e.getValue())) + .collect(toList()))); } /** @@ -98,18 +97,18 @@ static void registerConfigurationParameters( * Get the value of this item, which is cached on the first call of this method and will not * change afterward. * - *

        The value of an item {@code some_opt} is obtained from the following sources in - * increasing order of precedence: + *

        The value of an item {@code some_opt} is obtained from the following sources in increasing + * order of precedence: + * *

          - *
        1. the default value;
        2. - *
        3. {@code META-INF/MANIFEST.MF} attributes {@code Jazzer-Some-Opt} on the - * classpath;
        4. - *
        5. the {@code JAZZER_SOME_OPT} environment variable;
        6. - *
        7. the {@code jazzer.some_opt} system property;
        8. - *
        9. the {@code jazzer.some_opt} JUnit configuration parameter (if {@link - * #registerConfigurationParameters(Function)} has been called);
        10. - *
        11. the {@code --some_opt} command-line argument (if {@link #registerCommandLineArgs(List) - * has been called}).
        12. + *
        13. the default value; + *
        14. {@code META-INF/MANIFEST.MF} attributes {@code Jazzer-Some-Opt} on the classpath; + *
        15. the {@code JAZZER_SOME_OPT} environment variable; + *
        16. the {@code jazzer.some_opt} system property; + *
        17. the {@code jazzer.some_opt} JUnit configuration parameter (if {@link + * #registerConfigurationParameters(Function)} has been called); + *
        18. the {@code --some_opt} command-line argument (if {@link #registerCommandLineArgs(List) + * has been called}). *
        */ @Override @@ -129,13 +128,14 @@ public final T get() throws IllegalOptionValueException { * taking the role of its default value, otherwise throws an {@link IllegalStateException}. * * @return {@code true} if the value of the item was not set explicitly and thus defaulted to - * {@code newValue}. + * {@code newValue}. */ public final boolean setIfDefault(T newValue) throws IllegalOptionValueException { if (value != null) { - throw new IllegalStateException(String.format( - "Attempted to set of option %s to %s, but it has already been read elsewhere", - propertyName(), newValue)); + throw new IllegalStateException( + String.format( + "Attempted to set of option %s to %s, but it has already been read elsewhere", + propertyName(), newValue)); } Optional explicitValue = getExplicitValue(); if (explicitValue.isPresent()) { @@ -166,16 +166,18 @@ protected T accumulate(T oldValue, T newValue) { } private Optional getExplicitValue() { - return Stream - .>>of(this::getFromManifest, this::getFromEnv, this::getFromProperties, - this::getFromConfigurationParameters, this::getFromCommandLineArguments) + return Stream.>>of( + this::getFromManifest, + this::getFromEnv, + this::getFromProperties, + this::getFromConfigurationParameters, + this::getFromCommandLineArguments) .flatMap(Supplier::get) .reduce(this::accumulate); } private Stream getFromCommandLineArguments() { - return cliArgs.orElse(emptyList()) - .stream() + return cliArgs.orElse(emptyList()).stream() .filter(e -> e.getKey().equals(cliArgName())) .map(Entry::getValue) .map(s -> fromStringOrThrow(s, "command-line argument " + cliArgName())); @@ -203,27 +205,37 @@ private Stream getFromManifest() { // The manifest entry that comes *last* on the class path should be evaluated *first* as it // has the *lowest* precedence. reverse(manifests); - return manifests.stream().flatMap(url -> { - try (InputStream inputStream = url.openStream()) { - return stream( - Optional.ofNullable( - new Manifest(inputStream).getMainAttributes().getValue(manifestAttributeName()))) - .map(s - -> fromStringOrThrow(s, - String.format("manifest attribute %s in %s", manifestAttributeName(), url))); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); + return manifests.stream() + .flatMap( + url -> { + try (InputStream inputStream = url.openStream()) { + return stream( + Optional.ofNullable( + new Manifest(inputStream) + .getMainAttributes() + .getValue(manifestAttributeName()))) + .map( + s -> + fromStringOrThrow( + s, + String.format( + "manifest attribute %s in %s", + manifestAttributeName(), url))); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); } catch (IOException e) { throw new UncheckedIOException(e); } } private T fromStringOrThrow(String rawValue, String what) throws IllegalOptionValueException { - return fromString(rawValue).orElseThrow(() - -> new IllegalOptionValueException(String.format( - "Invalid value for %s: %s", what, rawValue))); + return fromString(rawValue) + .orElseThrow( + () -> + new IllegalOptionValueException( + String.format("Invalid value for %s: %s", what, rawValue))); } String cliArgName() { diff --git a/src/main/java/com/code_intelligence/jazzer/driver/OptParser.java b/src/main/java/com/code_intelligence/jazzer/driver/OptParser.java index a6542aed3..87a0152fc 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/OptParser.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/OptParser.java @@ -17,55 +17,50 @@ package com.code_intelligence.jazzer.driver; import static java.lang.System.exit; -import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toCollection; -import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import com.code_intelligence.jazzer.utils.Log; import java.io.File; -import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.TreeMap; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; final class OptParser { - private static final String[] HELP_HEADER = new String[] { - "A coverage-guided, in-process fuzzer for the JVM", - "", - "Usage:", - String.format( - " java -cp jazzer.jar[%cclasspath_entries] com.code_intelligence.jazzer.Jazzer --target_class= [args...]", - File.separatorChar), - String.format( - " java -cp jazzer.jar[%cclasspath_entries] com.code_intelligence.jazzer.Jazzer --autofuzz= [args...]", - File.separatorChar), - "", - "In addition to the options listed below, Jazzer also accepts all", - "libFuzzer options described at:", - " https://llvm.org/docs/LibFuzzer.html#options", - "", - "Options:", - }; + private static final String[] HELP_HEADER = + new String[] { + "A coverage-guided, in-process fuzzer for the JVM", + "", + "Usage:", + String.format( + " java -cp jazzer.jar[%cclasspath_entries] com.code_intelligence.jazzer.Jazzer" + + " --target_class= [args...]", + File.separatorChar), + String.format( + " java -cp jazzer.jar[%cclasspath_entries] com.code_intelligence.jazzer.Jazzer" + + " --autofuzz= [args...]", + File.separatorChar), + "", + "In addition to the options listed below, Jazzer also accepts all", + "libFuzzer options described at:", + " https://llvm.org/docs/LibFuzzer.html#options", + "", + "Options:", + }; // All supported arguments are added to this set by the individual *Setting methods. private static final List> knownArgs = new ArrayList<>(); static String getHelpText() { - return Stream - .concat(Arrays.stream(HELP_HEADER), + return Stream.concat( + Arrays.stream(HELP_HEADER), knownArgs.stream().filter(Objects::nonNull).map(OptItem::toString)) .collect(joining("\n\n")); } @@ -99,16 +94,18 @@ static OptItem uint64Setting(String name, long defaultValue, String descri } static void registerAndValidateCommandLineArgs(List> cliArgs) { - Set allowedArgs = knownArgs.stream() - .filter(optItem -> !optItem.isInternal()) - .map(OptItem::cliArgName) - .collect(toSet()); - String invalidArgs = cliArgs.stream() - .map(Entry::getKey) - .filter(arg -> !allowedArgs.contains(arg)) - .distinct() - .map(arg -> "--" + arg) - .collect(joining(", ")); + Set allowedArgs = + knownArgs.stream() + .filter(optItem -> !optItem.isInternal()) + .map(OptItem::cliArgName) + .collect(toSet()); + String invalidArgs = + cliArgs.stream() + .map(Entry::getKey) + .filter(arg -> !allowedArgs.contains(arg)) + .distinct() + .map(arg -> "--" + arg) + .collect(joining(", ")); if (!invalidArgs.isEmpty()) { Log.error("Unknown arguments (list available arguments with --help): " + invalidArgs); diff --git a/src/main/java/com/code_intelligence/jazzer/driver/ReproducerTemplate.java b/src/main/java/com/code_intelligence/jazzer/driver/ReproducerTemplate.java index dd01ed0bc..403d7c826 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/ReproducerTemplate.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/ReproducerTemplate.java @@ -35,7 +35,8 @@ final class ReproducerTemplate { private static final String RAW_BYTES_INPUT = "byte[] input = java.util.Base64.getDecoder().decode(base64Bytes);"; private static final String FUZZED_DATA_PROVIDER_INPUT = - "com.code_intelligence.jazzer.api.CannedFuzzedDataProvider input = new com.code_intelligence.jazzer.api.CannedFuzzedDataProvider(base64Bytes);"; + "com.code_intelligence.jazzer.api.CannedFuzzedDataProvider input = new" + + " com.code_intelligence.jazzer.api.CannedFuzzedDataProvider(base64Bytes);"; private final String targetClass; private final boolean useFuzzedDataProvider; @@ -53,11 +54,13 @@ public ReproducerTemplate(String targetClass, boolean useFuzzedDataProvider) { */ public void dumpReproducer(String data, String sha) { String targetArg = useFuzzedDataProvider ? FUZZED_DATA_PROVIDER_INPUT : RAW_BYTES_INPUT; - String template = new BufferedReader( - new InputStreamReader(ReproducerTemplate.class.getResourceAsStream("Reproducer.java.tmpl"), - StandardCharsets.UTF_8)) - .lines() - .collect(Collectors.joining("\n")); + String template = + new BufferedReader( + new InputStreamReader( + ReproducerTemplate.class.getResourceAsStream("Reproducer.java.tmpl"), + StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.joining("\n")); String chunkedData = chunkStringLiteral(data); String javaSource = String.format(template, sha, chunkedData, targetClass, targetArg); Path javaPath = Paths.get(Opt.reproducerPath.get(), String.format("Crash_%s.java", sha)); @@ -67,8 +70,10 @@ public void dumpReproducer(String data, String sha) { Log.error(String.format("Failed to write Java reproducer to %s%n", javaPath)); e.printStackTrace(); } - Log.println(String.format("reproducer_path='%s'; Java reproducer written to %s%n", - Opt.reproducerPath.get(), javaPath)); + Log.println( + String.format( + "reproducer_path='%s'; Java reproducer written to %s%n", + Opt.reproducerPath.get(), javaPath)); } // The serialization of recorded FuzzedDataProvider invocations can get too long to be emitted @@ -77,8 +82,9 @@ public void dumpReproducer(String data, String sha) { private String chunkStringLiteral(String data) { ArrayList chunks = new ArrayList<>(); for (int i = 0; i <= data.length() / DATA_CHUNK_MAX_LENGTH; i++) { - chunks.add(data.substring( - i * DATA_CHUNK_MAX_LENGTH, Math.min((i + 1) * DATA_CHUNK_MAX_LENGTH, data.length()))); + chunks.add( + data.substring( + i * DATA_CHUNK_MAX_LENGTH, Math.min((i + 1) * DATA_CHUNK_MAX_LENGTH, data.length()))); } return String.join("\", \"", chunks); } diff --git a/src/main/java/com/code_intelligence/jazzer/driver/junit/JUnitRunner.java b/src/main/java/com/code_intelligence/jazzer/driver/junit/JUnitRunner.java index 9047d8143..1f58b610c 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/junit/JUnitRunner.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/junit/JUnitRunner.java @@ -70,14 +70,15 @@ public static boolean isSupported() { public static Optional create(String testClassName, List libFuzzerArgs) { // We want the test execution to be as lightweight as possible, so disable all auto-discover and // only register the test engine we are using for @FuzzTest, JUnit Jupiter. - LauncherConfig config = LauncherConfig.builder() - .addTestEngines(new JupiterTestEngine()) - .enableLauncherDiscoveryListenerAutoRegistration(false) - .enableLauncherSessionListenerAutoRegistration(false) - .enablePostDiscoveryFilterAutoRegistration(false) - .enableTestEngineAutoRegistration(false) - .enableTestExecutionListenerAutoRegistration(false) - .build(); + LauncherConfig config = + LauncherConfig.builder() + .addTestEngines(new JupiterTestEngine()) + .enableLauncherDiscoveryListenerAutoRegistration(false) + .enableLauncherSessionListenerAutoRegistration(false) + .enablePostDiscoveryFilterAutoRegistration(false) + .enableTestEngineAutoRegistration(false) + .enableTestExecutionListenerAutoRegistration(false) + .build(); Map indexedArgs = IntStream.range(0, libFuzzerArgs.size()) @@ -85,8 +86,7 @@ public static Optional create(String testClassName, List li .collect(Collectors.toMap(i -> "jazzer.internal.arg." + i, libFuzzerArgs::get)); LauncherDiscoveryRequestBuilder requestBuilder = - LauncherDiscoveryRequestBuilder - .request() + LauncherDiscoveryRequestBuilder.request() // JUnit's timeout handling interferes with libFuzzer as from the point of view of JUnit // all fuzz test invocations are combined in a single JUnit test method execution. // https://junit.org/junit5/docs/current/user-guide/#writing-tests-declarative-timeouts-mode @@ -98,12 +98,15 @@ public static Optional create(String testClassName, List li if (!Opt.targetMethod.get().isEmpty()) { // HACK: This depends on JUnit internals as we need to filter by method name without having to // specify the parameter types of the method. - requestBuilder.filters((PostDiscoveryFilter) testDescriptor - -> includedIf(!(testDescriptor instanceof MethodBasedTestDescriptor) - || ((MethodBasedTestDescriptor) testDescriptor) - .getTestMethod() - .getName() - .equals(Opt.targetMethod.get()))); + requestBuilder.filters( + (PostDiscoveryFilter) + testDescriptor -> + includedIf( + !(testDescriptor instanceof MethodBasedTestDescriptor) + || ((MethodBasedTestDescriptor) testDescriptor) + .getTestMethod() + .getName() + .equals(Opt.targetMethod.get()))); } LauncherDiscoveryRequest request = requestBuilder.build(); Launcher launcher = LauncherFactory.create(config); @@ -117,30 +120,34 @@ public static Optional create(String testClassName, List li public int run() { AtomicReference resultHolder = new AtomicReference<>(TestExecutionResult.successful()); - launcher.execute(testPlan, new TestExecutionListener() { - @Override - public void executionFinished( - TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { - // Lifecycle methods can fail too, which results in failed execution results on container - // nodes. We keep the last failing one with a stack trace. For tests, we also keep the stack - // traces of aborted tests so that we can show a warning. In JUnit Jupiter, tests and - // containers always fail with a throwable: - // https://github.com/junit-team/junit5/blob/ac31e9a7d58973db73496244dab4defe17ae563e/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ThrowableCollector.java#LL176C37-L176C37 - if ((testIdentifier.isTest() && testExecutionResult.getThrowable().isPresent()) - || testExecutionResult.getStatus() == FAILED) { - resultHolder.set(testExecutionResult); - } - if (testExecutionResult.getStatus() == FAILED - && testExecutionResult.getThrowable().isPresent()) { - resultHolder.set(testExecutionResult); - } - } + launcher.execute( + testPlan, + new TestExecutionListener() { + @Override + public void executionFinished( + TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { + // Lifecycle methods can fail too, which results in failed execution results on + // container + // nodes. We keep the last failing one with a stack trace. For tests, we also keep the + // stack + // traces of aborted tests so that we can show a warning. In JUnit Jupiter, tests and + // containers always fail with a throwable: + // https://github.com/junit-team/junit5/blob/ac31e9a7d58973db73496244dab4defe17ae563e/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ThrowableCollector.java#LL176C37-L176C37 + if ((testIdentifier.isTest() && testExecutionResult.getThrowable().isPresent()) + || testExecutionResult.getStatus() == FAILED) { + resultHolder.set(testExecutionResult); + } + if (testExecutionResult.getStatus() == FAILED + && testExecutionResult.getThrowable().isPresent()) { + resultHolder.set(testExecutionResult); + } + } - @Override - public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) { - entry.getKeyValuePairs().values().forEach(Log::info); - } - }); + @Override + public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) { + entry.getKeyValuePairs().values().forEach(Log::info); + } + }); TestExecutionResult result = resultHolder.get(); if (result.getStatus() != FAILED) { diff --git a/src/main/java/com/code_intelligence/jazzer/junit/AgentConfigurator.java b/src/main/java/com/code_intelligence/jazzer/junit/AgentConfigurator.java index a5f6ce174..3c132ac1d 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/AgentConfigurator.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/AgentConfigurator.java @@ -20,7 +20,6 @@ import static java.util.Collections.singletonList; import com.code_intelligence.jazzer.driver.Opt; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.extension.ExtensionContext; @@ -59,9 +58,21 @@ static void forFuzzing(ExtensionContext extensionContext) { private static void applyCommonConfiguration(ExtensionContext extensionContext) { Opt.registerConfigurationParameters(extensionContext::getConfigurationParameter); // Do not hook common IDE and JUnit classes and their dependencies. - Opt.customHookExcludes.setIfDefault(asList("com.google.testing.junit.**", "com.intellij.**", - "org.jetbrains.**", "io.github.classgraph.**", "junit.framework.**", "net.bytebuddy.**", - "org.apiguardian.**", "org.assertj.core.**", "org.hamcrest.**", "org.junit.**", - "org.opentest4j.**", "org.mockito.**", "org.apache.maven.**", "org.gradle.**")); + Opt.customHookExcludes.setIfDefault( + asList( + "com.google.testing.junit.**", + "com.intellij.**", + "org.jetbrains.**", + "io.github.classgraph.**", + "junit.framework.**", + "net.bytebuddy.**", + "org.apiguardian.**", + "org.assertj.core.**", + "org.hamcrest.**", + "org.junit.**", + "org.opentest4j.**", + "org.mockito.**", + "org.apache.maven.**", + "org.gradle.**")); } } diff --git a/src/main/java/com/code_intelligence/jazzer/junit/FuzzTest.java b/src/main/java/com/code_intelligence/jazzer/junit/FuzzTest.java index 2ebdbb9f8..cbd4555dd 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/FuzzTest.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/FuzzTest.java @@ -35,21 +35,19 @@ *

        Methods annotated with {@link FuzzTest} can take either of the following types of parameters: * *

        - *
        {@code byte[]}
        - *
        Raw byte input mutated by the fuzzer. Use this signature when your fuzz test naturally - * handles raw bytes (e.g. when fuzzing a binary format parser). This is the most efficient, but - * also the least convenient way to write a fuzz test.
        - * - *
        {@link com.code_intelligence.jazzer.api.FuzzedDataProvider}
        - *
        Provides convenience methods that generate instances of commonly used Java types from the raw - * fuzzer input. This is generally the best way to write fuzz tests.
        - * - *
        any non-zero number of parameters of any type
        - *
        In this case, Jazzer will rely on reflection and class path scanning to instantiate concrete - * arguments. While convenient and a good way to get started, fuzz tests using this feature will - * generally be less efficient than fuzz tests using any of the other possible signatures. Due to - * the reliance on class path scanning, any change to the class path may also render previous - * findings unreproducible.
        + *
        {@code byte[]} + *
        Raw byte input mutated by the fuzzer. Use this signature when your fuzz test naturally + * handles raw bytes (e.g. when fuzzing a binary format parser). This is the most efficient, + * but also the least convenient way to write a fuzz test. + *
        {@link com.code_intelligence.jazzer.api.FuzzedDataProvider} + *
        Provides convenience methods that generate instances of commonly used Java types from the + * raw fuzzer input. This is generally the best way to write fuzz tests. + *
        any non-zero number of parameters of any type + *
        In this case, Jazzer will rely on reflection and class path scanning to instantiate + * concrete arguments. While convenient and a good way to get started, fuzz tests using this + * feature will generally be less efficient than fuzz tests using any of the other possible + * signatures. Due to the reliance on class path scanning, any change to the class path may + * also render previous findings unreproducible. *
        * *

        Test modes

        @@ -57,6 +55,7 @@ * A fuzz test can be run in two modes: fuzzing and regression testing. * *

        Fuzzing

        + * *

        When the environment variable {@code JAZZER_FUZZ} is set to any non-empty value, fuzz tests * run in "fuzzing" mode. In this mode, the method annotated with {@link FuzzTest} are invoked * repeatedly with inputs that Jazzer generates and mutates based on feedback obtained from @@ -74,15 +73,16 @@ * will be skipped. * *

        Regression testing

        + * *

        By default, a fuzz test is executed as a regular JUnit {@link ParameterizedTest} running on a * fixed set of inputs. It can be run together with regular unit tests and used to verify that past * findings remain fixed. In IDEs with JUnit 5 integration, it can also be used to conveniently * debug individual findings. * *

        Fuzz tests are always executed on the empty input as well as all input files contained in the - * resource directory called {@code Inputs} in the current package. For example, - * all fuzz tests contained in the class {@code com.example.MyFuzzTests} would run on all files - * under {@code src/test/resources/com/example/MyFuzzTestsInputs}. + * resource directory called {@code Inputs} in the current package. For example, all + * fuzz tests contained in the class {@code com.example.MyFuzzTests} would run on all files under + * {@code src/test/resources/com/example/MyFuzzTestsInputs}. */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExecutor.java b/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExecutor.java index 989cbc4e8..5d3dc8e52 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExecutor.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExecutor.java @@ -77,15 +77,18 @@ public static FuzzTestExecutor prepare(ExtensionContext context, String maxDurat "FuzzTestExecutor#prepare can only be called once per test run"); } - List allSources = AnnotationSupport.findRepeatableAnnotations( - context.getRequiredTestMethod(), ArgumentsSource.class); + List allSources = + AnnotationSupport.findRepeatableAnnotations( + context.getRequiredTestMethod(), ArgumentsSource.class); // Non-empty as it always contains FuzzingArgumentsProvider. ArgumentsSource lastSource = allSources.get(allSources.size() - 1); // Ensure that our ArgumentsProviders run last so that we can record all the seeds generated by // user-provided ones. if (lastSource.value().getPackage() != FuzzTestExecutor.class.getPackage()) { - throw new IllegalArgumentException("@FuzzTest must be the last annotation on a fuzz test," - + " but it came after the (meta-)annotation " + lastSource); + throw new IllegalArgumentException( + "@FuzzTest must be the last annotation on a fuzz test," + + " but it came after the (meta-)annotation " + + lastSource); } List originalLibFuzzerArgs = getLibFuzzerArgs(context); @@ -121,7 +124,7 @@ public static FuzzTestExecutor prepare(ExtensionContext context, String maxDurat // up IntelliJ's memory usage. libFuzzerArgs.add("-rss_limit_mb=0"); if (Utils.permissivelyParseBoolean( - context.getConfigurationParameter("jazzer.valueprofile").orElse("false"))) { + context.getConfigurationParameter("jazzer.valueprofile").orElse("false"))) { libFuzzerArgs.add("-use_value_profile=1"); } @@ -134,24 +137,24 @@ public static FuzzTestExecutor prepare(ExtensionContext context, String maxDurat } private static Optional translateJUnitTimeoutToLibFuzzerFlag(ExtensionContext context) { - return Stream - .>>of( - () - -> AnnotationSupport.findAnnotation(context.getRequiredTestMethod(), Timeout.class) - .map(timeout -> timeout.unit().toSeconds(timeout.value())), - () - -> AnnotationSupport.findAnnotation(context.getRequiredTestClass(), Timeout.class) - .map(timeout -> timeout.unit().toSeconds(timeout.value())), - () - -> context.getConfigurationParameter( + return Stream.>>of( + () -> + AnnotationSupport.findAnnotation(context.getRequiredTestMethod(), Timeout.class) + .map(timeout -> timeout.unit().toSeconds(timeout.value())), + () -> + AnnotationSupport.findAnnotation(context.getRequiredTestClass(), Timeout.class) + .map(timeout -> timeout.unit().toSeconds(timeout.value())), + () -> + context.getConfigurationParameter( "junit.jupiter.execution.timeout.testtemplate.method.default", Utils::parseJUnitTimeoutValueToSeconds), - () - -> context.getConfigurationParameter( + () -> + context.getConfigurationParameter( "junit.jupiter.execution.timeout.testable.method.default", Utils::parseJUnitTimeoutValueToSeconds), - () - -> context.getConfigurationParameter("junit.jupiter.execution.timeout.default", + () -> + context.getConfigurationParameter( + "junit.jupiter.execution.timeout.default", Utils::parseJUnitTimeoutValueToSeconds)) .map(Supplier::get) .filter(Optional::isPresent) @@ -202,13 +205,14 @@ private static Path addInputAndSeedDirs(ExtensionContext context, List l Optional findingsDirectory = inputsDirectorySourcePath(fuzzTestClass, fuzzTestMethod, baseDir); if (!findingsDirectory.isPresent()) { - context.publishReportEntry(String.format( - "Collecting crashing inputs in the project root directory.\nIf you want to keep them " - + "organized by fuzz test and automatically run them as regression tests with " - + "JUnit Jupiter, create a test resource directory called '%s' in package '%s' " - + "and move the files there.", - inputsDirectoryResourcePath(fuzzTestClass, fuzzTestMethod), - fuzzTestClass.getPackage().getName())); + context.publishReportEntry( + String.format( + "Collecting crashing inputs in the project root directory.\nIf you want to keep them " + + "organized by fuzz test and automatically run them as regression tests with " + + "JUnit Jupiter, create a test resource directory called '%s' in package '%s' " + + "and move the files there.", + inputsDirectoryResourcePath(fuzzTestClass, fuzzTestMethod), + fuzzTestClass.getPackage().getName())); } // We prefer the inputs directory on the classpath, if it exists, as that is more reliable than @@ -229,7 +233,7 @@ private static Path addInputAndSeedDirs(ExtensionContext context, List l if (inputsDirectoryUrl != null && !findingsDirectory.isPresent()) { context.publishReportEntry( "When running Jazzer fuzz tests from a JAR rather than class files, the inputs " - + "directory isn't used unless it is located under src/test/resources/..."); + + "directory isn't used unless it is located under src/test/resources/..."); } inputsDirectory = findingsDirectory; } @@ -239,17 +243,17 @@ private static Path addInputAndSeedDirs(ExtensionContext context, List l inputsDirectory.ifPresent(dir -> libFuzzerArgs.add(dir.toAbsolutePath().toString())); Path javaSeedsDir = Files.createTempDirectory("jazzer-java-seeds"); libFuzzerArgs.add(javaSeedsDir.toAbsolutePath().toString()); - libFuzzerArgs.add(String.format("-artifact_prefix=%s%c", - findingsDirectory.orElse(baseDir).toAbsolutePath(), File.separatorChar)); + libFuzzerArgs.add( + String.format( + "-artifact_prefix=%s%c", + findingsDirectory.orElse(baseDir).toAbsolutePath(), File.separatorChar)); return javaSeedsDir; } - /** - * Returns the list of arguments set on the command line. - */ + /** Returns the list of arguments set on the command line. */ private static List getLibFuzzerArgs(ExtensionContext extensionContext) { List args = new ArrayList<>(); - for (int i = 0;; i++) { + for (int i = 0; ; i++) { Optional arg = extensionContext.getConfigurationParameter("jazzer.internal.arg." + i); if (!arg.isPresent()) { break; @@ -259,8 +263,9 @@ private static List getLibFuzzerArgs(ExtensionContext extensionContext) return args; } - static void configureAndInstallAgent(ExtensionContext extensionContext, String maxDuration, - long maxExecutions) throws IOException { + static void configureAndInstallAgent( + ExtensionContext extensionContext, String maxDuration, long maxExecutions) + throws IOException { if (!agentInstalled.compareAndSet(false, true)) { return; } @@ -275,7 +280,8 @@ static void configureAndInstallAgent(ExtensionContext extensionContext, String m } static FuzzTestExecutor fromContext(ExtensionContext extensionContext) { - return extensionContext.getRoot() + return extensionContext + .getRoot() .getStore(Namespace.GLOBAL) .get(FuzzTestExecutor.class, FuzzTestExecutor.class); } @@ -303,27 +309,33 @@ public void addSeed(byte[] bytes) throws IOException { } @SuppressWarnings("OptionalGetWithoutIsPresent") - public Optional execute(ReflectiveInvocationContext invocationContext, - ExtensionContext extensionContext, SeedSerializer seedSerializer) { + public Optional execute( + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext, + SeedSerializer seedSerializer) { if (seedSerializer instanceof AutofuzzSeedSerializer) { - FuzzTargetHolder.fuzzTarget = FuzzTargetHolder.autofuzzFuzzTarget(() -> { - // Provide an empty throws declaration to prevent autofuzz from - // ignoring the defined test exceptions. All exceptions in tests - // should cause them to fail. - Map[]> throwsDeclarations = new HashMap<>(1); - throwsDeclarations.put(invocationContext.getExecutable(), new Class[0]); - - com.code_intelligence.jazzer.autofuzz.FuzzTarget.setTarget( - new Executable[] {invocationContext.getExecutable()}, - invocationContext.getTarget().get(), invocationContext.getExecutable().toString(), - Collections.emptySet(), throwsDeclarations); - return null; - }); + FuzzTargetHolder.fuzzTarget = + FuzzTargetHolder.autofuzzFuzzTarget( + () -> { + // Provide an empty throws declaration to prevent autofuzz from + // ignoring the defined test exceptions. All exceptions in tests + // should cause them to fail. + Map[]> throwsDeclarations = new HashMap<>(1); + throwsDeclarations.put(invocationContext.getExecutable(), new Class[0]); + + com.code_intelligence.jazzer.autofuzz.FuzzTarget.setTarget( + new Executable[] {invocationContext.getExecutable()}, + invocationContext.getTarget().get(), + invocationContext.getExecutable().toString(), + Collections.emptySet(), + throwsDeclarations); + return null; + }); } else { FuzzTargetHolder.fuzzTarget = - new FuzzTargetHolder.FuzzTarget(invocationContext.getExecutable(), - () - -> invocationContext.getTarget().get(), + new FuzzTargetHolder.FuzzTarget( + invocationContext.getExecutable(), + () -> invocationContext.getTarget().get(), JUnitLifecycleMethodsInvoker.of(extensionContext)); } @@ -332,10 +344,11 @@ public Optional execute(ReflectiveInvocationContext invocatio // like --keep_going. AtomicReference atomicFinding = new AtomicReference<>(); if (!isRunFromCommandLine) { - FuzzTargetRunner.registerFindingHandler(t -> { - atomicFinding.set(t); - return false; - }); + FuzzTargetRunner.registerFindingHandler( + t -> { + atomicFinding.set(t); + return false; + }); } int exitCode = FuzzTargetRunner.startLibFuzzer(libFuzzerArgs); diff --git a/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExtensions.java b/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExtensions.java index 2c0497acc..9259a09bb 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExtensions.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExtensions.java @@ -39,8 +39,10 @@ class FuzzTestExtensions private static Field hooksEnabledField; @Override - public void interceptTestTemplateMethod(Invocation invocation, - ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) + public void interceptTestTemplateMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) throws Throwable { FuzzTest fuzzTest = AnnotationSupport.findAnnotation(invocationContext.getExecutable(), FuzzTest.class).get(); @@ -69,10 +71,12 @@ public void interceptTestTemplateMethod(Invocation invocation, /** * Mimics the logic of Jazzer's FuzzTargetRunner, which reports findings in the following way: + * *

          *
        1. If a hook used Jazzer#reportFindingFromHook to explicitly report a finding, the last such - * finding, as stored in JazzerInternal#lastFinding, is reported.
        2. If the fuzz target method - * threw a Throwable, that is reported.
        3. 3. Otherwise, nothing is reported. + * finding, as stored in JazzerInternal#lastFinding, is reported. + *
        4. If the fuzz target method threw a Throwable, that is reported. + *
        5. 3. Otherwise, nothing is reported. *
        */ private static void runWithHooks(Invocation invocation) throws Throwable { @@ -86,7 +90,8 @@ private static void runWithHooks(Invocation invocation) throws Throwable { // * Using a dedicated class loader for @FuzzTests: First-class support for this isn't // available in JUnit 5 (https://github.com/junit-team/junit5/issues/201), but // third-party extensions have done it: - // https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathExtension.java + // + // https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathExtension.java // However, as this involves launching a new test run as part of running a test, this // introduces a number of inconsistencies if applied on the test method rather than test // class level. For example, @BeforeAll methods will have to be run twice in different class @@ -105,13 +110,16 @@ private static void runWithHooks(Invocation invocation) throws Throwable { } } - private static void startFuzzing(Invocation invocation, - ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) + private static void startFuzzing( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) throws Throwable { invocation.skip(); - Optional throwable = FuzzTestExecutor.fromContext(extensionContext) - .execute(invocationContext, extensionContext, - getOrCreateSeedSerializer(extensionContext)); + Optional throwable = + FuzzTestExecutor.fromContext(extensionContext) + .execute( + invocationContext, extensionContext, getOrCreateSeedSerializer(extensionContext)); if (throwable.isPresent()) { throw throwable.get(); } @@ -131,23 +139,27 @@ private void recordSeedForFuzzing(List arguments, ExtensionContext exten public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext extensionContext) { if (!Utils.isFuzzing(extensionContext)) { return ConditionEvaluationResult.enabled( - "Regression tests are run instead of fuzzing since JAZZER_FUZZ has not been set to a non-empty value"); + "Regression tests are run instead of fuzzing since JAZZER_FUZZ has not been set to a" + + " non-empty value"); } // Only fuzz the first @FuzzTest that makes it here. if (FuzzTestExtensions.fuzzTestMethod.compareAndSet( null, extensionContext.getRequiredTestMethod()) - || extensionContext.getRequiredTestMethod().equals( - FuzzTestExtensions.fuzzTestMethod.get())) { + || extensionContext + .getRequiredTestMethod() + .equals(FuzzTestExtensions.fuzzTestMethod.get())) { return ConditionEvaluationResult.enabled( "Fuzzing " + extensionContext.getRequiredTestMethod()); } return ConditionEvaluationResult.disabled( - "Only one fuzz test can be run at a time, but multiple tests have been annotated with @FuzzTest"); + "Only one fuzz test can be run at a time, but multiple tests have been annotated with" + + " @FuzzTest"); } private static SeedSerializer getOrCreateSeedSerializer(ExtensionContext extensionContext) { Method method = extensionContext.getRequiredTestMethod(); - return extensionContext.getStore(Namespace.create(FuzzTestExtensions.class, method)) + return extensionContext + .getStore(Namespace.create(FuzzTestExtensions.class, method)) .getOrComputeIfAbsent( SeedSerializer.class, unused -> SeedSerializer.of(method), SeedSerializer.class); } diff --git a/src/main/java/com/code_intelligence/jazzer/junit/JUnitLifecycleMethodsInvoker.java b/src/main/java/com/code_intelligence/jazzer/junit/JUnitLifecycleMethodsInvoker.java index 2a5d552c9..4d1c0e728 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/JUnitLifecycleMethodsInvoker.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/JUnitLifecycleMethodsInvoker.java @@ -34,8 +34,8 @@ import org.junit.jupiter.engine.extension.ExtensionRegistry; /** - * Adapts JUnit BeforeEach and AfterEach callbacks to - * {@link com.code_intelligence.jazzer.driver.FuzzTargetRunner} lifecycle hooks. + * Adapts JUnit BeforeEach and AfterEach callbacks to {@link + * com.code_intelligence.jazzer.driver.FuzzTargetRunner} lifecycle hooks. */ public class JUnitLifecycleMethodsInvoker implements LifecycleMethodsInvoker { private final ThrowingRunnable[] beforeEachExecutionRunnables; @@ -62,26 +62,27 @@ static LifecycleMethodsInvoker of(ExtensionContext extensionContext) { // are turned into extensions using an internal adapter class, BeforeEachMethodAdapter. // https://junit.org/junit5/docs/current/user-guide/#extensions-execution-order-wrapping-behavior ArrayList beforeEachMethods = - Stream - .concat( + Stream.concat( extensionRegistry.stream(BeforeEachCallback.class) .map(callback -> () -> callback.beforeEach(extensionContext)), extensionRegistry.stream(BeforeEachMethodAdapter.class) - .map(callback - -> () - -> callback.invokeBeforeEachMethod( - extensionContext, extensionRegistry))) + .map( + callback -> + () -> + callback.invokeBeforeEachMethod( + extensionContext, extensionRegistry))) .collect(toCollection(ArrayList::new)); ArrayList afterEachMethods = - Stream - .concat( + Stream.concat( extensionRegistry.stream(AfterEachCallback.class) .map(callback -> () -> callback.afterEach(extensionContext)), extensionRegistry.stream(AfterEachMethodAdapter.class) - .map(callback - -> () - -> callback.invokeAfterEachMethod(extensionContext, extensionRegistry))) + .map( + callback -> + () -> + callback.invokeAfterEachMethod( + extensionContext, extensionRegistry))) .collect(toCollection(ArrayList::new)); // JUnit calls AfterEach methods in reverse order of registration so that the methods registered // first run last. @@ -89,7 +90,7 @@ static LifecycleMethodsInvoker of(ExtensionContext extensionContext) { return new JUnitLifecycleMethodsInvoker( Stream.concat(afterEachMethods.stream(), beforeEachMethods.stream()) - .toArray(ThrowingRunnable[] ::new)); + .toArray(ThrowingRunnable[]::new)); } private static Optional getExtensionRegistryViaHack( @@ -105,14 +106,16 @@ private static Optional getExtensionRegistryViaHack( return Arrays.stream(DefaultExecutableInvoker.class.getDeclaredFields()) .filter(field -> field.getType() == ExtensionRegistry.class) .findFirst() - .flatMap(extensionRegistryField -> { - DefaultExecutableInvoker invoker = - (DefaultExecutableInvoker) extensionContext.getExecutableInvoker(); - long extensionRegistryFieldOffset = - UnsafeProvider.getUnsafe().objectFieldOffset(extensionRegistryField); - return Optional.ofNullable((ExtensionRegistry) UnsafeProvider.getUnsafe().getObject( - invoker, extensionRegistryFieldOffset)); - }); + .flatMap( + extensionRegistryField -> { + DefaultExecutableInvoker invoker = + (DefaultExecutableInvoker) extensionContext.getExecutableInvoker(); + long extensionRegistryFieldOffset = + UnsafeProvider.getUnsafe().objectFieldOffset(extensionRegistryField); + return Optional.ofNullable( + (ExtensionRegistry) + UnsafeProvider.getUnsafe().getObject(invoker, extensionRegistryFieldOffset)); + }); } @Override diff --git a/src/main/java/com/code_intelligence/jazzer/junit/SeedArgumentsProvider.java b/src/main/java/com/code_intelligence/jazzer/junit/SeedArgumentsProvider.java index 687a1f7f7..30d97a14b 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/SeedArgumentsProvider.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/SeedArgumentsProvider.java @@ -60,44 +60,51 @@ public Stream provideArguments(ExtensionContext extensionCo if (Utils.isCoverageAgentPresent() && Files.isDirectory(Utils.generatedCorpusPath(testClass, testMethod))) { - rawSeeds = Stream.concat(rawSeeds, - walkInputsInPath(Utils.generatedCorpusPath(testClass, testMethod), Integer.MAX_VALUE)); + rawSeeds = + Stream.concat( + rawSeeds, + walkInputsInPath( + Utils.generatedCorpusPath(testClass, testMethod), Integer.MAX_VALUE)); } SeedSerializer serializer = SeedSerializer.of(testMethod); return rawSeeds - .map(entry -> { - Object[] args = serializer.read(entry.getValue()); - args[0] = named(entry.getKey(), args[0]); - return arguments(args); - }) - .onClose(() -> { - if (!isFuzzing(extensionContext)) { - extensionContext.publishReportEntry( - "No fuzzing has been performed, the fuzz test has only been executed on the fixed " - + "set of inputs in the seed corpus.\n" - + "To start fuzzing, run a test with the environment variable JAZZER_FUZZ set to a " - + "non-empty value."); - } - if (!serializer.allReadsValid()) { - extensionContext.publishReportEntry( - "Some files in the seed corpus do not match the fuzz target signature.\n" - + "This indicates that they were generated with a different signature and may cause " - + "issues reproducing previous findings."); - } - }); + .map( + entry -> { + Object[] args = serializer.read(entry.getValue()); + args[0] = named(entry.getKey(), args[0]); + return arguments(args); + }) + .onClose( + () -> { + if (!isFuzzing(extensionContext)) { + extensionContext.publishReportEntry( + "No fuzzing has been performed, the fuzz test has only been executed on the" + + " fixed set of inputs in the seed corpus.\n" + + "To start fuzzing, run a test with the environment variable JAZZER_FUZZ" + + " set to a non-empty value."); + } + if (!serializer.allReadsValid()) { + extensionContext.publishReportEntry( + "Some files in the seed corpus do not match the fuzz target signature.\n" + + "This indicates that they were generated with a different signature and" + + " may cause issues reproducing previous findings."); + } + }); } /** - * Used in regression mode to get test cases for the associated {@code testMethod} - * This will return a stream of files consisting of: + * Used in regression mode to get test cases for the associated {@code testMethod} This will + * return a stream of files consisting of: + * *
          - *
        • {@code resources//Inputs/*}
        • - *
        • {@code resources//Inputs//**}
        • + *
        • {@code resources//Inputs/*} + *
        • {@code resources//Inputs//**} *
        + * * Or the equivalent behavior on resources inside a jar file. - *

        - * Note that the first {@code Inputs} path will not recursively search all + * + *

        Note that the first {@code Inputs} path will not recursively search all * directories but only gives files in that directory whereas the {@code } * directory is searched recursively. This allows for multiple tests to share inputs without * needing to explicitly copy them into each test's directory. @@ -135,15 +142,16 @@ private Stream> walkInputs(Class testClass, Method Path classPathInJar = jar.getPath(pathInJar); - return Stream - .concat(walkClassInputs(classPathInJar), walkTestInputs(classPathInJar, testMethod)) - .onClose(() -> { - try { - jar.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); + return Stream.concat( + walkClassInputs(classPathInJar), walkTestInputs(classPathInJar, testMethod)) + .onClose( + () -> { + try { + jar.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } else { throw new IOException( "Unsupported protocol for inputs resource directory: " + classInputsDirUrl); @@ -152,9 +160,9 @@ private Stream> walkInputs(Class testClass, Method /** * Walks over the inputs for the method being tested, recurses into subdirectories + * * @param classInputsPath the path of the class being tested, used as the base path where the test - * method's directory - * should be + * method's directory should be * @param testMethod the method being tested * @return a stream of all files under {@code /} * @throws IOException can be thrown by the underlying call to {@link Files#find} @@ -171,10 +179,10 @@ private static Stream> walkTestInputs( /** * Walks over the inputs for the class being tested. Does not recurse into subdirectories + * * @param path the path to search to files * @return a stream of all files (without directories) within {@code path}. If {@code path} is not - * found, an empty - * stream is returned. + * found, an empty stream is returned. * @throws IOException can be thrown by the underlying call to {@link Files#find} */ private static Stream> walkClassInputs(Path path) throws IOException { @@ -189,10 +197,11 @@ private static Stream> walkClassInputs(Path path) thro /** * Gets a sorted stream of all files (without directories) within under the given {@code path} + * * @param path the path to walk * @param depth the maximum depth of subdirectories to search from within {@code path}. 1 - * indicates it should return - * only the files directly in {@code path} and not search any of its subdirectories + * indicates it should return only the files directly in {@code path} and not search any of + * its subdirectories * @return a stream of file name -> file contents as a raw byte array * @throws IOException can be thrown by the call to {@link Files#find(Path, int, BiPredicate, * FileVisitOption...)} @@ -201,18 +210,19 @@ private static Stream> walkInputsInPath(Path path, int throws IOException { // @ParameterTest automatically closes Streams and AutoCloseable instances. // noinspection resource - return Files - .find(path, depth, - (fileOrDir, basicFileAttributes) - -> !basicFileAttributes.isDirectory(), + return Files.find( + path, + depth, + (fileOrDir, basicFileAttributes) -> !basicFileAttributes.isDirectory(), FileVisitOption.FOLLOW_LINKS) // JUnit identifies individual runs of a `@ParameterizedTest` via their invocation number. // In order to get reproducible behavior e.g. when trying to debug a particular input, all // inputs thus have to be provided in deterministic order. .sorted() - .map(file - -> new SimpleImmutableEntry<>( - file.getFileName().toString(), readAllBytesUnchecked(file))); + .map( + file -> + new SimpleImmutableEntry<>( + file.getFileName().toString(), readAllBytesUnchecked(file))); } private static byte[] readAllBytesUnchecked(Path path) { diff --git a/src/main/java/com/code_intelligence/jazzer/junit/SeedSerializer.java b/src/main/java/com/code_intelligence/jazzer/junit/SeedSerializer.java index 83826df46..55d51ae65 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/SeedSerializer.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/SeedSerializer.java @@ -28,6 +28,7 @@ interface SeedSerializer { Object[] read(byte[] bytes); + default boolean allReadsValid() { return true; } @@ -38,11 +39,12 @@ default boolean allReadsValid() { /** * Creates specialized {@link SeedSerializer} instances for the following method parameters: + * *

          *
        • {@code byte[]} *
        • {@code FuzzDataProvider} *
        • Any other types will attempt to be created using either Autofuzz or the experimental - * mutator framework if {@link Opt}'s {@code experimentalMutator} is set. + * mutator framework if {@link Opt}'s {@code experimentalMutator} is set. *
        */ static SeedSerializer of(Method method) { @@ -58,7 +60,8 @@ static SeedSerializer of(Method method) { } else { Optional argumentsMutator = Opt.experimentalMutator.get() ? ArgumentsMutator.forMethod(method) : Optional.empty(); - return argumentsMutator.map(ArgumentsMutatorSeedSerializer::new) + return argumentsMutator + .map(ArgumentsMutatorSeedSerializer::new) .orElseGet(() -> new AutofuzzSeedSerializer(method)); } } diff --git a/src/main/java/com/code_intelligence/jazzer/junit/Utils.java b/src/main/java/com/code_intelligence/jazzer/junit/Utils.java index 64fb79a0d..5f277ad58 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/Utils.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/Utils.java @@ -79,8 +79,7 @@ class Utils { * will have the form {@code Inputs/} */ static String inputsDirectoryResourcePath(Class testClass, Method testMethod) { - return testClass.getSimpleName() + "Inputs" - + "/" + testMethod.getName(); + return testClass.getSimpleName() + "Inputs" + "/" + testMethod.getName(); } static String inputsDirectoryResourcePath(Class testClass) { @@ -126,9 +125,7 @@ static Path generatedCorpusPath(Class testClass, Method testMethod) { return Paths.get(".cifuzz-corpus", testClass.getName(), testMethod.getName()); } - /** - * Returns a heuristic default value for jazzer.instrument based on the test class. - */ + /** Returns a heuristic default value for jazzer.instrument based on the test class. */ static List getLegacyInstrumentationFilter(Class testClass) { // This is an extremely rough "implementation" of the public suffix list algorithm // (https://publicsuffix.org/): It tries to guess the shortest prefix of the package name that @@ -140,7 +137,8 @@ static List getLegacyInstrumentationFilter(Class testClass) { String packageName = testClass.getPackage().getName(); String[] packageSegments = packageName.split("\\."); int numSegments = 2; - if (packageSegments.length > 2 && packageSegments[0].equals("com") + if (packageSegments.length > 2 + && packageSegments[0].equals("com") && packageSegments[1].equals("github")) { numSegments = 3; } @@ -158,46 +156,55 @@ static List getLegacyInstrumentationFilter(Class testClass) { */ static Optional> getClassPathBasedInstrumentationFilter(String classPath) { List includes = - CLASSPATH_SPLITTER.splitAsStream(classPath) + CLASSPATH_SPLITTER + .splitAsStream(classPath) .map(Paths::get) // We consider classpath entries that are directories rather than jar files to contain // the classes of the current project rather than external dependencies. This is just a // heuristic and breaks with build systems that package all classes in jar files, e.g. // with Bazel. .filter(Files::isDirectory) - .flatMap(root -> { - HashSet pkgs = new HashSet<>(); - try { - Files.walkFileTree(root, new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory( - Path dir, BasicFileAttributes basicFileAttributes) throws IOException { - try (Stream entries = Files.list(dir)) { - // If a directory contains a .class file, we add an include filter matching it - // and all subdirectories. - // Special case: If there is a class defined at the root, only the unnamed - // package is included, so continue with the traversal of subdirectories - // to discover additional includes. - if (entries.filter(path -> path.toString().endsWith(".class")) - .anyMatch(Files::isRegularFile)) { - Path pkgPath = root.relativize(dir); - pkgs.add(pkgPath); - if (pkgPath.toString().isEmpty()) { - return FileVisitResult.CONTINUE; - } else { - return FileVisitResult.SKIP_SUBTREE; - } - } - } - return FileVisitResult.CONTINUE; + .flatMap( + root -> { + HashSet pkgs = new HashSet<>(); + try { + Files.walkFileTree( + root, + new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory( + Path dir, BasicFileAttributes basicFileAttributes) + throws IOException { + try (Stream entries = Files.list(dir)) { + // If a directory contains a .class file, we add an include filter + // matching it + // and all subdirectories. + // Special case: If there is a class defined at the root, only the + // unnamed + // package is included, so continue with the traversal of + // subdirectories + // to discover additional includes. + if (entries + .filter(path -> path.toString().endsWith(".class")) + .anyMatch(Files::isRegularFile)) { + Path pkgPath = root.relativize(dir); + pkgs.add(pkgPath); + if (pkgPath.toString().isEmpty()) { + return FileVisitResult.CONTINUE; + } else { + return FileVisitResult.SKIP_SUBTREE; + } + } + } + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + // This is only a best-effort heuristic anyway, ignore this directory. + return Stream.of(); } - }); - } catch (IOException e) { - // This is only a best-effort heuristic anyway, ignore this directory. - return Stream.of(); - } - return pkgs.stream(); - }) + return pkgs.stream(); + }) .distinct() .collect(toList()); if (includes.isEmpty()) { @@ -214,26 +221,27 @@ public FileVisitResult preVisitDirectory( private static final Pattern COVERAGE_AGENT_ARG = Pattern.compile("-javaagent:.*(?:intellij-coverage-agent|jacoco).*"); + static boolean isCoverageAgentPresent() { - return ManagementFactory.getRuntimeMXBean().getInputArguments().stream().anyMatch( - s -> COVERAGE_AGENT_ARG.matcher(s).matches()); + return ManagementFactory.getRuntimeMXBean().getInputArguments().stream() + .anyMatch(s -> COVERAGE_AGENT_ARG.matcher(s).matches()); } private static final boolean IS_FUZZING_ENV = System.getenv("JAZZER_FUZZ") != null && !System.getenv("JAZZER_FUZZ").isEmpty(); + static boolean isFuzzing(ExtensionContext extensionContext) { return IS_FUZZING_ENV || runFromCommandLine(extensionContext); } static boolean runFromCommandLine(ExtensionContext extensionContext) { - return extensionContext.getConfigurationParameter("jazzer.internal.commandLine") + return extensionContext + .getConfigurationParameter("jazzer.internal.commandLine") .map(Boolean::parseBoolean) .orElse(false); } - /** - * Returns true if and only if the value is equal to "true", "1", or "yes" case-insensitively. - */ + /** Returns true if and only if the value is equal to "true", "1", or "yes" case-insensitively. */ static boolean permissivelyParseBoolean(String value) { return value.equalsIgnoreCase("true") || value.equals("1") || value.equalsIgnoreCase("yes"); } @@ -271,16 +279,17 @@ static long parseJUnitTimeoutValueToSeconds(String value) { * @param displayName the display name to assign to every argument */ static Arguments getMarkedArguments(Method method, String displayName) { - return arguments(stream(method.getParameterTypes()) - .map(Utils::getMarkedInstance) - // Wrap in named as toString may crash on marked instances. - .map(arg -> named(displayName, arg)) - .toArray(Object[] ::new)); + return arguments( + stream(method.getParameterTypes()) + .map(Utils::getMarkedInstance) + // Wrap in named as toString may crash on marked instances. + .map(arg -> named(displayName, arg)) + .toArray(Object[]::new)); } /** * @return {@code true} if and only if the arguments for this test method invocation were created - * with {@link #getMarkedArguments} + * with {@link #getMarkedArguments} */ static boolean isMarkedInvocation(ReflectiveInvocationContext invocationContext) { if (invocationContext.getArguments().stream().anyMatch(Utils::isMarkedInstance)) { @@ -294,12 +303,13 @@ static boolean isMarkedInvocation(ReflectiveInvocationContext invocation } } - private static final ClassValue uniqueInstanceCache = new ClassValue() { - @Override - protected Object computeValue(Class clazz) { - return makeMarkedInstance(clazz); - } - }; + private static final ClassValue uniqueInstanceCache = + new ClassValue() { + @Override + protected Object computeValue(Class clazz) { + return makeMarkedInstance(clazz); + } + }; private static final Set uniqueInstances = newSetFromMap(new IdentityHashMap<>()); // Visible for testing. diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/ArgumentsMutator.java b/src/main/java/com/code_intelligence/jazzer/mutation/ArgumentsMutator.java index bf0666d6e..73d828511 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/ArgumentsMutator.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/ArgumentsMutator.java @@ -52,12 +52,11 @@ public final class ArgumentsMutator { private Object[] arguments; /** - * True if the arguments array has already been passed to a user-provided function or exposed - * via {@link #getArguments()} without going through {@link ProductMutator#detach(Object[])}. - * In this case the arguments may have been modified externally, which interferes with mutation, - * or could have been stored in static state that would be affected by future mutations. - * Arguments should either be detached or not be reused after being exposed, which is enforced by - * this variable. + * True if the arguments array has already been passed to a user-provided function or exposed via + * {@link #getArguments()} without going through {@link ProductMutator#detach(Object[])}. In this + * case the arguments may have been modified externally, which interferes with mutation, or could + * have been stored in static state that would be affected by future mutations. Arguments should + * either be detached or not be reused after being exposed, which is enforced by this variable. */ private boolean argumentsExposed; @@ -68,22 +67,27 @@ private ArgumentsMutator(Object instance, Method method, ProductMutator productM } private static String prettyPrintMethod(Method method) { - return format("%s.%s(%s)", method.getDeclaringClass().getName(), method.getName(), + return format( + "%s.%s(%s)", + method.getDeclaringClass().getName(), + method.getName(), stream(method.getAnnotatedParameterTypes()).map(Object::toString).collect(joining(", "))); } public static ArgumentsMutator forInstanceMethodOrThrow(Object instance, Method method) { return forInstanceMethod(Mutators.newFactory(), instance, method) - .orElseThrow(() - -> new IllegalArgumentException( - "Failed to construct mutator for " + prettyPrintMethod(method))); + .orElseThrow( + () -> + new IllegalArgumentException( + "Failed to construct mutator for " + prettyPrintMethod(method))); } public static ArgumentsMutator forStaticMethodOrThrow(Method method) { return forStaticMethod(Mutators.newFactory(), method) - .orElseThrow(() - -> new IllegalArgumentException( - "Failed to construct mutator for " + prettyPrintMethod(method))); + .orElseThrow( + () -> + new IllegalArgumentException( + "Failed to construct mutator for " + prettyPrintMethod(method))); } public static Optional forMethod(Method method) { @@ -94,7 +98,8 @@ public static Optional forInstanceMethod( MutatorFactory mutatorFactory, Object instance, Method method) { require(!isStatic(method), "method must not be static"); requireNonNull(instance, "instance must not be null"); - require(method.getDeclaringClass().isInstance(instance), + require( + method.getDeclaringClass().isInstance(instance), format("instance is a %s, expected %s", instance.getClass(), method.getDeclaringClass())); return forMethod(mutatorFactory, instance, method); } @@ -112,8 +117,8 @@ public static Optional forMethod( validateAnnotationUsage(parameter); } return toArrayOrEmpty( - stream(method.getAnnotatedParameterTypes()).map(mutatorFactory::tryCreate), - SerializingMutator[] ::new) + stream(method.getAnnotatedParameterTypes()).map(mutatorFactory::tryCreate), + SerializingMutator[]::new) .map(MutatorCombinators::mutateProduct) .map(productMutator -> ArgumentsMutator.create(instance, method, productMutator)); } @@ -226,7 +231,9 @@ public String toString() { } private void failIfArgumentsExposed() { - Preconditions.check(!argumentsExposed, - "Arguments have previously been exposed to user-provided code without calling #detach and may have been modified"); + Preconditions.check( + !argumentsExposed, + "Arguments have previously been exposed to user-provided code without calling #detach and" + + " may have been modified"); } } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/AppliesTo.java b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/AppliesTo.java index 4d4c4a89c..5bfed7ae3 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/AppliesTo.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/AppliesTo.java @@ -22,19 +22,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -/** - * A meta-annotation that limits the concrete types an annotation for type usages applies to. - */ +/** A meta-annotation that limits the concrete types an annotation for type usages applies to. */ @Target(ANNOTATION_TYPE) @Retention(RUNTIME) public @interface AppliesTo { - /** - * The meta-annotated annotation can be applied to these classes. - */ + /** The meta-annotated annotation can be applied to these classes. */ Class[] value() default {}; - /** - * The meta-annotated annotation can be applied to subclasses of these classes. - */ + /** The meta-annotated annotation can be applied to subclasses of these classes. */ Class[] subClassesOf() default {}; } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/DoubleInRange.java b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/DoubleInRange.java index 27765879d..3879e4fad 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/DoubleInRange.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/DoubleInRange.java @@ -27,6 +27,8 @@ @AppliesTo({double.class, Double.class}) public @interface DoubleInRange { double min() default Double.NEGATIVE_INFINITY; + double max() default Double.POSITIVE_INFINITY; + boolean allowNaN() default true; } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/FloatInRange.java b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/FloatInRange.java index ec54e0265..acf2a1815 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/FloatInRange.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/FloatInRange.java @@ -27,6 +27,8 @@ @AppliesTo({float.class, Float.class}) public @interface FloatInRange { float min() default Float.NEGATIVE_INFINITY; + float max() default Float.POSITIVE_INFINITY; + boolean allowNaN() default true; } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/InRange.java b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/InRange.java index a8dc82817..49755f94b 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/InRange.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/InRange.java @@ -19,15 +19,21 @@ import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(TYPE_USE) @Retention(RUNTIME) -@AppliesTo({byte.class, Byte.class, short.class, Short.class, int.class, Integer.class, long.class, - Long.class}) +@AppliesTo({ + byte.class, + Byte.class, + short.class, + Short.class, + int.class, + Integer.class, + long.class, + Long.class +}) public @interface InRange { long min() default Long.MIN_VALUE; diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/WithLength.java b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/WithLength.java index 7cee23dfc..5dbc464d8 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/WithLength.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/WithLength.java @@ -16,7 +16,6 @@ package com.code_intelligence.jazzer.mutation.annotation; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/WithUtf8Length.java b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/WithUtf8Length.java index 00b1f463c..5cdc47514 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/WithUtf8Length.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/WithUtf8Length.java @@ -25,13 +25,15 @@ /** * An annotation that applies to {@link String} to limit the length of the UTF-8 * encoding of the string. In practical terms, this means that strings given this - * annotation will sometimes have a {@link String#length()} of less than - * {@code min} but will never exceed {@code max}.

        Due to the fact that our String mutator is - * backed by the byte array mutator, it's difficult to know how many characters we'll get from the - * byte array we get from libfuzzer. Rather than reuse {@link WithLength} for strings which may give - * the impression that {@link String#length()} will return a value between {@code min} and {@code - * max}, we use this annotation to help make clear that the string consists of between - * {@code min} and {@code max} UTF-8 bytes, not necessarily (UTF-16) characters. + * annotation will sometimes have a {@link String#length()} of less than {@code min} but will never + * exceed {@code max}. + * + *

        Due to the fact that our String mutator is backed by the byte array mutator, it's difficult to + * know how many characters we'll get from the byte array we get from libfuzzer. Rather than reuse + * {@link WithLength} for strings which may give the impression that {@link String#length()} will + * return a value between {@code min} and {@code max}, we use this annotation to help make clear + * that the string consists of between {@code min} and {@code max} UTF-8 bytes, not necessarily + * (UTF-16) characters. */ @Target(TYPE_USE) @Retention(RUNTIME) diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/AnySource.java b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/AnySource.java index 9f802dfef..442bb459d 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/AnySource.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/AnySource.java @@ -33,8 +33,6 @@ @Retention(RUNTIME) @AppliesTo(subClassesOf = {Message.class, Builder.class}) public @interface AnySource { - /** - * A non-empty list of {@link Message}s to use for {@link com.google.protobuf.Any} fields. - */ + /** A non-empty list of {@link Message}s to use for {@link com.google.protobuf.Any} fields. */ Class[] value(); } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/WithDefaultInstance.java b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/WithDefaultInstance.java index e26d73a26..d1eff3bd6 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/WithDefaultInstance.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/annotation/proto/WithDefaultInstance.java @@ -34,10 +34,10 @@ @AppliesTo(subClassesOf = {Message.class, Message.Builder.class}) public @interface WithDefaultInstance { /** - * The fully qualified name of a static method (e.g. - * {@code com.example.MyClass#getDefaultInstance}) with return type assignable to - * {@link com.google.protobuf.Message}, which returns a default instance that mutations should be - * based on. + * The fully qualified name of a static method (e.g. {@code + * com.example.MyClass#getDefaultInstance}) with return type assignable to {@link + * com.google.protobuf.Message}, which returns a default instance that mutations should be based + * on. */ String value(); } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/ChainedMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/ChainedMutatorFactory.java index bf27e81b0..2b646a308 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/ChainedMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/ChainedMutatorFactory.java @@ -25,9 +25,7 @@ import java.util.List; import java.util.Optional; -/** - * A {@link MutatorFactory} that delegates to the given factories in order. - */ +/** A {@link MutatorFactory} that delegates to the given factories in order. */ public final class ChainedMutatorFactory extends MutatorFactory { private final List factories; diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/Debuggable.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/Debuggable.java index df6f82883..4353caef9 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/Debuggable.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/Debuggable.java @@ -29,9 +29,9 @@ public interface Debuggable { * its structure in tests. * * @param isInCycle evaluates to {@code true} if a cycle has been detected during recursive calls - * of this function. Must be called at most once with {@code this} as the single - * argument. Implementing classes that know that their current instance can never - * be contained in recursive substructures need not call this method. + * of this function. Must be called at most once with {@code this} as the single argument. + * Implementing classes that know that their current instance can never be contained in + * recursive substructures need not call this method. */ String toDebugString(Predicate isInCycle); diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/Detacher.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/Detacher.java index d927e505f..7d478c83d 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/Detacher.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/Detacher.java @@ -18,27 +18,27 @@ import com.google.errorprone.annotations.CheckReturnValue; -/** - * Knows how to clone a {@code T} such that it shares no mutable state with the original. - */ +/** Knows how to clone a {@code T} such that it shares no mutable state with the original. */ @FunctionalInterface public interface Detacher { /** * Returns an equal instance that shares no mutable state with {@code value}. * *

        Implementations + * *

          *
        • MUST return an instance that {@link Object#equals(Object)} the argument; *
        • MUST return an instance that cannot be used to mutate the state of the argument through - * its API (ignoring uses of {@link sun.misc.Unsafe}); - *
        • MUST return an instance that is not affected by any changes to the original value made - * by any mutator;
        • - *
        • MUST be accepted by mutator methods just like the original value;
        • + * its API (ignoring uses of {@link sun.misc.Unsafe}); + *
        • MUST return an instance that is not affected by any changes to the original value made by + * any mutator; + *
        • MUST be accepted by mutator methods just like the original value; *
        • MAY return the argument itself if it is deeply immutable. *
        * * @param value the instance to detach * @return an equal instance that shares no mutable state with {@code value} */ - @CheckReturnValue T detach(T value); + @CheckReturnValue + T detach(T value); } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/InPlaceMutator.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/InPlaceMutator.java index cf1b243d6..adbce8ffc 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/InPlaceMutator.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/InPlaceMutator.java @@ -25,15 +25,16 @@ * can't. In such cases, use {@link ValueMutator} instead. * *

        Implementations + * *

          *
        • MAY weakly associate mutable state with the identity (not equality class) of objects they - * have been passed as arguments or returned from initialization functions; - *
        • MAY assume that they are only passed arguments that they have initialized or mutated;
        • + * have been passed as arguments or returned from initialization functions; + *
        • MAY assume that they are only passed arguments that they have initialized or mutated; *
        • SHOULD use {@link com.code_intelligence.jazzer.mutation.support.WeakIdentityHashMap} for - * this purpose; + * this purpose; *
        • MUST otherwise be deeply immutable; *
        • SHOULD override {@link Object#toString()} to return {@code - * Debuggable.getDebugString(this)}. + * Debuggable.getDebugString(this)}. *
        * * @param the reference type this mutator operates on @@ -41,33 +42,36 @@ public interface InPlaceMutator extends Debuggable { /** * Implementations + * *
          *
        • MUST accept any mutable instance of {@code T}, not just those it creates itself. *
        • SHOULD, when called repeatedly, initialize the object in ways that are likely to be - * distinct. + * distinct. *
        */ void initInPlace(T reference, PseudoRandom prng); /** * Implementations + * *
          *
        • MUST ensure that {@code reference} does not {@link Object#equals(Object)} the state it - * had prior to the call (if possible); + * had prior to the call (if possible); *
        • MUST accept any mutable instance of {@code T}, not just those it creates itself. *
        • SHOULD, when called repeatedly, be able to eventually reach any valid state of the part - * of {@code T} governed by this mutator; + * of {@code T} governed by this mutator; *
        */ void mutateInPlace(T reference, PseudoRandom prng); /** * Implementations + * *
          *
        • MUST ensure that {@code reference} does not {@link Object#equals(Object)} the state it - * had prior to the call (if possible); + * had prior to the call (if possible); *
        • MUST accept any mutable instance of {@code T}, not just those it creates itself. - *
        • MUST NOT mutate {@code otherReference}
        • + *
        • MUST NOT mutate {@code otherReference} *
        */ void crossOverInPlace(T reference, T otherReference, PseudoRandom prng); diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/MutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/MutatorFactory.java index 64771285f..91e82d502 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/MutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/MutatorFactory.java @@ -54,11 +54,14 @@ public final SerializingInPlaceMutator createInPlaceOrThrow(AnnotatedType typ * of {@link SerializingInPlaceMutator}. */ public final Optional> tryCreateInPlace(AnnotatedType type) { - return tryCreate(type).map(mutator -> { - require(mutator instanceof InPlaceMutator, - format("Mutator for %s is not in-place: %s", type, mutator.getClass())); - return (SerializingInPlaceMutator) mutator; - }); + return tryCreate(type) + .map( + mutator -> { + require( + mutator instanceof InPlaceMutator, + format("Mutator for %s is not in-place: %s", type, mutator.getClass())); + return (SerializingInPlaceMutator) mutator; + }); } @CheckReturnValue @@ -71,8 +74,8 @@ public final Optional> tryCreate(AnnotatedType type) { * * @param type the type to mutate * @param factory the factory to use when creating submutators - * @return a {@link SerializingMutator} for the given {@code type}, or {@link Optional#empty()} - * if this factory can't create such mutators + * @return a {@link SerializingMutator} for the given {@code type}, or {@link Optional#empty()} if + * this factory can't create such mutators */ @CheckReturnValue public abstract Optional> tryCreate( diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/PseudoRandom.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/PseudoRandom.java index 3755a7b1b..f8a39ac05 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/PseudoRandom.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/PseudoRandom.java @@ -66,8 +66,8 @@ public interface PseudoRandom { /** * @throws IllegalArgumentException if {@code array.length < 2} - * @return a uniformly random index valid for the given array and different from - * {@code currentIndex} + * @return a uniformly random index valid for the given array and different from {@code + * currentIndex} */ int otherIndexIn(T[] array, int currentIndex); @@ -79,26 +79,26 @@ public interface PseudoRandom { int otherIndexIn(int range, int currentIndex); /** - * @return a uniformly random {@code int} in the closed range - * {@code [lowerInclusive, upperInclusive]}. + * @return a uniformly random {@code int} in the closed range {@code [lowerInclusive, + * upperInclusive]}. */ int closedRange(int lowerInclusive, int upperInclusive); /** - * @return a uniformly random {@code long} in the closed range - * {@code [lowerInclusive, upperInclusive]}. + * @return a uniformly random {@code long} in the closed range {@code [lowerInclusive, + * upperInclusive]}. */ long closedRange(long lowerInclusive, long upperInclusive); /** - * @return a uniformly random {@code float} in the closed range - * {@code [lowerInclusive, upperInclusive]}. + * @return a uniformly random {@code float} in the closed range {@code [lowerInclusive, + * upperInclusive]}. */ float closedRange(float lowerInclusive, float upperInclusive); /** - * @return a uniformly random {@code double} in the closed range - * {@code [lowerInclusive, upperInclusive]}. + * @return a uniformly random {@code double} in the closed range {@code [lowerInclusive, + * upperInclusive]}. */ double closedRange(double lowerInclusive, double upperInclusive); @@ -114,9 +114,7 @@ public interface PseudoRandom { */ int closedRangeBiasedTowardsSmall(int lowerInclusive, int upperInclusive); - /** - * Fills the given array with random bytes. - */ + /** Fills the given array with random bytes. */ void bytes(byte[] bytes); /** diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/Serializer.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/Serializer.java index b948b177b..642985608 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/Serializer.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/Serializer.java @@ -41,6 +41,7 @@ public interface Serializer extends Detacher { * Reads a {@code T} from an endless stream that is eventually 0. * *

        Implementations + * *

          *
        • MUST not attempt to consume the entire stream; *
        • MUST return a valid {@code T} and not throw for any (even garbage) stream; @@ -50,9 +51,10 @@ public interface Serializer extends Detacher { * @param in an endless stream that eventually only reads null bytes * @return a {@code T} constructed from the bytes read * @throws IOException declared, but must not be thrown by implementations unless methods called - * on {@code in} do + * on {@code in} do */ - @CheckReturnValue T read(DataInputStream in) throws IOException; + @CheckReturnValue + T read(DataInputStream in) throws IOException; /** * Writes a {@code T} to a stream in such a way that an equal object can be recovered from the @@ -63,9 +65,9 @@ public interface Serializer extends Detacher { * length. * * @param value the value to write - * @param out the stream to write to + * @param out the stream to write to * @throws IOException declared, but must not be thrown by implementations unless methods called - * on {@code out} do + * on {@code out} do */ void write(T value, DataOutputStream out) throws IOException; @@ -73,10 +75,11 @@ public interface Serializer extends Detacher { * Reads a {@code T} from a finite stream, potentially using a simpler representation than that * read by {@link #read(DataInputStream)}. * - *

          The default implementations call extends the stream with null bytes and then calls - * {@link #read(DataInputStream)}. + *

          The default implementations call extends the stream with null bytes and then calls {@link + * #read(DataInputStream)}. * *

          Implementations + * *

            *
          • MUST return a valid {@code T} and not throw for any (even garbage) stream; *
          • SHOULD short-circuit the creation of nested structures upon reading null bytes; @@ -86,7 +89,7 @@ public interface Serializer extends Detacher { * @param in a finite stream * @return a {@code T} constructed from the bytes read * @throws IOException declared, but must not be thrown by implementations unless methods called - * on {@code in} do + * on {@code in} do */ @CheckReturnValue default T readExclusive(InputStream in) throws IOException { @@ -106,9 +109,9 @@ default T readExclusive(InputStream in) throws IOException { * be written to the stream without prepending its length. * * @param value the value to write - * @param out the stream to write to + * @param out the stream to write to * @throws IOException declared, but must not be thrown by implementations unless methods called - * on {@code out} do + * on {@code out} do */ default void writeExclusive(T value, OutputStream out) throws IOException { write(value, new DataOutputStream(out)); diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/SerializingInPlaceMutator.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/SerializingInPlaceMutator.java index b7bc4d4c4..ff2ffcb30 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/SerializingInPlaceMutator.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/SerializingInPlaceMutator.java @@ -30,8 +30,8 @@ * *

            Implementing classes SHOULD be declared final. */ -public abstract class SerializingInPlaceMutator - extends SerializingMutator implements InPlaceMutator { +public abstract class SerializingInPlaceMutator extends SerializingMutator + implements InPlaceMutator { // ByteArrayInputStream#close is documented as being a no-op, so it is safe to reuse an instance // here. // TODO: Introduce a dedicated empty InputStream implementation. diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/api/ValueMutator.java b/src/main/java/com/code_intelligence/jazzer/mutation/api/ValueMutator.java index aa2b551e6..55d06a261 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/api/ValueMutator.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/api/ValueMutator.java @@ -19,22 +19,23 @@ import com.google.errorprone.annotations.CheckReturnValue; /** - * Knows how to initialize and mutate objects of type {@code T} and how to incorporate - * (cross over) parts of another object of the same type. + * Knows how to initialize and mutate objects of type {@code T} and how to incorporate (cross over) + * parts of another object of the same type. * *

            Certain types can be mutated fully in place. In such cases, prefer implementing the more * versatile {@link InPlaceMutator} instead. * *

            Implementations + * *

              *
            • MAY weakly associate mutable state with the identity (not equality class) of objects they - * have been passed as arguments or returned from initialization functions; - *
            • MAY assume that they are only passed arguments that they have initialized or mutated;
            • + * have been passed as arguments or returned from initialization functions; + *
            • MAY assume that they are only passed arguments that they have initialized or mutated; *
            • SHOULD use {@link com.code_intelligence.jazzer.mutation.support.WeakIdentityHashMap} for - * this purpose; + * this purpose; *
            • MUST otherwise be deeply immutable; *
            • SHOULD override {@link Object#toString()} to return {@code - * Debuggable.getDebugString(this)}. + * Debuggable.getDebugString(this)}. *
            * * @param the type this mutator operates on @@ -42,34 +43,40 @@ public interface ValueMutator extends Debuggable { /** * Implementations + * *
              *
            • SHOULD, when called repeatedly, return a low amount of duplicates. *
            * * @return an instance of {@code T} */ - @CheckReturnValue T init(PseudoRandom prng); + @CheckReturnValue + T init(PseudoRandom prng); /** * Implementations + * *
              *
            • MUST return a value that does not {@link Object#equals(Object)} the argument (if - * possible); - *
            • SHOULD, when called repeatedly, be able to eventually return any valid value of - * type {@code T}; + * possible); + *
            • SHOULD, when called repeatedly, be able to eventually return any valid value of type + * {@code T}; *
            • MAY mutate the argument. *
            */ - @CheckReturnValue T mutate(T value, PseudoRandom prng); + @CheckReturnValue + T mutate(T value, PseudoRandom prng); /** * Implementations + * *
              *
            • MUST return a value that does not {@link Object#equals(Object)} the arguments (if - * possible); + * possible); *
            • MAY mutate {@code value}. *
            • MUST NOT mutate {@code otherValue}. *
            */ - @CheckReturnValue T crossOver(T value, T otherValue, PseudoRandom prng); + @CheckReturnValue + T crossOver(T value, T otherValue, PseudoRandom prng); } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinators.java b/src/main/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinators.java index 18fa02886..d88683dcf 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinators.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinators.java @@ -45,7 +45,7 @@ public final class MutatorCombinators { // Inverse frequency in which value mutator should be used in cross over. - private final static int INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY = 100; + private static final int INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY = 100; private MutatorCombinators() {} @@ -73,10 +73,12 @@ public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) { // don't cross over values themselves. R referenceValue = getter.apply(reference); R otherReferenceValue = getter.apply(otherReference); - R crossedOver = prng.pickValue(referenceValue, otherReferenceValue, - () - -> mutator.crossOver(referenceValue, otherReferenceValue, prng), - INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY); + R crossedOver = + prng.pickValue( + referenceValue, + otherReferenceValue, + () -> mutator.crossOver(referenceValue, otherReferenceValue, prng), + INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY); if (crossedOver == otherReferenceValue) { // If otherReference was picked, it needs to be detached as mutating // it is prohibited in cross over. @@ -205,8 +207,11 @@ public static SerializingMutator mutateThenMap( return new PostComposedMutator(mutator, map, inverse) {}; } - public static SerializingMutator mutateThenMap(SerializingMutator mutator, - Function map, Function inverse, Function, String> debug) { + public static SerializingMutator mutateThenMap( + SerializingMutator mutator, + Function map, + Function inverse, + Function, String> debug) { return new PostComposedMutator(mutator, map, inverse) { @Override public String toDebugString(Predicate isInCycle) { @@ -226,7 +231,9 @@ public R detach(R value) { } public static SerializingMutator mutateThenMapToImmutable( - SerializingMutator mutator, Function map, Function inverse, + SerializingMutator mutator, + Function map, + Function inverse, Function, String> debug) { return new PostComposedMutator(mutator, map, inverse) { @Override @@ -282,8 +289,8 @@ public String toDebugString(Predicate isInCycle) { } /** - * Combines multiple mutators for potentially different types into one that mutates an - * {@code Object[]} containing one instance per mutator. + * Combines multiple mutators for potentially different types into one that mutates an {@code + * Object[]} containing one instance per mutator. */ @SuppressWarnings("rawtypes") public static ProductMutator mutateProduct(SerializingMutator... mutators) { @@ -291,10 +298,11 @@ public static ProductMutator mutateProduct(SerializingMutator... mutators) { } /** - * Mutates a sum type (e.g. a Protobuf oneof) in place, preferring to mutate the current state - * but occasionally switching to a different state. + * Mutates a sum type (e.g. a Protobuf oneof) in place, preferring to mutate the current state but + * occasionally switching to a different state. + * * @param getState a function that returns the current state of the sum type as an index into - * {@code perStateMutators}, or -1 if the state is indeterminate. + * {@code perStateMutators}, or -1 if the state is indeterminate. * @param perStateMutators the mutators for each state * @return a mutator that mutates the sum type in place */ @@ -352,7 +360,7 @@ public String toDebugString(Predicate isInCycle) { /** * @return a mutator that behaves identically to the provided one except that its {@link - * InPlaceMutator#initInPlace(Object, PseudoRandom)} is a no-op + * InPlaceMutator#initInPlace(Object, PseudoRandom)} is a no-op */ public static InPlaceMutator withoutInit(InPlaceMutator mutator) { return new InPlaceMutator() { @@ -424,20 +432,20 @@ public T crossOver(T value, T otherValue, PseudoRandom prng) { /** * Assembles the parameters into a full implementation of {@link SerializingInPlaceMutator}: * - * @param registerSelf a callback that will receive the uninitialized mutator instance - * before {@code lazyMutator} is invoked. For simple cases this can - * just do nothing, but it is needed to implement mutators for - * structures that are self-referential (e.g. Protobuf message A having - * a field of type A). + * @param registerSelf a callback that will receive the uninitialized mutator instance before + * {@code lazyMutator} is invoked. For simple cases this can just do nothing, but it is needed + * to implement mutators for structures that are self-referential (e.g. Protobuf message A + * having a field of type A). * @param makeDefaultInstance constructs a mutable default instance of {@code T} - * @param serializer implementation of the {@link Serializer} part - * @param lazyMutator supplies the implementation of the {@link InPlaceMutator} part. - * This is guaranteed to be invoked exactly once and only after - * {@code registerSelf}. + * @param serializer implementation of the {@link Serializer} part + * @param lazyMutator supplies the implementation of the {@link InPlaceMutator} part. This is + * guaranteed to be invoked exactly once and only after {@code registerSelf}. */ public static SerializingInPlaceMutator assemble( - Consumer> registerSelf, Supplier makeDefaultInstance, - Serializer serializer, Supplier> lazyMutator) { + Consumer> registerSelf, + Supplier makeDefaultInstance, + Serializer serializer, + Supplier> lazyMutator) { return new DelegatingSerializingInPlaceMutator<>( registerSelf, makeDefaultInstance, serializer, lazyMutator); } @@ -447,8 +455,10 @@ private static class DelegatingSerializingInPlaceMutator extends SerializingI private final Serializer serializer; private final InPlaceMutator mutator; - private DelegatingSerializingInPlaceMutator(Consumer> registerSelf, - Supplier makeDefaultInstance, Serializer serializer, + private DelegatingSerializingInPlaceMutator( + Consumer> registerSelf, + Supplier makeDefaultInstance, + Serializer serializer, Supplier> lazyMutator) { requireNonNull(makeDefaultInstance); requireNonNull(serializer); diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/combinator/ProductMutator.java b/src/main/java/com/code_intelligence/jazzer/mutation/combinator/ProductMutator.java index 9057fd35d..20c38f85d 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/combinator/ProductMutator.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/combinator/ProductMutator.java @@ -37,7 +37,7 @@ @SuppressWarnings({"unchecked", "rawtypes"}) public final class ProductMutator extends SerializingInPlaceMutator { // Inverse frequency in which product type mutators should be used in cross over. - private final static int INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY = 100; + private static final int INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY = 100; private final SerializingMutator[] mutators; @@ -109,8 +109,12 @@ public void crossOverInPlace(Object[] reference, Object[] otherReference, Pseudo SerializingMutator mutator = mutators[i]; Object value = reference[i]; Object otherValue = otherReference[i]; - Object crossedOver = prng.pickValue(value, otherValue, - () -> mutator.crossOver(value, otherValue, prng), INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY); + Object crossedOver = + prng.pickValue( + value, + otherValue, + () -> mutator.crossOver(value, otherValue, prng), + INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY); if (crossedOver == otherReference) { // If otherReference was picked, it needs to be detached as mutating // it is prohibited in cross over. diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandom.java b/src/main/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandom.java index 515f345bb..cd3f96a7b 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandom.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandom.java @@ -177,8 +177,7 @@ public double closedRange(double lowerInclusive, double upperInclusive) { upperInclusive == Double.POSITIVE_INFINITY ? Double.MAX_VALUE : upperInclusive; // After limiting, the range may contain only a single value: return that - if (limitedLower == limitedUpper) - return limitedLower; + if (limitedLower == limitedUpper) return limitedLower; // random.nextDouble() is exclusive of the upper bound. To include the upper bound, // we extend the bound to the next double value by using Math.nextUp(limitedUpper). @@ -197,8 +196,7 @@ public double closedRange(double lowerInclusive, double upperInclusive) { // This is a workaround for RandomSupport.nextDouble() that causes it to // return values greater than upper bound. // See https://bugs.openjdk.org/browse/JDK-8281183 for a list of affected JDK versions. - if (result > limitedUpper) - result = limitedUpper; + if (result > limitedUpper) result = limitedUpper; return result; } else { // Ranges that exceeds the maximum representable double value, or ranges that could not be diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/Mutators.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/Mutators.java index 9bdf5a3ba..7777f557c 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/Mutators.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/Mutators.java @@ -35,48 +35,56 @@ public final class Mutators { private Mutators() {} public static MutatorFactory newFactory() { - return new ChainedMutatorFactory(LangMutators.newFactory(), CollectionMutators.newFactory(), - ProtoMutators.newFactory(), LibFuzzerMutators.newFactory()); + return new ChainedMutatorFactory( + LangMutators.newFactory(), + CollectionMutators.newFactory(), + ProtoMutators.newFactory(), + LibFuzzerMutators.newFactory()); } /** - * Throws an exception if any annotation on {@code type} violates the restrictions of its - * {@link AppliesTo} meta-annotation. + * Throws an exception if any annotation on {@code type} violates the restrictions of its {@link + * AppliesTo} meta-annotation. */ public static void validateAnnotationUsage(AnnotatedType type) { - visitAnnotatedType(type, (clazz, annotations) -> { - outer: - for (Annotation annotation : annotations) { - AppliesTo appliesTo = annotation.annotationType().getAnnotation(AppliesTo.class); - if (appliesTo == null) { - continue; - } - for (Class allowedClass : appliesTo.value()) { - if (allowedClass == clazz) { - continue outer; + visitAnnotatedType( + type, + (clazz, annotations) -> { + outer: + for (Annotation annotation : annotations) { + AppliesTo appliesTo = annotation.annotationType().getAnnotation(AppliesTo.class); + if (appliesTo == null) { + continue; } - } - for (Class allowedSuperClass : appliesTo.subClassesOf()) { - if (allowedSuperClass.isAssignableFrom(clazz)) { - continue outer; + for (Class allowedClass : appliesTo.value()) { + if (allowedClass == clazz) { + continue outer; + } + } + for (Class allowedSuperClass : appliesTo.subClassesOf()) { + if (allowedSuperClass.isAssignableFrom(clazz)) { + continue outer; + } } - } - String helpText = ""; - if (appliesTo.value().length != 0) { - helpText = stream(appliesTo.value()).map(Class::getName).collect(joining(", ")); - } - if (appliesTo.subClassesOf().length != 0) { - if (!helpText.isEmpty()) { - helpText += "as well as "; + String helpText = ""; + if (appliesTo.value().length != 0) { + helpText = stream(appliesTo.value()).map(Class::getName).collect(joining(", ")); + } + if (appliesTo.subClassesOf().length != 0) { + if (!helpText.isEmpty()) { + helpText += "as well as "; + } + helpText += "subclasses of "; + helpText += + stream(appliesTo.subClassesOf()).map(Class::getName).collect(joining(", ")); } - helpText += "subclasses of "; - helpText += stream(appliesTo.subClassesOf()).map(Class::getName).collect(joining(", ")); + // Use the simple name as our annotations live in a single package. + throw new IllegalArgumentException( + format( + "%s does not apply to %s, only applies to %s", + annotation.annotationType().getSimpleName(), clazz.getName(), helpText)); } - // Use the simple name as our annotations live in a single package. - throw new IllegalArgumentException(format("%s does not apply to %s, only applies to %s", - annotation.annotationType().getSimpleName(), clazz.getName(), helpText)); - } - }); + }); } } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkCrossOvers.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkCrossOvers.java index 48b7d2277..820e271d0 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkCrossOvers.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkCrossOvers.java @@ -44,9 +44,13 @@ static void overwriteChunk(List list, List otherList, PseudoRandom prn static void crossOverChunk( List list, List otherList, SerializingMutator elementMutator, PseudoRandom prng) { - onCorrespondingChunks(list, otherList, prng, (toPos, element) -> { - list.set(toPos, elementMutator.crossOver(list.get(toPos), element, prng)); - }); + onCorrespondingChunks( + list, + otherList, + prng, + (toPos, element) -> { + list.set(toPos, elementMutator.crossOver(list.get(toPos), element, prng)); + }); } @FunctionalInterface @@ -54,7 +58,7 @@ private interface ChunkListElementOperation { void apply(int toPos, T chunk); } - static private void onCorrespondingChunks( + private static void onCorrespondingChunks( List list, List otherList, PseudoRandom prng, ChunkListElementOperation operation) { int maxChunkSize = Math.min(list.size(), otherList.size()); int chunkSize = prng.closedRangeBiasedTowardsSmall(1, maxChunkSize); @@ -88,20 +92,28 @@ static void insertChunk( } static void overwriteChunk(Map map, Map otherMap, PseudoRandom prng) { - onCorrespondingChunks(map, otherMap, prng, (fromIterator, toIterator, chunkSize) -> { - // As keys can not be overwritten, only removed and new ones added, this - // cross over overwrites the values. Removal of keys is handled by the - // removeChunk mutation. Value equality is not checked here. - for (int i = 0; i < chunkSize; i++) { - Entry from = fromIterator.next(); - Entry to = toIterator.next(); - to.setValue(from.getValue()); - } - }); + onCorrespondingChunks( + map, + otherMap, + prng, + (fromIterator, toIterator, chunkSize) -> { + // As keys can not be overwritten, only removed and new ones added, this + // cross over overwrites the values. Removal of keys is handled by the + // removeChunk mutation. Value equality is not checked here. + for (int i = 0; i < chunkSize; i++) { + Entry from = fromIterator.next(); + Entry to = toIterator.next(); + to.setValue(from.getValue()); + } + }); } - static void crossOverChunk(Map map, Map otherMap, - SerializingMutator keyMutator, SerializingMutator valueMutator, PseudoRandom prng) { + static void crossOverChunk( + Map map, + Map otherMap, + SerializingMutator keyMutator, + SerializingMutator valueMutator, + PseudoRandom prng) { if (prng.choice()) { crossOverChunkKeys(map, otherMap, keyMutator, prng); } else { @@ -111,34 +123,38 @@ static void crossOverChunk(Map map, Map otherMap, private static void crossOverChunkKeys( Map map, Map otherMap, SerializingMutator keyMutator, PseudoRandom prng) { - onCorrespondingChunks(map, otherMap, prng, (fromIterator, toIterator, chunkSize) -> { - Map entriesToAdd = new LinkedHashMap<>(chunkSize); - for (int i = 0; i < chunkSize; i++) { - Entry to = toIterator.next(); - Entry from = fromIterator.next(); - - // The entry has to be removed from the map before the cross-over, as - // mutating its key could cause problems in subsequent lookups. - // Furthermore, no new entries may be added while using the iterator, - // so crossed-over keys are collected for later addition. - K key = to.getKey(); - V value = to.getValue(); - toIterator.remove(); - - // As cross-overs do not guarantee to mutate the given object, no - // checks if the crossed over key already exists in the map are - // performed. This potentially overwrites existing entries or - // generates equal keys. - // In case of cross over this behavior is acceptable. - K newKey = keyMutator.crossOver(key, from.getKey(), prng); - - // Prevent null keys, as those are not allowed in some map implementations. - if (newKey != null) { - entriesToAdd.put(newKey, value); - } - } - map.putAll(entriesToAdd); - }); + onCorrespondingChunks( + map, + otherMap, + prng, + (fromIterator, toIterator, chunkSize) -> { + Map entriesToAdd = new LinkedHashMap<>(chunkSize); + for (int i = 0; i < chunkSize; i++) { + Entry to = toIterator.next(); + Entry from = fromIterator.next(); + + // The entry has to be removed from the map before the cross-over, as + // mutating its key could cause problems in subsequent lookups. + // Furthermore, no new entries may be added while using the iterator, + // so crossed-over keys are collected for later addition. + K key = to.getKey(); + V value = to.getValue(); + toIterator.remove(); + + // As cross-overs do not guarantee to mutate the given object, no + // checks if the crossed over key already exists in the map are + // performed. This potentially overwrites existing entries or + // generates equal keys. + // In case of cross over this behavior is acceptable. + K newKey = keyMutator.crossOver(key, from.getKey(), prng); + + // Prevent null keys, as those are not allowed in some map implementations. + if (newKey != null) { + entriesToAdd.put(newKey, value); + } + } + map.putAll(entriesToAdd); + }); } private static void crossOverChunkValues( @@ -147,19 +163,23 @@ private static void crossOverChunkValues( // checks if a new value is produced are performed. // The cross-over could have already mutated value, but explicitly set it // through the iterator to be sure. - onCorrespondingChunks(map, otherMap, prng, (fromIterator, toIterator, chunkSize) -> { - for (int i = 0; i < chunkSize; i++) { - Entry fromEntry = fromIterator.next(); - Entry toEntry = toIterator.next(); - // As cross-overs do not guarantee to mutate the given object, no - // checks if a new value is produced are performed. - V newValue = valueMutator.crossOver(toEntry.getValue(), fromEntry.getValue(), prng); - - // The cross-over could have already mutated value, but explicitly set it - // through the iterator to be sure. - toEntry.setValue(newValue); - } - }); + onCorrespondingChunks( + map, + otherMap, + prng, + (fromIterator, toIterator, chunkSize) -> { + for (int i = 0; i < chunkSize; i++) { + Entry fromEntry = fromIterator.next(); + Entry toEntry = toIterator.next(); + // As cross-overs do not guarantee to mutate the given object, no + // checks if a new value is produced are performed. + V newValue = valueMutator.crossOver(toEntry.getValue(), fromEntry.getValue(), prng); + + // The cross-over could have already mutated value, but explicitly set it + // through the iterator to be sure. + toEntry.setValue(newValue); + } + }); } @FunctionalInterface diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutations.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutations.java index d52602890..a72c74140 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutations.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutations.java @@ -81,8 +81,12 @@ static void insertRandomChunk( list.addAll(chunkOffset, new ArraySharingList<>(chunk)); } - static boolean insertRandomChunk(Set set, Consumer addIfNew, int maxSize, - ValueMutator elementMutator, PseudoRandom prng) { + static boolean insertRandomChunk( + Set set, + Consumer addIfNew, + int maxSize, + ValueMutator elementMutator, + PseudoRandom prng) { int oldSize = set.size(); int chunkSize = prng.closedRangeBiasedTowardsSmall(1, maxSize - oldSize); return growBy(set, addIfNew, chunkSize, () -> elementMutator.init(prng)); @@ -124,21 +128,23 @@ static boolean mutateRandomKeysChunk( keysToRemove.add(entry.getKey()); } - Consumer addIfNew = key -> { - int sizeBeforeAdd = map.size(); - map.putIfAbsent(key, unboxNull(values.peekFirst())); - // The mutated key was new, try to mutate and add the next in line. - if (map.size() > sizeBeforeAdd) { - keysToMutate.removeFirst(); - values.removeFirst(); - } - }; - Supplier nextCandidate = () -> { - // Mutate the next candidate in the queue. - K candidate = keyMutator.mutate(unboxNull(keysToMutate.removeFirst()), prng); - keysToMutate.addFirst(boxNull(candidate)); - return candidate; - }; + Consumer addIfNew = + key -> { + int sizeBeforeAdd = map.size(); + map.putIfAbsent(key, unboxNull(values.peekFirst())); + // The mutated key was new, try to mutate and add the next in line. + if (map.size() > sizeBeforeAdd) { + keysToMutate.removeFirst(); + values.removeFirst(); + } + }; + Supplier nextCandidate = + () -> { + // Mutate the next candidate in the queue. + K candidate = keyMutator.mutate(unboxNull(keysToMutate.removeFirst()), prng); + keysToMutate.addFirst(boxNull(candidate)); + return candidate; + }; growBy(map.keySet(), addIfNew, chunkSize, nextCandidate); // Remove the original keys that were successfully mutated into new keys. Since the original diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorFactory.java index 86ecbd4f7..c90aa5503 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorFactory.java @@ -71,9 +71,11 @@ private static final class ListMutator extends SerializingInPlaceMutator= 1, format("WithSize#max=%d needs to be greater than 0", maxSize)); require(minSize >= 0, format("WithSize#min=%d needs to be positive", minSize)); - require(minSize <= maxSize, - format("WithSize#min=%d needs to be smaller or equal than WithSize#max=%d", minSize, - maxSize)); + require( + minSize <= maxSize, + format( + "WithSize#min=%d needs to be smaller or equal than WithSize#max=%d", + minSize, maxSize)); } @Override diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java index fca8b5cb3..052e4e0dd 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java @@ -56,24 +56,26 @@ final class MapMutatorFactory extends MutatorFactory { @Override public Optional> tryCreate(AnnotatedType type, MutatorFactory factory) { return parameterTypesIfParameterized(type, Map.class) - .map(parameterTypes - -> parameterTypes.stream() - .map(factory::tryCreate) - .flatMap(StreamSupport::getOrEmpty) - .collect(Collectors.toList())) - .map(elementMutators -> { - check(elementMutators.size() == 2); - int min = MapMutator.DEFAULT_MIN_SIZE; - int max = MapMutator.DEFAULT_MAX_SIZE; - for (Annotation annotation : type.getDeclaredAnnotations()) { - if (annotation instanceof WithSize) { - WithSize withSize = (WithSize) annotation; - min = withSize.min(); - max = withSize.max(); - } - } - return new MapMutator<>(elementMutators.get(0), elementMutators.get(1), min, max); - }); + .map( + parameterTypes -> + parameterTypes.stream() + .map(factory::tryCreate) + .flatMap(StreamSupport::getOrEmpty) + .collect(Collectors.toList())) + .map( + elementMutators -> { + check(elementMutators.size() == 2); + int min = MapMutator.DEFAULT_MIN_SIZE; + int max = MapMutator.DEFAULT_MAX_SIZE; + for (Annotation annotation : type.getDeclaredAnnotations()) { + if (annotation instanceof WithSize) { + WithSize withSize = (WithSize) annotation; + min = withSize.min(); + max = withSize.max(); + } + } + return new MapMutator<>(elementMutators.get(0), elementMutators.get(1), min, max); + }); } private static final class MapMutator extends SerializingInPlaceMutator> { @@ -85,7 +87,10 @@ private static final class MapMutator extends SerializingInPlaceMutator keyMutator, SerializingMutator valueMutator, int minSize, + MapMutator( + SerializingMutator keyMutator, + SerializingMutator valueMutator, + int minSize, int maxSize) { this.keyMutator = keyMutator; this.valueMutator = valueMutator; @@ -132,14 +137,17 @@ protected Map makeDefaultInstance() { public void initInPlace(Map map, PseudoRandom prng) { int targetSize = prng.closedRange(minInitialSize(), maxInitialSize()); map.clear(); - growBy(map.keySet(), - key - -> map.putIfAbsent(key, valueMutator.init(prng)), - targetSize, () -> keyMutator.init(prng)); + growBy( + map.keySet(), + key -> map.putIfAbsent(key, valueMutator.init(prng)), + targetSize, + () -> keyMutator.init(prng)); if (map.size() < minSize) { - throw new IllegalStateException(String.format( - "Failed to create %d distinct elements of type %s to satisfy the @WithSize#minSize constraint on Map", - minSize, keyMutator)); + throw new IllegalStateException( + String.format( + "Failed to create %d distinct elements of type %s to satisfy the @WithSize#minSize" + + " constraint on Map", + minSize, keyMutator)); } } @@ -150,8 +158,12 @@ public void mutateInPlace(Map map, PseudoRandom prng) { deleteRandomChunk(map.keySet(), minSize, prng); break; case INSERT_CHUNK: - insertRandomChunk(map.keySet(), - key -> map.putIfAbsent(key, valueMutator.init(prng)), maxSize, keyMutator, prng); + insertRandomChunk( + map.keySet(), + key -> map.putIfAbsent(key, valueMutator.init(prng)), + maxSize, + keyMutator, + prng); break; case MUTATE_CHUNK: if (prng.choice() || !mutateRandomKeysChunk(map, keyMutator, prng)) { @@ -165,8 +177,8 @@ public void mutateInPlace(Map map, PseudoRandom prng) { @Override public void crossOverInPlace(Map reference, Map otherReference, PseudoRandom prng) { - switch ( - pickRandomCrossOverAction(reference.keySet(), otherReference.keySet(), maxSize, prng)) { + switch (pickRandomCrossOverAction( + reference.keySet(), otherReference.keySet(), maxSize, prng)) { case INSERT_CHUNK: insertChunk(reference, otherReference, maxSize, prng); break; @@ -183,15 +195,20 @@ public void crossOverInPlace(Map reference, Map otherReference, Pseu @Override public Map detach(Map value) { - return value.entrySet().stream().collect(toMap(entry - -> keyMutator.detach(entry.getKey()), - entry -> valueMutator.detach(entry.getValue()))); + return value.entrySet().stream() + .collect( + toMap( + entry -> keyMutator.detach(entry.getKey()), + entry -> valueMutator.detach(entry.getValue()))); } @Override public String toDebugString(Predicate isInCycle) { - return "Map<" + keyMutator.toDebugString(isInCycle) + "," - + valueMutator.toDebugString(isInCycle) + ">"; + return "Map<" + + keyMutator.toDebugString(isInCycle) + + "," + + valueMutator.toDebugString(isInCycle) + + ">"; } private int minInitialSize() { diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/EnumMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/EnumMutatorFactory.java index 80b9e6c99..859f8bbf9 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/EnumMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/EnumMutatorFactory.java @@ -18,7 +18,6 @@ import static com.code_intelligence.jazzer.mutation.combinator.MutatorCombinators.mutateIndices; import static com.code_intelligence.jazzer.mutation.combinator.MutatorCombinators.mutateThenMap; -import static com.code_intelligence.jazzer.mutation.combinator.MutatorCombinators.mutateThenMapToImmutable; import static com.code_intelligence.jazzer.mutation.support.Preconditions.require; import static com.code_intelligence.jazzer.mutation.support.TypeSupport.asSubclassOrEmpty; @@ -32,16 +31,21 @@ final class EnumMutatorFactory extends MutatorFactory { @Override public Optional> tryCreate(AnnotatedType type, MutatorFactory factory) { - return asSubclassOrEmpty(type, Enum.class).map(parent -> { - require(((Class>) type.getType()).getEnumConstants().length > 1, - String.format( - "%s defines less than two enum constants and can't be mutated. Use a constant instead.", - parent)); - Enum[] values = ((Class>) type.getType()).getEnumConstants(); - return mutateThenMap(mutateIndices(values.length), - (index) - -> values[index], - Enum::ordinal, (Predicate inCycle) -> "Enum<" + parent.getSimpleName() + ">"); - }); + return asSubclassOrEmpty(type, Enum.class) + .map( + parent -> { + require( + ((Class>) type.getType()).getEnumConstants().length > 1, + String.format( + "%s defines less than two enum constants and can't be mutated. Use a constant" + + " instead.", + parent)); + Enum[] values = ((Class>) type.getType()).getEnumConstants(); + return mutateThenMap( + mutateIndices(values.length), + (index) -> values[index], + Enum::ordinal, + (Predicate inCycle) -> "Enum<" + parent.getSimpleName() + ">"); + }); } } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorFactory.java index ed778c5c6..e36a0e31c 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorFactory.java @@ -42,10 +42,33 @@ final class FloatingPointMutatorFactory extends MutatorFactory { @SuppressWarnings("unchecked") private static final DoubleFunction[] mathFunctions = - new DoubleFunction[] {Math::acos, Math::asin, Math::atan, Math::cbrt, Math::ceil, Math::cos, - Math::cosh, Math::exp, Math::expm1, Math::floor, Math::log, Math::log10, Math::log1p, - Math::rint, Math::sin, Math::sinh, Math::sqrt, Math::tan, Math::tanh, Math::toDegrees, - Math::toRadians, n -> n * 0.5, n -> n * 2.0, n -> n * 0.333333333333333, n -> n * 3.0}; + new DoubleFunction[] { + Math::acos, + Math::asin, + Math::atan, + Math::cbrt, + Math::ceil, + Math::cos, + Math::cosh, + Math::exp, + Math::expm1, + Math::floor, + Math::log, + Math::log10, + Math::log1p, + Math::rint, + Math::sin, + Math::sinh, + Math::sqrt, + Math::tan, + Math::tanh, + Math::toDegrees, + Math::toRadians, + n -> n * 0.5, + n -> n * 2.0, + n -> n * 0.333333333333333, + n -> n * 3.0 + }; @Override public Optional> tryCreate(AnnotatedType type, MutatorFactory factory) { @@ -79,7 +102,10 @@ static final class FloatMutator extends SerializingMutator { final boolean allowNaN; private final float[] specialValues; - FloatMutator(AnnotatedType type, float defaultMinValueForType, float defaultMaxValueForType, + FloatMutator( + AnnotatedType type, + float defaultMinValueForType, + float defaultMaxValueForType, boolean defaultAllowNaN) { float minValue = defaultMinValueForType; float maxValue = defaultMaxValueForType; @@ -94,9 +120,11 @@ static final class FloatMutator extends SerializingMutator { } } - require(minValue <= maxValue, + require( + minValue <= maxValue, format("[%f, %f] is not a valid interval: %s", minValue, maxValue, type)); - require(minValue != maxValue, + require( + minValue != maxValue, format( "[%f, %f] can not be mutated, use a constant instead: %s", minValue, maxValue, type)); this.minValue = minValue; @@ -108,10 +136,18 @@ static final class FloatMutator extends SerializingMutator { private float[] collectSpecialValues(float minValue, float maxValue) { // stream of floats List specialValues = - DoubleStream - .of(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, 0.0f, -0.0f, Float.NaN, - Float.MAX_VALUE, Float.MIN_VALUE, -Float.MAX_VALUE, -Float.MIN_VALUE, - this.minValue, this.maxValue) + DoubleStream.of( + Float.NEGATIVE_INFINITY, + Float.POSITIVE_INFINITY, + 0.0f, + -0.0f, + Float.NaN, + Float.MAX_VALUE, + Float.MIN_VALUE, + -Float.MAX_VALUE, + -Float.MIN_VALUE, + this.minValue, + this.maxValue) .filter(n -> (n >= minValue && n <= maxValue) || allowNaN && Double.isNaN(n)) .distinct() .sorted() @@ -175,12 +211,12 @@ public Float mutate(Float value, PseudoRandom prng) { return prng.closedRange(minValue, maxValue); } else { // Change the value to the neighboring float. if (result > minValue && result < maxValue) { - return prng.choice() ? Math.nextAfter(result, Float.NEGATIVE_INFINITY) - : Math.nextAfter(result, Float.POSITIVE_INFINITY); + return prng.choice() + ? Math.nextAfter(result, Float.NEGATIVE_INFINITY) + : Math.nextAfter(result, Float.POSITIVE_INFINITY); } else if (result > minValue) { return Math.nextAfter(result, Float.NEGATIVE_INFINITY); - } else - return Math.nextAfter(result, Float.POSITIVE_INFINITY); + } else return Math.nextAfter(result, Float.POSITIVE_INFINITY); } } @@ -192,22 +228,18 @@ static float forceInRange(float value, float minValue, float maxValue, boolean a return value; // Clamp infinite values - if (value == Float.POSITIVE_INFINITY) - return maxValue; - if (value == Float.NEGATIVE_INFINITY) - return minValue; + if (value == Float.POSITIVE_INFINITY) return maxValue; + if (value == Float.NEGATIVE_INFINITY) return minValue; // From here on limits should be finite float finiteMax = Math.min(Float.MAX_VALUE, maxValue); float finiteMin = Math.max(-Float.MAX_VALUE, minValue); // If NaN was allowed, it was handled above. Replace it by the midpoint of the range. - if (Float.isNaN(value)) - return finiteMin * 0.5f + finiteMax * 0.5f; + if (Float.isNaN(value)) return finiteMin * 0.5f + finiteMax * 0.5f; float range = finiteMax - finiteMin; - if (range == 0f) - return finiteMin; + if (range == 0f) return finiteMin; float diff = value - finiteMin; @@ -234,10 +266,12 @@ private float mutateWithBitFlip(float value, PseudoRandom prng) { private float mutateExponent(float value, PseudoRandom prng) { int bits = Float.floatToRawIntBits(value); - int exponent = ((bits >> EXPONENT_INITIAL_BIT) & EXPONENT_MASK) - + prng.closedRange(0, EXPONENT_RANDOM_WALK_RANGE); - bits = (bits & ~(EXPONENT_MASK << EXPONENT_INITIAL_BIT)) - | ((exponent % EXPONENT_MASK) << EXPONENT_INITIAL_BIT); + int exponent = + ((bits >> EXPONENT_INITIAL_BIT) & EXPONENT_MASK) + + prng.closedRange(0, EXPONENT_RANDOM_WALK_RANGE); + bits = + (bits & ~(EXPONENT_MASK << EXPONENT_INITIAL_BIT)) + | ((exponent % EXPONENT_MASK) << EXPONENT_INITIAL_BIT); return Float.intBitsToFloat(bits); } @@ -249,12 +283,12 @@ private float mutateMantissa(float value, PseudoRandom prng) { case 0: // + mantissa = (mantissa + prng.closedRange(-MANTISSA_RANDOM_WALK_RANGE, MANTISSA_RANDOM_WALK_RANGE)) - % MANTISSA_MASK; + % MANTISSA_MASK; break; case 1: // * mantissa = (mantissa * prng.closedRange(-MANTISSA_RANDOM_WALK_RANGE, MANTISSA_RANDOM_WALK_RANGE)) - % MANTISSA_MASK; + % MANTISSA_MASK; break; case 2: // / int divisor = prng.closedRange(2, MANTISSA_RANDOM_WALK_RANGE); @@ -343,7 +377,10 @@ static final class DoubleMutator extends SerializingMutator { final boolean allowNaN; private final double[] specialValues; - DoubleMutator(AnnotatedType type, double defaultMinValueForType, double defaultMaxValueForType, + DoubleMutator( + AnnotatedType type, + double defaultMinValueForType, + double defaultMaxValueForType, boolean defaultAllowNaN) { double minValue = defaultMinValueForType; double maxValue = defaultMaxValueForType; @@ -358,11 +395,14 @@ static final class DoubleMutator extends SerializingMutator { } } - require(!Double.isNaN(minValue) && !Double.isNaN(maxValue), + require( + !Double.isNaN(minValue) && !Double.isNaN(maxValue), format("[%f, %f] is not a valid interval: %s", minValue, maxValue, type)); - require(minValue <= maxValue, + require( + minValue <= maxValue, format("[%f, %f] is not a valid interval: %s", minValue, maxValue, type)); - require(minValue != maxValue, + require( + minValue != maxValue, format( "[%f, %f] can not be mutated, use a constant instead: %s", minValue, maxValue, type)); this.minValue = minValue; @@ -372,9 +412,20 @@ static final class DoubleMutator extends SerializingMutator { } private double[] collectSpecialValues(double minValue, double maxValue) { - double[] specialValues = new double[] {Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, - 0.0, -0.0, Double.NaN, Double.MAX_VALUE, Double.MIN_VALUE, -Double.MAX_VALUE, - -Double.MIN_VALUE, this.minValue, this.maxValue}; + double[] specialValues = + new double[] { + Double.NEGATIVE_INFINITY, + Double.POSITIVE_INFINITY, + 0.0, + -0.0, + Double.NaN, + Double.MAX_VALUE, + Double.MIN_VALUE, + -Double.MAX_VALUE, + -Double.MIN_VALUE, + this.minValue, + this.maxValue + }; return Arrays.stream(specialValues) .boxed() .filter(value -> (allowNaN && value.isNaN()) || (value >= minValue && value <= maxValue)) @@ -435,12 +486,12 @@ public Double mutate(Double value, PseudoRandom prng) { return prng.closedRange(minValue, maxValue); } else { // Change the value to the neighboring float. if (result > minValue && result < maxValue) { - return prng.choice() ? Math.nextAfter(result, Double.NEGATIVE_INFINITY) - : Math.nextAfter(result, Double.POSITIVE_INFINITY); + return prng.choice() + ? Math.nextAfter(result, Double.NEGATIVE_INFINITY) + : Math.nextAfter(result, Double.POSITIVE_INFINITY); } else if (result > minValue) { return Math.nextAfter(result, Double.NEGATIVE_INFINITY); - } else - return Math.nextAfter(result, Double.POSITIVE_INFINITY); + } else return Math.nextAfter(result, Double.POSITIVE_INFINITY); } } @@ -453,10 +504,8 @@ static double forceInRange(double value, double minValue, double maxValue, boole } // Clamp infinite values - if (value == Double.POSITIVE_INFINITY) - return maxValue; - if (value == Double.NEGATIVE_INFINITY) - return minValue; + if (value == Double.POSITIVE_INFINITY) return maxValue; + if (value == Double.NEGATIVE_INFINITY) return minValue; // From here on limits should be finite double finiteMax = Math.min(Double.MAX_VALUE, maxValue); @@ -472,8 +521,7 @@ static double forceInRange(double value, double minValue, double maxValue, boole } double range = finiteMax - finiteMin; - if (range == 0) - return finiteMin; + if (range == 0) return finiteMin; double diff = value - finiteMin; @@ -499,10 +547,12 @@ public static double mutateWithBitFlip(double value, PseudoRandom prng) { private static double mutateExponent(double value, PseudoRandom prng) { long bits = Double.doubleToRawLongBits(value); - long exponent = ((bits >> EXPONENT_INITIAL_BIT) & EXPONENT_MASK) - + prng.closedRange(0, EXPONENT_RANDOM_WALK_RANGE); - bits = (bits & ~(EXPONENT_MASK << EXPONENT_INITIAL_BIT)) - | ((exponent % EXPONENT_MASK) << EXPONENT_INITIAL_BIT); + long exponent = + ((bits >> EXPONENT_INITIAL_BIT) & EXPONENT_MASK) + + prng.closedRange(0, EXPONENT_RANDOM_WALK_RANGE); + bits = + (bits & ~(EXPONENT_MASK << EXPONENT_INITIAL_BIT)) + | ((exponent % EXPONENT_MASK) << EXPONENT_INITIAL_BIT); return Double.longBitsToDouble(bits); } @@ -513,12 +563,12 @@ public static double mutateMantissa(double value, PseudoRandom prng) { case 0: // + mantissa = (mantissa + prng.closedRange(-MANTISSA_RANDOM_WALK_RANGE, MANTISSA_RANDOM_WALK_RANGE)) - % MANTISSA_MASK; + % MANTISSA_MASK; break; case 1: // * mantissa = (mantissa * prng.closedRange(-MANTISSA_RANDOM_WALK_RANGE, MANTISSA_RANDOM_WALK_RANGE)) - % MANTISSA_MASK; + % MANTISSA_MASK; break; case 2: // / long divisor = prng.closedRange(2, MANTISSA_RANDOM_WALK_RANGE); diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/IntegralMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/IntegralMutatorFactory.java index be4dd50bb..2a1e7350e 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/IntegralMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/IntegralMutatorFactory.java @@ -45,37 +45,38 @@ public Optional> tryCreate(AnnotatedType type, MutatorFact Class clazz = (Class) type.getType(); if (clazz == byte.class || clazz == Byte.class) { - return Optional.of(new AbstractIntegralMutator(type, Byte.MIN_VALUE, Byte.MAX_VALUE) { - @Override - protected long mutateWithLibFuzzer(long value) { - return LibFuzzerMutate.mutateDefault((byte) value, this, 0); - } + return Optional.of( + new AbstractIntegralMutator(type, Byte.MIN_VALUE, Byte.MAX_VALUE) { + @Override + protected long mutateWithLibFuzzer(long value) { + return LibFuzzerMutate.mutateDefault((byte) value, this, 0); + } - @Override - public Byte init(PseudoRandom prng) { - return (byte) initImpl(prng); - } + @Override + public Byte init(PseudoRandom prng) { + return (byte) initImpl(prng); + } - @Override - public Byte mutate(Byte value, PseudoRandom prng) { - return (byte) mutateImpl(value, prng); - } + @Override + public Byte mutate(Byte value, PseudoRandom prng) { + return (byte) mutateImpl(value, prng); + } - @Override - public Byte crossOver(Byte value, Byte otherValue, PseudoRandom prng) { - return (byte) crossOverImpl(value, otherValue, prng); - } + @Override + public Byte crossOver(Byte value, Byte otherValue, PseudoRandom prng) { + return (byte) crossOverImpl(value, otherValue, prng); + } - @Override - public Byte read(DataInputStream in) throws IOException { - return (byte) forceInRange(in.readByte()); - } + @Override + public Byte read(DataInputStream in) throws IOException { + return (byte) forceInRange(in.readByte()); + } - @Override - public void write(Byte value, DataOutputStream out) throws IOException { - out.writeByte(value); - } - }); + @Override + public void write(Byte value, DataOutputStream out) throws IOException { + out.writeByte(value); + } + }); } else if (clazz == short.class || clazz == Short.class) { return Optional.of( new AbstractIntegralMutator(type, Short.MIN_VALUE, Short.MAX_VALUE) { @@ -143,37 +144,38 @@ public void write(Integer value, DataOutputStream out) throws IOException { } }); } else if (clazz == long.class || clazz == Long.class) { - return Optional.of(new AbstractIntegralMutator(type, Long.MIN_VALUE, Long.MAX_VALUE) { - @Override - protected long mutateWithLibFuzzer(long value) { - return LibFuzzerMutate.mutateDefault(value, this, 0); - } + return Optional.of( + new AbstractIntegralMutator(type, Long.MIN_VALUE, Long.MAX_VALUE) { + @Override + protected long mutateWithLibFuzzer(long value) { + return LibFuzzerMutate.mutateDefault(value, this, 0); + } - @Override - public Long init(PseudoRandom prng) { - return initImpl(prng); - } + @Override + public Long init(PseudoRandom prng) { + return initImpl(prng); + } - @Override - public Long mutate(Long value, PseudoRandom prng) { - return mutateImpl(value, prng); - } + @Override + public Long mutate(Long value, PseudoRandom prng) { + return mutateImpl(value, prng); + } - @Override - public Long crossOver(Long value, Long otherValue, PseudoRandom prng) { - return crossOverImpl(value, otherValue, prng); - } + @Override + public Long crossOver(Long value, Long otherValue, PseudoRandom prng) { + return crossOverImpl(value, otherValue, prng); + } - @Override - public Long read(DataInputStream in) throws IOException { - return forceInRange(in.readLong()); - } + @Override + public Long read(DataInputStream in) throws IOException { + return forceInRange(in.readLong()); + } - @Override - public void write(Long value, DataOutputStream out) throws IOException { - out.writeLong(value); - } - }); + @Override + public void write(Long value, DataOutputStream out) throws IOException { + out.writeLong(value); + } + }); } else { return Optional.empty(); } @@ -185,7 +187,7 @@ public void write(Long value, DataOutputStream out) throws IOException { // Copyright 2022 Google LLC // // Visible for testing. - static abstract class AbstractIntegralMutator extends SerializingMutator { + abstract static class AbstractIntegralMutator extends SerializingMutator { private static final long RANDOM_WALK_RANGE = 5; private final long minValue; private final long maxValue; @@ -209,21 +211,25 @@ static abstract class AbstractIntegralMutator extends Serializ // Byte.MAX_VALUE instead. IDEs will warn about the redundant specification of the default // value, so this should not be a problem in practice. if (inRange.min() != Long.MIN_VALUE) { - require(inRange.min() >= defaultMinValueForType, + require( + inRange.min() >= defaultMinValueForType, format("@InRange.min=%d is out of range: %s", inRange.min(), type.getType())); minValue = inRange.min(); } if (inRange.max() != Long.MAX_VALUE) { - require(inRange.max() <= defaultMaxValueForType, + require( + inRange.max() <= defaultMaxValueForType, format("@InRange.max=%d is out of range: %s", inRange.max(), type.getType())); maxValue = inRange.max(); } } } - require(minValue <= maxValue, + require( + minValue <= maxValue, format("[%d, %d] is not a valid interval: %s", minValue, maxValue, type)); - require(minValue != maxValue, + require( + minValue != maxValue, format( "[%d, %d] can not be mutated, use a constant instead: %s", minValue, maxValue, type)); this.minValue = minValue; @@ -319,7 +325,8 @@ private static long mean(long x, long y) { return mean + (1 & xor & (mean >>> 31)); } - @ForOverride protected abstract long mutateWithLibFuzzer(long value); + @ForOverride + protected abstract long mutateWithLibFuzzer(long value); /** * Force value into the closed interval [minValue, maxValue] while preserving as many of its @@ -391,7 +398,8 @@ public T detach(T value) { @Override public String toDebugString(Predicate isInCycle) { - return ((Class) ((ParameterizedType) this.getClass().getGenericSuperclass()) + return ((Class) + ((ParameterizedType) this.getClass().getGenericSuperclass()) .getActualTypeArguments()[0]) .getSimpleName(); } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/LangMutators.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/LangMutators.java index 00ea8b477..580cf3b31 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/LangMutators.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/LangMutators.java @@ -23,8 +23,13 @@ public final class LangMutators { private LangMutators() {} public static MutatorFactory newFactory() { - return new ChainedMutatorFactory(new NullableMutatorFactory(), new BooleanMutatorFactory(), - new FloatingPointMutatorFactory(), new IntegralMutatorFactory(), - new ByteArrayMutatorFactory(), new StringMutatorFactory(), new EnumMutatorFactory()); + return new ChainedMutatorFactory( + new NullableMutatorFactory(), + new BooleanMutatorFactory(), + new FloatingPointMutatorFactory(), + new IntegralMutatorFactory(), + new ByteArrayMutatorFactory(), + new StringMutatorFactory(), + new EnumMutatorFactory()); } } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorFactory.java index d77cb9d35..914403591 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorFactory.java @@ -49,7 +49,7 @@ static void fixUpAscii(byte[] bytes) { // SPDX: Apache-2.0 // Copyright 2022 Google LLC static void fixUpUtf8(byte[] bytes) { - for (int pos = 0; pos < bytes.length;) { + for (int pos = 0; pos < bytes.length; ) { // Leniently read a UTF-8 code point consisting of any byte viewed as the leading byte and up // to three following bytes that have a continuation byte header. // @@ -152,21 +152,21 @@ public Optional> tryCreate(AnnotatedType type, MutatorFact return findFirstParentIfClass(type, String.class) .flatMap(parent -> factory.tryCreate(innerByteArray)) - .map(byteArrayMutator -> { - boolean fixUpAscii = type.getDeclaredAnnotation(Ascii.class) != null; - return mutateThenMapToImmutable((SerializingMutator) byteArrayMutator, - bytes - -> { - if (fixUpAscii) { - fixUpAscii(bytes); - } else { - fixUpUtf8(bytes); - } - return new String(bytes, StandardCharsets.UTF_8); - }, - string - -> string.getBytes(StandardCharsets.UTF_8), - (Predicate inCycle) -> "String"); - }); + .map( + byteArrayMutator -> { + boolean fixUpAscii = type.getDeclaredAnnotation(Ascii.class) != null; + return mutateThenMapToImmutable( + (SerializingMutator) byteArrayMutator, + bytes -> { + if (fixUpAscii) { + fixUpAscii(bytes); + } else { + fixUpUtf8(bytes); + } + return new String(bytes, StandardCharsets.UTF_8); + }, + string -> string.getBytes(StandardCharsets.UTF_8), + (Predicate inCycle) -> "String"); + }); } } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/FuzzedDataProviderMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/FuzzedDataProviderMutatorFactory.java index 95bff12a7..a015977c2 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/FuzzedDataProviderMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/libfuzzer/FuzzedDataProviderMutatorFactory.java @@ -36,9 +36,12 @@ final class FuzzedDataProviderMutatorFactory extends MutatorFactory { public Optional> tryCreate(AnnotatedType type, MutatorFactory factory) { return asSubclassOrEmpty(type, FuzzedDataProvider.class) .flatMap(parent -> factory.tryCreate(notNull(asAnnotatedType(byte[].class)))) - .map(byteArrayMutator - -> mutateThenMap((SerializingMutator) byteArrayMutator, - FuzzedDataProviderImpl::withJavaData, FuzzedDataProviderImpl::getJavaData, - (Predicate inCycle) -> "FuzzedDataProvider")); + .map( + byteArrayMutator -> + mutateThenMap( + (SerializingMutator) byteArrayMutator, + FuzzedDataProviderImpl::withJavaData, + FuzzedDataProviderImpl::getJavaData, + (Predicate inCycle) -> "FuzzedDataProvider")); } } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java index 85427c6f4..4ab5e0a26 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java @@ -93,9 +93,11 @@ private InPlaceMutator mutatorForField( if (field.isMapField()) { SerializingInPlaceMutator underlyingMutator = (SerializingInPlaceMutator) factory.createInPlaceOrThrow(typeToMutate); - mutator = mutateProperty(builder - -> getMapField(builder, field), - underlyingMutator, (builder, value) -> setMapField(builder, field, value)); + mutator = + mutateProperty( + builder -> getMapField(builder, field), + underlyingMutator, + (builder, value) -> setMapField(builder, field, value)); } else if (field.isRepeated()) { SerializingInPlaceMutator> underlyingMutator = (SerializingInPlaceMutator>) factory.createInPlaceOrThrow(typeToMutate); @@ -104,15 +106,19 @@ private InPlaceMutator mutatorForField( } else if (field.hasPresence()) { SerializingMutator underlyingMutator = (SerializingMutator) factory.createOrThrow(typeToMutate); - mutator = mutateProperty(builder - -> getPresentFieldOrNull(builder, field), - underlyingMutator, (builder, value) -> setFieldWithPresence(builder, field, value)); + mutator = + mutateProperty( + builder -> getPresentFieldOrNull(builder, field), + underlyingMutator, + (builder, value) -> setFieldWithPresence(builder, field, value)); } else { SerializingMutator underlyingMutator = (SerializingMutator) factory.createOrThrow(typeToMutate); - mutator = mutateProperty(builder - -> (U) builder.getField(field), - underlyingMutator, (builder, value) -> builder.setField(field, value)); + mutator = + mutateProperty( + builder -> (U) builder.getField(field), + underlyingMutator, + (builder, value) -> builder.setField(field, value)); } // If recursive message fields (i.e. those that have themselves as transitive subfields) are @@ -127,27 +133,37 @@ private MutatorFactory withDescriptorDependentMutatorFactoryIfNeeded( if (field.getJavaType() == JavaType.ENUM) { // Proto enum fields are special as their type (EnumValueDescriptor) does not encode their // domain - we need the actual EnumDescriptor instance. - return new ChainedMutatorFactory(originalFactory, new MutatorFactory() { - @Override - public Optional> tryCreate( - AnnotatedType type, MutatorFactory factory) { - return findFirstParentIfClass(type, EnumValueDescriptor.class).map(parent -> { - EnumDescriptor enumType = field.getEnumType(); - List values = enumType.getValues(); - String name = enumType.getName(); - if (values.size() == 1) { - // While we generally prefer to error out instead of creating a mutator that can't - // actually mutate its domain, we can't do that for proto enum fields as the user - // creating the fuzz test may not be in a position to modify the existing proto - // definition. - return fixedValue(values.get(0)); - } else { - return mutateThenMapToImmutable(mutateIndices(values.size()), values::get, - EnumValueDescriptor::getIndex, unused -> "Enum<" + name + ">"); + return new ChainedMutatorFactory( + originalFactory, + new MutatorFactory() { + @Override + public Optional> tryCreate( + AnnotatedType type, MutatorFactory factory) { + return findFirstParentIfClass(type, EnumValueDescriptor.class) + .map( + parent -> { + EnumDescriptor enumType = field.getEnumType(); + List values = enumType.getValues(); + String name = enumType.getName(); + if (values.size() == 1) { + // While we generally prefer to error out instead of creating a mutator + // that can't + // actually mutate its domain, we can't do that for proto enum fields as + // the user + // creating the fuzz test may not be in a position to modify the existing + // proto + // definition. + return fixedValue(values.get(0)); + } else { + return mutateThenMapToImmutable( + mutateIndices(values.size()), + values::get, + EnumValueDescriptor::getIndex, + unused -> "Enum<" + name + ">"); + } + }); } }); - } - }); } else if (field.getJavaType() == JavaType.MESSAGE) { Descriptor messageDescriptor; if (field.isMapField()) { @@ -161,31 +177,43 @@ public Optional> tryCreate( } else { messageDescriptor = field.getMessageType(); } - return new ChainedMutatorFactory(originalFactory, new MutatorFactory() { - @Override - public Optional> tryCreate( - AnnotatedType type, MutatorFactory factory) { - return asSubclassOrEmpty(type, Message.Builder.class).flatMap(clazz -> { - // BuilderMutatorFactory only handles subclasses of Message.Builder and requests - // Message.Builder itself for message fields, which we handle here. - if (clazz != Message.Builder.class) { - return Optional.empty(); + return new ChainedMutatorFactory( + originalFactory, + new MutatorFactory() { + @Override + public Optional> tryCreate( + AnnotatedType type, MutatorFactory factory) { + return asSubclassOrEmpty(type, Message.Builder.class) + .flatMap( + clazz -> { + // BuilderMutatorFactory only handles subclasses of Message.Builder and + // requests + // Message.Builder itself for message fields, which we handle here. + if (clazz != Message.Builder.class) { + return Optional.empty(); + } + // It is important that we use originalFactory here instead of factory: + // factory has this + // field-specific message mutator appended, but this mutator should only be + // used for + // this particular field and not any message subfields. + return Optional.of( + makeBuilderMutator( + originalFactory, + DynamicMessage.getDefaultInstance(messageDescriptor), + annotations)); + }); } - // It is important that we use originalFactory here instead of factory: factory has this - // field-specific message mutator appended, but this mutator should only be used for - // this particular field and not any message subfields. - return Optional.of(makeBuilderMutator(originalFactory, - DynamicMessage.getDefaultInstance(messageDescriptor), annotations)); }); - } - }); } else { return originalFactory; } } private Stream> mutatorsForFields( - Optional oneofField, List fields, Annotation[] annotations, + Optional oneofField, + List fields, + Annotation[] annotations, MutatorFactory factory) { if (oneofField.isPresent()) { // oneof fields are mutated as one as mutating them independently would cause the mutator to @@ -199,21 +227,21 @@ private Stream> mutatorsForFields( indexInOneof.put(oneofDescriptor.getField(i), i); } - return Stream.of(mutateSumInPlace( - (T builder) - -> { - FieldDescriptor setField = builder.getOneofFieldDescriptor(oneofDescriptor); - if (setField == null) { - return -1; - } else { - return indexInOneof.get(setField); - } - }, - // Mutating to the unset (-1) state is handled by the individual field mutators, which - // are created nullable as oneof fields report that they track presence. - fields.stream() - .map(field -> mutatorForField(field, annotations, factory)) - .toArray(InPlaceMutator[] ::new))); + return Stream.of( + mutateSumInPlace( + (T builder) -> { + FieldDescriptor setField = builder.getOneofFieldDescriptor(oneofDescriptor); + if (setField == null) { + return -1; + } else { + return indexInOneof.get(setField); + } + }, + // Mutating to the unset (-1) state is handled by the individual field mutators, which + // are created nullable as oneof fields report that they track presence. + fields.stream() + .map(field -> mutatorForField(field, annotations, factory)) + .toArray(InPlaceMutator[]::new))); } else { // All non-oneof fields are mutated independently, using the order in which they are declared // in the .proto file (which may not coincide with the order by field number). @@ -306,37 +334,40 @@ private SerializingMutator mutatorForAny( .boxed() .collect(toMap(i -> getTypeUrl(getDefaultInstance(anySource.value()[i])), identity())); - return assemble(mutator - -> internedMutators.put(new CacheKey(Any.getDescriptor(), anySource), mutator), - Any.getDefaultInstance()::toBuilder, makeBuilderSerializer(Any.getDefaultInstance()), - () - -> mutateSumInPlace( + return assemble( + mutator -> internedMutators.put(new CacheKey(Any.getDescriptor(), anySource), mutator), + Any.getDefaultInstance()::toBuilder, + makeBuilderSerializer(Any.getDefaultInstance()), + () -> + mutateSumInPlace( // Corpus entries may contain Anys with arbitrary (and even invalid) messages, so we // fall back to mutating the first message type if the type isn't recognized. - (Any.Builder builder) - -> typeUrlToIndex.getOrDefault(builder.getTypeUrl(), 0), + (Any.Builder builder) -> typeUrlToIndex.getOrDefault(builder.getTypeUrl(), 0), stream(anySource.value()) - .map(messageClass -> { - SerializingMutator messageMutator = - (SerializingMutator) factory.createOrThrow(notNull( - withExtraAnnotations(asAnnotatedType(messageClass), anySource))); - return mutateProperty( - (Any.Builder anyBuilder) - -> { - try { - return anyBuilder.build().unpack(messageClass); - } catch (InvalidProtocolBufferException e) { - // This can only happen if the corpus contains an invalid Any. - return getDefaultInstance(messageClass); - } - }, - messageMutator, - (Any.Builder any, Message message) -> { - any.setTypeUrl(getTypeUrl(message)); - any.setValue(message.toByteString()); - }); - }) - .toArray(InPlaceMutator[] ::new))); + .map( + messageClass -> { + SerializingMutator messageMutator = + (SerializingMutator) + factory.createOrThrow( + notNull( + withExtraAnnotations( + asAnnotatedType(messageClass), anySource))); + return mutateProperty( + (Any.Builder anyBuilder) -> { + try { + return anyBuilder.build().unpack(messageClass); + } catch (InvalidProtocolBufferException e) { + // This can only happen if the corpus contains an invalid Any. + return getDefaultInstance(messageClass); + } + }, + messageMutator, + (Any.Builder any, Message message) -> { + any.setTypeUrl(getTypeUrl(message)); + any.setValue(message.toByteString()); + }); + }) + .toArray(InPlaceMutator[]::new))); } private static String getTypeUrl(Message message) { @@ -347,38 +378,46 @@ private static String getTypeUrl(Message message) { @Override public Optional> tryCreate(AnnotatedType type, MutatorFactory factory) { - return asSubclassOrEmpty(type, Builder.class).flatMap(builderClass -> { - Message defaultInstance; - WithDefaultInstance withDefaultInstance = type.getAnnotation(WithDefaultInstance.class); - if (withDefaultInstance != null) { - defaultInstance = getDefaultInstance(withDefaultInstance); - } else if (builderClass == DynamicMessage.Builder.class) { - throw new IllegalArgumentException( - "To mutate a dynamic message, add a @WithDefaultInstance annotation specifying the" - + " fully qualified method name of a static method returning a default instance"); - } else if (builderClass == Message.Builder.class) { - // Handled by a custom mutator factory for message fields that is created in - // withDescriptorDependentMutatorFactoryIfNeeded. Without @WithDefaultInstance, - // BuilderMutatorFactory only handles proper subclasses, which correspond to generated - // message types. - return Optional.empty(); - } else { - defaultInstance = - getDefaultInstance((Class) builderClass.getEnclosingClass()); - } + return asSubclassOrEmpty(type, Builder.class) + .flatMap( + builderClass -> { + Message defaultInstance; + WithDefaultInstance withDefaultInstance = + type.getAnnotation(WithDefaultInstance.class); + if (withDefaultInstance != null) { + defaultInstance = getDefaultInstance(withDefaultInstance); + } else if (builderClass == DynamicMessage.Builder.class) { + throw new IllegalArgumentException( + "To mutate a dynamic message, add a @WithDefaultInstance annotation specifying" + + " the fully qualified method name of a static method returning a default" + + " instance"); + } else if (builderClass == Message.Builder.class) { + // Handled by a custom mutator factory for message fields that is created in + // withDescriptorDependentMutatorFactoryIfNeeded. Without @WithDefaultInstance, + // BuilderMutatorFactory only handles proper subclasses, which correspond to + // generated + // message types. + return Optional.empty(); + } else { + defaultInstance = + getDefaultInstance((Class) builderClass.getEnclosingClass()); + } - return Optional.of( - makeBuilderMutator(factory, defaultInstance, type.getDeclaredAnnotations())); - }); + return Optional.of( + makeBuilderMutator(factory, defaultInstance, type.getDeclaredAnnotations())); + }); } private SerializingMutator makeBuilderMutator( MutatorFactory factory, Message defaultInstance, Annotation[] annotations) { - AnySource anySource = (AnySource) stream(annotations) - .filter(annotation -> annotation.annotationType() == AnySource.class) - .findFirst() - .orElse(null); - Preconditions.require(anySource == null || anySource.value().length > 0, + AnySource anySource = + (AnySource) + stream(annotations) + .filter(annotation -> annotation.annotationType() == AnySource.class) + .findFirst() + .orElse(null); + Preconditions.require( + anySource == null || anySource.value().length > 0, "@AnySource must list a non-empty list of classes"); Descriptor descriptor = defaultInstance.getDescriptorForType(); @@ -397,29 +436,37 @@ private SerializingMutator makeBuilderMutator( // internedMutators map *before* recursively creating the mutators for its fields, which // ensures that the recursion is finite (bounded by the total number of distinct message types // that transitively occur as field types on the current message type). - return assemble(mutator - -> internedMutators.put(cacheKey, mutator), - defaultInstance::toBuilder, makeBuilderSerializer(defaultInstance), - () - -> combine( - descriptor.getFields() - .stream() + return assemble( + mutator -> internedMutators.put(cacheKey, mutator), + defaultInstance::toBuilder, + makeBuilderSerializer(defaultInstance), + () -> + combine( + descriptor.getFields().stream() // Keep oneofs sorted by the first appearance of their fields in the // .proto file. - .collect(groupingBy( - // groupingBy does not support null keys. We use getRealContainingOneof() - // instead of getContainingOneof() as the latter also reports oneofs for - // proto3 optional fields, which we handle separately. - fieldDescriptor - -> Optional.ofNullable(fieldDescriptor.getRealContainingOneof()), - LinkedHashMap::new, toList())) + .collect( + groupingBy( + // groupingBy does not support null keys. We use + // getRealContainingOneof() + // instead of getContainingOneof() as the latter also reports oneofs for + // proto3 optional fields, which we handle separately. + fieldDescriptor -> + Optional.ofNullable(fieldDescriptor.getRealContainingOneof()), + LinkedHashMap::new, + toList())) .entrySet() .stream() - .flatMap(entry - -> mutatorsForFields(entry.getKey(), entry.getValue(), - anySource == null ? new Annotation[0] : new Annotation[] {anySource}, - factory)) - .toArray(InPlaceMutator[] ::new))); + .flatMap( + entry -> + mutatorsForFields( + entry.getKey(), + entry.getValue(), + anySource == null + ? new Annotation[0] + : new Annotation[] {anySource}, + factory)) + .toArray(InPlaceMutator[]::new))); } private static final class CacheKey { diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/ByteStringMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/ByteStringMutatorFactory.java index a01ffae62..2a901fc54 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/ByteStringMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/ByteStringMutatorFactory.java @@ -34,8 +34,11 @@ final class ByteStringMutatorFactory extends MutatorFactory { public Optional> tryCreate(AnnotatedType type, MutatorFactory factory) { return findFirstParentIfClass(type, ByteString.class) .flatMap(parent -> factory.tryCreate(notNull(asAnnotatedType(byte[].class)))) - .map(byteArrayMutator - -> mutateThenMapToImmutable((SerializingMutator) byteArrayMutator, - ByteString::copyFrom, ByteString::toByteArray)); + .map( + byteArrayMutator -> + mutateThenMapToImmutable( + (SerializingMutator) byteArrayMutator, + ByteString::copyFrom, + ByteString::toByteArray)); } } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorFactory.java index eb3220f5a..9fafc2d67 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorFactory.java @@ -36,17 +36,22 @@ public Optional> tryCreate( return asSubclassOrEmpty(messageType, Message.class) // If the Message class doesn't have a nested Builder class, it is not a concrete generated // message and we can't mutate it. - .flatMap(messageClass - -> Arrays.stream(messageClass.getDeclaredClasses()) - .filter(clazz -> clazz.getSimpleName().equals("Builder")) - .findFirst()) - .flatMap(builderClass - -> - // Forward the annotations (e.g. @NotNull) on the Message type to the Builder type. - factory.tryCreateInPlace( - withExtraAnnotations(asAnnotatedType(builderClass), messageType.getAnnotations()))) - .map(builderMutator - -> mutateThenMapToImmutable( - (SerializingMutator) builderMutator, Builder::build, Message::toBuilder)); + .flatMap( + messageClass -> + Arrays.stream(messageClass.getDeclaredClasses()) + .filter(clazz -> clazz.getSimpleName().equals("Builder")) + .findFirst()) + .flatMap( + builderClass -> + // Forward the annotations (e.g. @NotNull) on the Message type to the Builder type. + factory.tryCreateInPlace( + withExtraAnnotations( + asAnnotatedType(builderClass), messageType.getAnnotations()))) + .map( + builderMutator -> + mutateThenMapToImmutable( + (SerializingMutator) builderMutator, + Builder::build, + Message::toBuilder)); } } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/TypeLibrary.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/TypeLibrary.java index 43338f148..ae6ce93ea 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/TypeLibrary.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/TypeLibrary.java @@ -33,15 +33,12 @@ import com.code_intelligence.jazzer.mutation.api.InPlaceMutator; import com.code_intelligence.jazzer.mutation.support.TypeHolder; import com.google.protobuf.ByteString; -import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor.JavaType; -import com.google.protobuf.DynamicMessage; import com.google.protobuf.Message; import com.google.protobuf.Message.Builder; import java.lang.reflect.AnnotatedType; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -55,14 +52,20 @@ final class TypeLibrary { private static final AnnotatedType RAW_LIST = new TypeHolder<@NotNull List>() {}.annotatedType(); private static final AnnotatedType RAW_MAP = new TypeHolder<@NotNull Map>() {}.annotatedType(); private static final Map BASE_TYPE_WITH_PRESENCE = - Stream - .of(entry(JavaType.BOOLEAN, Boolean.class), entry(JavaType.BYTE_STRING, ByteString.class), - entry(JavaType.DOUBLE, Double.class), entry(JavaType.ENUM, EnumValueDescriptor.class), - entry(JavaType.FLOAT, Float.class), entry(JavaType.INT, Integer.class), - entry(JavaType.LONG, Long.class), entry(JavaType.MESSAGE, Message.class), + Stream.of( + entry(JavaType.BOOLEAN, Boolean.class), + entry(JavaType.BYTE_STRING, ByteString.class), + entry(JavaType.DOUBLE, Double.class), + entry(JavaType.ENUM, EnumValueDescriptor.class), + entry(JavaType.FLOAT, Float.class), + entry(JavaType.INT, Integer.class), + entry(JavaType.LONG, Long.class), + entry(JavaType.MESSAGE, Message.class), entry(JavaType.STRING, String.class)) - .collect(collectingAndThen(toMap(Entry::getKey, e -> asAnnotatedType(e.getValue())), - map -> unmodifiableMap(new EnumMap<>(map)))); + .collect( + collectingAndThen( + toMap(Entry::getKey, e -> asAnnotatedType(e.getValue())), + map -> unmodifiableMap(new EnumMap<>(map)))); private TypeLibrary() {} @@ -97,14 +100,16 @@ static InPlaceMutator withoutInitIfRecursive( } private static boolean isRecursiveField(FieldDescriptor field) { - return containedInDirectedCycle(field, f -> { - // For map fields, only the value can be a message. - FieldDescriptor realField = f.isMapField() ? f.getMessageType().getFields().get(1) : f; - if (realField.getJavaType() != JavaType.MESSAGE) { - return Stream.empty(); - } - return realField.getMessageType().getFields().stream(); - }); + return containedInDirectedCycle( + field, + f -> { + // For map fields, only the value can be a message. + FieldDescriptor realField = f.isMapField() ? f.getMessageType().getFields().get(1) : f; + if (realField.getJavaType() != JavaType.MESSAGE) { + return Stream.empty(); + } + return realField.getMessageType().getFields().stream(); + }); } static Message getDefaultInstance(Class messageClass) { @@ -114,7 +119,8 @@ static Message getDefaultInstance(Class messageClass) { check(Modifier.isStatic(getDefaultInstance.getModifiers())); } catch (NoSuchMethodException e) { throw new IllegalStateException( - format("Message class for builder type %s does not have a getDefaultInstance method", + format( + "Message class for builder type %s does not have a getDefaultInstance method", messageClass.getName()), e); } @@ -130,7 +136,8 @@ static Message getDefaultInstance(WithDefaultInstance withDefaultInstance) { String[] parts = withDefaultInstance.value().split("#"); if (parts.length != 2) { throw new IllegalArgumentException( - format("Expected @WithDefaultInstance(\"%s\") to specify a fully-qualified method name" + format( + "Expected @WithDefaultInstance(\"%s\") to specify a fully-qualified method name" + " (e.g. com.example.MyClass#getDefaultInstance)", withDefaultInstance.value())); } @@ -140,8 +147,9 @@ static Message getDefaultInstance(WithDefaultInstance withDefaultInstance) { clazz = Class.forName(parts[0]); } catch (ClassNotFoundException e) { throw new IllegalArgumentException( - format("Failed to find class '%s' specified by @WithDefaultInstance(\"%s\")", parts[0], - withDefaultInstance.value()), + format( + "Failed to find class '%s' specified by @WithDefaultInstance(\"%s\")", + parts[0], withDefaultInstance.value()), e); } @@ -151,27 +159,33 @@ static Message getDefaultInstance(WithDefaultInstance withDefaultInstance) { method.setAccessible(true); } catch (NoSuchMethodException e) { throw new IllegalArgumentException( - format("Failed to find method specified by @WithDefaultInstance(\"%s\")", + format( + "Failed to find method specified by @WithDefaultInstance(\"%s\")", withDefaultInstance.value()), e); } if (!Modifier.isStatic(method.getModifiers())) { throw new IllegalArgumentException( - format("Expected method specified by @WithDefaultInstance(\"%s\") to be static", + format( + "Expected method specified by @WithDefaultInstance(\"%s\") to be static", withDefaultInstance.value())); } if (!Message.class.isAssignableFrom(method.getReturnType())) { - throw new IllegalArgumentException(format( - "Expected return type of method specified by @WithDefaultInstance(\"%s\") to be a" - + " subtype of %s, got %s", - withDefaultInstance.value(), Message.class.getName(), method.getReturnType().getName())); + throw new IllegalArgumentException( + format( + "Expected return type of method specified by @WithDefaultInstance(\"%s\") to be a" + + " subtype of %s, got %s", + withDefaultInstance.value(), + Message.class.getName(), + method.getReturnType().getName())); } try { return (Message) method.invoke(null); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalArgumentException( - format("Failed to execute method specified by @WithDefaultInstance(\"%s\")", + format( + "Failed to execute method specified by @WithDefaultInstance(\"%s\")", withDefaultInstance.value()), e); } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/support/ExceptionSupport.java b/src/main/java/com/code_intelligence/jazzer/mutation/support/ExceptionSupport.java index bd5f434ba..907edddeb 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/support/ExceptionSupport.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/support/ExceptionSupport.java @@ -24,7 +24,7 @@ public final class ExceptionSupport { */ @SuppressWarnings("unchecked") public static T asUnchecked(Throwable t) throws T { - throw(T) t; + throw (T) t; } private ExceptionSupport() {} diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java b/src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java index c643fea20..e0902428a 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/support/InputStreamSupport.java @@ -33,7 +33,7 @@ public static byte[] readAllBytes(InputStream stream) throws IOException { requireNonNull(stream); Queue buffers = new ArrayDeque<>(); int arrayLength = 0; - outer: + outer: while (true) { byte[] buffer = new byte[max(8192, stream.available())]; buffers.add(buffer); @@ -194,9 +194,9 @@ public void close() throws IOException { } /** - * Wraps a given stream with the functionality to detect if it was read exactly. - * To do so, the stream must provide an accurate implementation of {@link - * InputStream#available()}, hence it's restricted to {@link ByteArrayInputStream} for now. + * Wraps a given stream with the functionality to detect if it was read exactly. To do so, the + * stream must provide an accurate implementation of {@link InputStream#available()}, hence it's + * restricted to {@link ByteArrayInputStream} for now. * * @return {@code stream} extended that detects if it was consumed exactly */ diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/support/ParameterHolder.java b/src/main/java/com/code_intelligence/jazzer/mutation/support/ParameterHolder.java index 316a6dfd0..bacdc7ef1 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/support/ParameterHolder.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/support/ParameterHolder.java @@ -50,13 +50,16 @@ public Annotation[] parameterAnnotations() { } private Method getMethod() { - List foos = Arrays.stream(this.getClass().getDeclaredMethods()) - .filter(method -> method.getName().equals("foo")) - .collect(toList()); - require(foos.size() == 1, + List foos = + Arrays.stream(this.getClass().getDeclaredMethods()) + .filter(method -> method.getName().equals("foo")) + .collect(toList()); + require( + foos.size() == 1, this.getClass().getName() + " must define exactly one function named 'foo'"); Method foo = foos.get(0); - require(foo.getParameterCount() == 1, + require( + foo.getParameterCount() == 1, this.getClass().getName() + "#foo must define exactly one parameter"); return foo; } diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/support/RandomSupport.java b/src/main/java/com/code_intelligence/jazzer/mutation/support/RandomSupport.java index 5cfa765e4..03879749a 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/support/RandomSupport.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/support/RandomSupport.java @@ -21,21 +21,19 @@ public final class RandomSupport { private RandomSupport() {} - /** - * Polyfill for {@link SplittableRandom#nextBytes(byte[])}, which is not available in Java 8. - */ + /** Polyfill for {@link SplittableRandom#nextBytes(byte[])}, which is not available in Java 8. */ public static void nextBytes(SplittableRandom random, byte[] bytes) { // Taken from the implementation contract // https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/random/RandomGenerator.html#nextBytes(byte%5B%5D) // for interoperability with the RandomGenerator interface available as of Java 17. int i = 0; int len = bytes.length; - for (int words = len >> 3; words-- > 0;) { + for (int words = len >> 3; words-- > 0; ) { long rnd = random.nextLong(); for (int n = 8; n-- > 0; rnd >>>= Byte.SIZE) bytes[i++] = (byte) rnd; } if (i < len) - for (long rnd = random.nextLong(); i>>= Byte.SIZE) bytes[i++] = (byte) rnd; + for (long rnd = random.nextLong(); i < len; rnd >>>= Byte.SIZE) bytes[i++] = (byte) rnd; } /** diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/support/StreamSupport.java b/src/main/java/com/code_intelligence/jazzer/mutation/support/StreamSupport.java index b61980c4f..2d3fd2475 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/support/StreamSupport.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/support/StreamSupport.java @@ -45,8 +45,8 @@ public static Optional findFirstPresent(Stream> stream) { } /** - * @return an array with the values if all {@link Optional}s are present, otherwise - * {@link Optional#empty()} + * @return an array with the values if all {@link Optional}s are present, otherwise {@link + * Optional#empty()} */ public static Optional toArrayOrEmpty( Stream> stream, IntFunction newArray) { diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/support/TypeSupport.java b/src/main/java/com/code_intelligence/jazzer/mutation/support/TypeSupport.java index 8ce3aefca..9971153fd 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/support/TypeSupport.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/support/TypeSupport.java @@ -54,7 +54,7 @@ public static boolean isPrimitive(AnnotatedType type) { } public static boolean isPrimitive(Type type) { - if (!(type instanceof Class) ) { + if (!(type instanceof Class)) { return false; } return ((Class) type).isPrimitive(); @@ -65,15 +65,14 @@ public static boolean isInheritable(Annotation annotation) { } /** - * Returns {@code type} as a {@code Class} if it is a subclass of T, otherwise - * empty. + * Returns {@code type} as a {@code Class} if it is a subclass of T, otherwise empty. * *

            This function also returns an empty {@link Optional} for more complex (e.g. parameterized) * types. */ public static Optional> asSubclassOrEmpty( AnnotatedType type, Class superclass) { - if (!(type.getType() instanceof Class) ) { + if (!(type.getType() instanceof Class)) { return Optional.empty(); } @@ -107,7 +106,7 @@ public Annotation[] getAnnotations() { } return stream(clazz.getSuperclass().getAnnotations()) .filter(TypeSupport::isInheritable) - .toArray(Annotation[] ::new); + .toArray(Annotation[]::new); } @Override @@ -192,8 +191,8 @@ public static AnnotatedType notNull(AnnotatedType type) { /** * Constructs an anonymous WithLength class that can be applied as an annotation to {@code type} - * with the given - * {@code min} and {@code max} values. + * with the given {@code min} and {@code max} values. + * * @param type * @param min * @param max @@ -245,51 +244,58 @@ public static AnnotatedParameterizedType withTypeArguments( requireNonNull(type); requireNonNullElements(typeArguments); require(typeArguments.length > 0); - require(!(type instanceof AnnotatedParameterizedType || type instanceof AnnotatedTypeVariable - || type instanceof AnnotatedWildcardType || type instanceof AnnotatedArrayType), + require( + !(type instanceof AnnotatedParameterizedType + || type instanceof AnnotatedTypeVariable + || type instanceof AnnotatedWildcardType + || type instanceof AnnotatedArrayType), "only plain annotated types are supported"); require( ((Class) type.getType()).getEnclosingClass() == null, "nested classes aren't supported"); - ParameterizedType filledRawType = new ParameterizedType() { - @Override - public Type[] getActualTypeArguments() { - return stream(typeArguments).map(AnnotatedType::getType).toArray(Type[] ::new); - } - - @Override - public Type getRawType() { - return type.getType(); - } - - @Override - public Type getOwnerType() { - // We require the class is top-level. - return null; - } - - @Override - public String toString() { - return getRawType() - + stream(getActualTypeArguments()).map(Type::toString).collect(joining(",", "<", ">")); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ParameterizedType)) { - return false; - } - ParameterizedType other = (ParameterizedType) obj; - return getRawType().equals(other.getRawType()) && null == other.getOwnerType() - && Arrays.equals(getActualTypeArguments(), other.getActualTypeArguments()); - } - - @Override - public int hashCode() { - throw new UnsupportedOperationException( - "hashCode() is not supported as its behavior isn't specified"); - } - }; + ParameterizedType filledRawType = + new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return stream(typeArguments).map(AnnotatedType::getType).toArray(Type[]::new); + } + + @Override + public Type getRawType() { + return type.getType(); + } + + @Override + public Type getOwnerType() { + // We require the class is top-level. + return null; + } + + @Override + public String toString() { + return getRawType() + + stream(getActualTypeArguments()) + .map(Type::toString) + .collect(joining(",", "<", ">")); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ParameterizedType)) { + return false; + } + ParameterizedType other = (ParameterizedType) obj; + return getRawType().equals(other.getRawType()) + && null == other.getOwnerType() + && Arrays.equals(getActualTypeArguments(), other.getActualTypeArguments()); + } + + @Override + public int hashCode() { + throw new UnsupportedOperationException( + "hashCode() is not supported as its behavior isn't specified"); + } + }; return new AnnotatedParameterizedType() { @Override @@ -359,7 +365,8 @@ public static AnnotatedType withExtraAnnotations( return base; } - require(!(base instanceof AnnotatedTypeVariable || base instanceof AnnotatedWildcardType), + require( + !(base instanceof AnnotatedTypeVariable || base instanceof AnnotatedWildcardType), "Adding annotations to AnnotatedTypeVariables or AnnotatedWildcardTypes is not supported"); if (base instanceof AnnotatedArrayType) { return new AugmentedArrayType((AnnotatedArrayType) base, extraAnnotations); @@ -391,7 +398,7 @@ private static T annotatedElementGetAnnotation( } public static Optional> findFirstParentIfClass(AnnotatedType type, Class... parents) { - if (!(type.getType() instanceof Class) ) { + if (!(type.getType() instanceof Class)) { return Optional.empty(); } Class clazz = (Class) type.getType(); @@ -400,19 +407,21 @@ public static Optional> findFirstParentIfClass(AnnotatedType type, Clas public static Optional parameterTypeIfParameterized( AnnotatedType type, Class expectedParent) { - return parameterTypesIfParameterized(type, expectedParent).flatMap(typeArguments -> { - if (typeArguments.size() != 1) { - return Optional.empty(); - } else { - AnnotatedType elementType = typeArguments.get(0); - if (!(elementType.getType() instanceof ParameterizedType) - && !(elementType.getType() instanceof Class)) { - return Optional.empty(); - } else { - return Optional.of(elementType); - } - } - }); + return parameterTypesIfParameterized(type, expectedParent) + .flatMap( + typeArguments -> { + if (typeArguments.size() != 1) { + return Optional.empty(); + } else { + AnnotatedType elementType = typeArguments.get(0); + if (!(elementType.getType() instanceof ParameterizedType) + && !(elementType.getType() instanceof Class)) { + return Optional.empty(); + } else { + return Optional.of(elementType); + } + } + }); } public static Optional> parameterTypesIfParameterized( @@ -452,8 +461,8 @@ public static boolean containedInDirectedCycle(T root, Function return false; } - private static class AugmentedArrayType - extends AugmentedAnnotatedType implements AnnotatedArrayType { + private static class AugmentedArrayType extends AugmentedAnnotatedType + implements AnnotatedArrayType { private AugmentedArrayType(AnnotatedArrayType base, Annotation[] extraAnnotations) { super(base, extraAnnotations); } @@ -470,8 +479,8 @@ public AnnotatedType getAnnotatedOwnerType() { } } - private static class AugmentedParameterizedType - extends AugmentedAnnotatedType implements AnnotatedParameterizedType { + private static class AugmentedParameterizedType extends AugmentedAnnotatedType + implements AnnotatedParameterizedType { private AugmentedParameterizedType( AnnotatedParameterizedType base, Annotation[] extraAnnotations) { super(base, extraAnnotations); @@ -526,8 +535,7 @@ public T getAnnotation(Class annotationClass) { public Annotation[] getAnnotations() { Set> directlyPresentTypes = stream(getDeclaredAnnotations()).map(Annotation::annotationType).collect(toSet()); - return Stream - .concat( + return Stream.concat( // Directly present annotations. stream(getDeclaredAnnotations()), // Present but not directly present annotations, never added by us as we don't add @@ -535,13 +543,13 @@ public Annotation[] getAnnotations() { stream(base.getAnnotations()) .filter( annotation -> !directlyPresentTypes.contains(annotation.annotationType()))) - .toArray(Annotation[] ::new); + .toArray(Annotation[]::new); } @Override public Annotation[] getDeclaredAnnotations() { return Stream.concat(stream(base.getDeclaredAnnotations()), stream(extraAnnotations)) - .toArray(Annotation[] ::new); + .toArray(Annotation[]::new); } @Override diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/support/WeakIdentityHashMap.java b/src/main/java/com/code_intelligence/jazzer/mutation/support/WeakIdentityHashMap.java index 0eef7c247..4e56aa4ac 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/support/WeakIdentityHashMap.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/support/WeakIdentityHashMap.java @@ -29,8 +29,8 @@ import java.util.Set; /** - * An unoptimized version of a {@link java.util.WeakHashMap} with the semantics of a - * {@link java.util.IdentityHashMap}. + * An unoptimized version of a {@link java.util.WeakHashMap} with the semantics of a {@link + * java.util.IdentityHashMap}. * *

            If this class ever becomes a bottleneck, e.g. because of the IdentityWeakReference * allocations, it should be replaced by a copy of the * {@link java.util.WeakHashMap} code with all @@ -111,18 +111,19 @@ public Collection values() { @Override public Set> entrySet() { removeNewWeaklyReachables(); - return map.entrySet() - .stream() + return map.entrySet().stream() .map(e -> new SimpleEntry<>(e.getKey().get(), e.getValue())) .filter(e -> e.getKey() != null) .collect(toSet()); } void collectKeysForTesting() { - map.keySet().forEach(ref -> { - ref.clear(); - ref.enqueue(); - }); + map.keySet() + .forEach( + ref -> { + ref.clear(); + ref.enqueue(); + }); } private void removeNewWeaklyReachables() { diff --git a/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java b/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java index dc76328e5..8a3fdbfc4 100644 --- a/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java +++ b/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java @@ -101,7 +101,8 @@ public static void executeFuzzTarget(Class fuzzTarget, byte[] input) { } catch (Exception e) { handleInvokeException(e, fuzzTarget); } - System.err.printf("%s must define exactly one of the following two functions:%n" + 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" @@ -111,8 +112,7 @@ public static void executeFuzzTarget(Class fuzzTarget, byte[] input) { } private static void handleInvokeException(Exception e, Class fuzzTarget) { - if (e instanceof NoSuchMethodException) - return; + if (e instanceof NoSuchMethodException) return; if (e instanceof InvocationTargetException) { filterOutOwnStackTraceElements(e.getCause(), fuzzTarget); e.getCause().printStackTrace(); @@ -124,14 +124,11 @@ private static void handleInvokeException(Exception e, Class fuzzTarget) { } 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; + 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; + || !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()) diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java b/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java index a945a30a8..c9ec8cdb7 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java @@ -30,16 +30,17 @@ * Represents the Java view on a libFuzzer 8 bit counter coverage map. By using a direct ByteBuffer, * the counters are shared directly with native code. */ -final public class CoverageMap { +public final class CoverageMap { static { RulesJni.loadLibrary("jazzer_driver", "/com/code_intelligence/jazzer/driver"); } private static final String ENV_MAX_NUM_COUNTERS = "JAZZER_MAX_NUM_COUNTERS"; - private static final int MAX_NUM_COUNTERS = System.getenv(ENV_MAX_NUM_COUNTERS) != null - ? Integer.parseInt(System.getenv(ENV_MAX_NUM_COUNTERS)) - : 1 << 20; + private static final int MAX_NUM_COUNTERS = + System.getenv(ENV_MAX_NUM_COUNTERS) != null + ? Integer.parseInt(System.getenv(ENV_MAX_NUM_COUNTERS)) + : 1 << 20; private static final Unsafe UNSAFE = UnsafeProvider.getUnsafe(); private static final Class LOG; @@ -48,12 +49,16 @@ final public class CoverageMap { static { try { - LOG = Class.forName( - "com.code_intelligence.jazzer.utils.Log", false, ClassLoader.getSystemClassLoader()); - LOG_INFO = MethodHandles.lookup().findStatic( - LOG, "info", MethodType.methodType(void.class, String.class)); - LOG_ERROR = MethodHandles.lookup().findStatic( - LOG, "error", MethodType.methodType(void.class, String.class, Throwable.class)); + LOG = + Class.forName( + "com.code_intelligence.jazzer.utils.Log", false, ClassLoader.getSystemClassLoader()); + LOG_INFO = + MethodHandles.lookup() + .findStatic(LOG, "info", MethodType.methodType(void.class, String.class)); + LOG_ERROR = + MethodHandles.lookup() + .findStatic( + LOG, "error", MethodType.methodType(void.class, String.class, Throwable.class)); } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e); } @@ -94,9 +99,10 @@ public static void enlargeIfNeeded(int nextId) { if (newNumCounters > MAX_NUM_COUNTERS) { logError( String.format( - "Maximum number (%s) of coverage counters exceeded. Try to limit the scope of a single fuzz target as " - + "much as possible to keep the fuzzer fast. If that is not possible, the maximum number of " - + "counters can be increased via the %s environment variable.", + "Maximum number (%s) of coverage counters exceeded. Try to limit the scope of a" + + " single fuzz target as much as possible to keep the fuzzer fast. If that is" + + " not possible, the maximum number of counters can be increased via the %s" + + " environment variable.", MAX_NUM_COUNTERS, ENV_MAX_NUM_COUNTERS), null); System.exit(1); diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchError.java b/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchError.java index cf1360510..1c447a741 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchError.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchError.java @@ -17,9 +17,7 @@ import java.io.PrintStream; import java.io.PrintWriter; -/** - * An Error that rethrows itself when any of its getters is invoked. - */ +/** An Error that rethrows itself when any of its getters is invoked. */ public class HardToCatchError extends Error { public HardToCatchError() { super(); diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java b/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java index 3b3685310..e0ad72c09 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java @@ -16,8 +16,9 @@ import java.util.ArrayList; -final public class JazzerInternal { +public final class JazzerInternal { public static Throwable lastFinding; + // The value is only relevant when regression testing. Read by the bytecode emitted by // HookMethodVisitor to enable hooks only when invoked from a @FuzzTest. // @@ -26,7 +27,8 @@ final public class JazzerInternal { // parallel with regular unit tests, but it is next to impossible to determine which thread is // currently doing work for a fuzz test versus a regular unit test. Instead, @FuzzTest is // annotated with @Isolated. - @SuppressWarnings("unused") public static boolean hooksEnabled = true; + @SuppressWarnings("unused") + public static boolean hooksEnabled = true; private static final ArrayList onFuzzTargetReadyCallbacks = new ArrayList<>(); diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/Mutator.java b/src/main/java/com/code_intelligence/jazzer/runtime/Mutator.java index 6b03eaebd..66a8b0106 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/Mutator.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/Mutator.java @@ -24,7 +24,7 @@ public final class Mutator { @SuppressWarnings("ConstantValue") public static final boolean SHOULD_MOCK = Boolean.parseBoolean(System.getenv("JAZZER_MOCK_LIBFUZZER_MUTATOR")) - || Mutator.class.getName().startsWith("com.code_intelligence.selffuzz."); + || Mutator.class.getName().startsWith("com.code_intelligence.selffuzz."); static { if (!SHOULD_MOCK) { diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/NativeLibHooks.java b/src/main/java/com/code_intelligence/jazzer/runtime/NativeLibHooks.java index 8572f05ab..01e4587c2 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/NativeLibHooks.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/NativeLibHooks.java @@ -19,17 +19,29 @@ import java.lang.invoke.MethodHandle; @SuppressWarnings("unused") -final public class NativeLibHooks { - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Runtime", - targetMethod = "loadLibrary", targetMethodDescriptor = "(Ljava/lang/String;)V") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.System", - targetMethod = "loadLibrary", targetMethodDescriptor = "(Ljava/lang/String;)V") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Runtime", targetMethod = "load", +public final class NativeLibHooks { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Runtime", + targetMethod = "loadLibrary", targetMethodDescriptor = "(Ljava/lang/String;)V") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.System", targetMethod = "load", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.System", + targetMethod = "loadLibrary", targetMethodDescriptor = "(Ljava/lang/String;)V") - public static void - loadLibraryHook(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Runtime", + targetMethod = "load", + targetMethodDescriptor = "(Ljava/lang/String;)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.System", + targetMethod = "load", + targetMethodDescriptor = "(Ljava/lang/String;)V") + public static void loadLibraryHook( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { if (Constants.IS_ANDROID) { return; } diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java b/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java index bceeeaa07..ab5a36be8 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java @@ -20,58 +20,106 @@ import java.util.*; @SuppressWarnings("unused") -final public class TraceCmpHooks { - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Byte", targetMethod = "compare", +public final class TraceCmpHooks { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Byte", + targetMethod = "compare", + targetMethodDescriptor = "(BB)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Byte", + targetMethod = "compareUnsigned", targetMethodDescriptor = "(BB)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Byte", - targetMethod = "compareUnsigned", targetMethodDescriptor = "(BB)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Short", targetMethod = "compare", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Short", + targetMethod = "compare", + targetMethodDescriptor = "(SS)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Short", + targetMethod = "compareUnsigned", targetMethodDescriptor = "(SS)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Short", - targetMethod = "compareUnsigned", targetMethodDescriptor = "(SS)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer", - targetMethod = "compare", targetMethodDescriptor = "(II)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer", - targetMethod = "compareUnsigned", targetMethodDescriptor = "(II)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "kotlin.jvm.internal.Intrinsics ", - targetMethod = "compare", targetMethodDescriptor = "(II)I") - public static void - integerCompare(MethodHandle method, Object alwaysNull, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Integer", + targetMethod = "compare", + targetMethodDescriptor = "(II)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Integer", + targetMethod = "compareUnsigned", + targetMethodDescriptor = "(II)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "kotlin.jvm.internal.Intrinsics ", + targetMethod = "compare", + targetMethodDescriptor = "(II)I") + public static void integerCompare( + MethodHandle method, Object alwaysNull, Object[] arguments, int hookId) { TraceDataFlowNativeCallbacks.traceCmpInt( ((Number) arguments[0]).intValue(), ((Number) arguments[1]).intValue(), hookId); } - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Byte", - targetMethod = "compareTo", targetMethodDescriptor = "(Ljava/lang/Byte;)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Short", - targetMethod = "compareTo", targetMethodDescriptor = "(Ljava/lang/Short;)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer", - targetMethod = "compareTo", targetMethodDescriptor = "(Ljava/lang/Integer;)I") - public static void - integerCompareTo(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Byte", + targetMethod = "compareTo", + targetMethodDescriptor = "(Ljava/lang/Byte;)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Short", + targetMethod = "compareTo", + targetMethodDescriptor = "(Ljava/lang/Short;)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Integer", + targetMethod = "compareTo", + targetMethodDescriptor = "(Ljava/lang/Integer;)I") + public static void integerCompareTo( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { TraceDataFlowNativeCallbacks.traceCmpInt( ((Number) thisObject).intValue(), ((Number) arguments[0]).intValue(), hookId); } - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long", targetMethod = "compare", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Long", + targetMethod = "compare", + targetMethodDescriptor = "(JJ)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Long", + targetMethod = "compareUnsigned", targetMethodDescriptor = "(JJ)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long", - targetMethod = "compareUnsigned", targetMethodDescriptor = "(JJ)I") - public static void - longCompare(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static void longCompare( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { TraceDataFlowNativeCallbacks.traceCmpLong((long) arguments[0], (long) arguments[1], hookId); } - @MethodHook(type = HookType.BEFORE, targetClassName = "clojure.lang.Numbers", targetMethod = "lt", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "clojure.lang.Numbers", + targetMethod = "lt", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)Z") - @MethodHook(type = HookType.BEFORE, targetClassName = "clojure.lang.Numbers", targetMethod = "gt", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "clojure.lang.Numbers", + targetMethod = "gt", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)Z") - @MethodHook(type = HookType.BEFORE, targetClassName = "clojure.lang.Numbers", - targetMethod = "lte", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)Z") - @MethodHook(type = HookType.BEFORE, targetClassName = "clojure.lang.Numbers", - targetMethod = "gte", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)Z") - public static void - numberCompare(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "clojure.lang.Numbers", + targetMethod = "lte", + targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)Z") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "clojure.lang.Numbers", + targetMethod = "gte", + targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)Z") + public static void numberCompare( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { // Clojure unconditionally casts the arguments to Number. // https://github.com/clojure/clojure/blob/2a058814e5fa3e8fb630ae507c3fa7dc865138c6/src/jvm/clojure/lang/Numbers.java#L253 Number arg1 = (Number) arguments[0]; @@ -79,37 +127,54 @@ final public class TraceCmpHooks { TraceDataFlowNativeCallbacks.traceCmpLong(arg1.longValue(), arg2.longValue(), hookId); } - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long", - targetMethod = "compareTo", targetMethodDescriptor = "(Ljava/lang/Long;)I") - public static void - longCompareTo(MethodHandle method, Long thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Long", + targetMethod = "compareTo", + targetMethodDescriptor = "(Ljava/lang/Long;)I") + public static void longCompareTo( + MethodHandle method, Long thisObject, Object[] arguments, int hookId) { TraceDataFlowNativeCallbacks.traceCmpLong(thisObject, (long) arguments[0], hookId); } - @MethodHook(type = HookType.BEFORE, targetClassName = "clojure.lang.Numbers", - targetMethod = "isZero", targetMethodDescriptor = "(Ljava/lang/Number;)Z") - @MethodHook(type = HookType.BEFORE, targetClassName = "clojure.lang.Numbers", - targetMethod = "isPos", targetMethodDescriptor = "(Ljava/lang/Number;)Z") - @MethodHook(type = HookType.BEFORE, targetClassName = "clojure.lang.Numbers", - targetMethod = "isNeg", targetMethodDescriptor = "(Ljava/lang/Number;)Z") - public static void - staticNumberCompareZero(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "clojure.lang.Numbers", + targetMethod = "isZero", + targetMethodDescriptor = "(Ljava/lang/Number;)Z") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "clojure.lang.Numbers", + targetMethod = "isPos", + targetMethodDescriptor = "(Ljava/lang/Number;)Z") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "clojure.lang.Numbers", + targetMethod = "isNeg", + targetMethodDescriptor = "(Ljava/lang/Number;)Z") + public static void staticNumberCompareZero( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { Number arg = (Number) arguments[0]; TraceDataFlowNativeCallbacks.traceCmpLong(arg.longValue(), 0, hookId); } - @MethodHook(type = HookType.BEFORE, targetClassName = "kotlin.jvm.internal.Intrinsics ", - targetMethod = "compare", targetMethodDescriptor = "(JJ)I") - public static void - longCompareKt(MethodHandle method, Object alwaysNull, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "kotlin.jvm.internal.Intrinsics ", + targetMethod = "compare", + targetMethodDescriptor = "(JJ)I") + public static void longCompareKt( + MethodHandle method, Object alwaysNull, Object[] arguments, int hookId) { TraceDataFlowNativeCallbacks.traceCmpLong((long) arguments[0], (long) arguments[1], hookId); } @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "equals") - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.String", targetMethod = "equalsIgnoreCase") - public static void - equals(MethodHandle method, String thisObject, Object[] arguments, int hookId, Boolean areEqual) { + public static void equals( + MethodHandle method, String thisObject, Object[] arguments, int hookId, Boolean areEqual) { if (!areEqual && arguments.length == 1 && arguments[0] instanceof String) { // The precise value of the result of the comparison is not used by libFuzzer as long as it is // non-zero. @@ -119,12 +184,15 @@ final public class TraceCmpHooks { @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.Object", targetMethod = "equals") @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.CharSequence", targetMethod = "equals") + type = HookType.AFTER, + targetClassName = "java.lang.CharSequence", + targetMethod = "equals") @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.Number", targetMethod = "equals") - public static void - genericEquals( + public static void genericEquals( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean areEqual) { - if (!areEqual && arguments.length == 1 && arguments[0] != null + if (!areEqual + && arguments.length == 1 + && arguments[0] != null && thisObject.getClass() == arguments[0].getClass()) { TraceDataFlowNativeCallbacks.traceGenericCmp(thisObject, arguments[0], hookId); } @@ -133,29 +201,40 @@ final public class TraceCmpHooks { @MethodHook(type = HookType.AFTER, targetClassName = "clojure.lang.Util", targetMethod = "equiv") public static void genericStaticEquals( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean areEqual) { - if (!areEqual && arguments.length == 2 && arguments[0] != null && arguments[1] != null + if (!areEqual + && arguments.length == 2 + && arguments[0] != null + && arguments[1] != null && arguments[1].getClass() == arguments[0].getClass()) { TraceDataFlowNativeCallbacks.traceGenericCmp(arguments[0], arguments[1], hookId); } } - @MethodHook(type = HookType.AFTER, targetClassName = "clojure.lang.Util", - targetMethod = "compare", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)I") - public static void - genericStaticCompareTo( + @MethodHook( + type = HookType.AFTER, + targetClassName = "clojure.lang.Util", + targetMethod = "compare", + targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)I") + public static void genericStaticCompareTo( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Integer result) { - if (result != 0 && arguments.length == 2 && arguments[0] != null && arguments[1] != null + if (result != 0 + && arguments.length == 2 + && arguments[0] != null + && arguments[1] != null && arguments[1].getClass() == arguments[0].getClass()) { TraceDataFlowNativeCallbacks.traceGenericCmp(arguments[0], arguments[1], hookId); } } @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "compareTo") - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "compareTo") + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.String", targetMethod = "compareToIgnoreCase") - public static void - compareTo( + public static void compareTo( MethodHandle method, String thisObject, Object[] arguments, int hookId, Integer returnValue) { if (returnValue != 0 && arguments.length == 1 && arguments[0] instanceof String) { TraceDataFlowNativeCallbacks.traceStrcmp( @@ -164,9 +243,14 @@ public static void genericStaticEquals( } @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "contentEquals") - public static void - contentEquals(MethodHandle method, String thisObject, Object[] arguments, int hookId, + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "contentEquals") + public static void contentEquals( + MethodHandle method, + String thisObject, + Object[] arguments, + int hookId, Boolean areEqualContents) { if (!areEqualContents && arguments.length == 1 && arguments[0] instanceof CharSequence) { TraceDataFlowNativeCallbacks.traceStrcmp( @@ -174,10 +258,12 @@ public static void genericStaticEquals( } } - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", - targetMethod = "regionMatches", targetMethodDescriptor = "(ZILjava/lang/String;II)Z") - public static void - regionsMatches5( + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "regionMatches", + targetMethodDescriptor = "(ZILjava/lang/String;II)Z") + public static void regionsMatches5( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) { if (!returnValue) { int toffset = (int) arguments[1]; @@ -188,10 +274,12 @@ public static void genericStaticEquals( } } - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", - targetMethod = "regionMatches", targetMethodDescriptor = "(ILjava/lang/String;II)Z") - public static void - regionMatches4( + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "regionMatches", + targetMethodDescriptor = "(ILjava/lang/String;II)Z") + public static void regionMatches4( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) { if (!returnValue) { int toffset = (int) arguments[0]; @@ -204,8 +292,7 @@ public static void genericStaticEquals( private static void regionMatchesInternal( String thisString, int toffset, String other, int ooffset, int len, int hookId) { - if (toffset < 0 || ooffset < 0) - return; + if (toffset < 0 || ooffset < 0) return; int cappedThisStringEnd = Math.min(toffset + len, thisString.length()); int cappedOtherStringEnd = Math.min(ooffset + len, other.length()); String thisPart = thisString.substring(toffset, cappedThisStringEnd); @@ -214,9 +301,10 @@ private static void regionMatchesInternal( } @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "contains") - public static void - contains( + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "contains") + public static void contains( MethodHandle method, String thisObject, Object[] arguments, int hookId, Boolean doesContain) { if (!doesContain && arguments.length == 1 && arguments[0] instanceof CharSequence) { TraceDataFlowNativeCallbacks.traceStrstr( @@ -226,17 +314,26 @@ private static void regionMatchesInternal( @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "indexOf") @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "lastIndexOf") + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "lastIndexOf") + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.StringBuffer", + targetMethod = "indexOf") @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.StringBuffer", targetMethod = "indexOf") - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.StringBuffer", + type = HookType.AFTER, + targetClassName = "java.lang.StringBuffer", targetMethod = "lastIndexOf") @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.StringBuilder", targetMethod = "indexOf") - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.StringBuilder", + type = HookType.AFTER, + targetClassName = "java.lang.StringBuilder", + targetMethod = "indexOf") + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.StringBuilder", targetMethod = "lastIndexOf") - public static void - indexOf( + public static void indexOf( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Integer returnValue) { if (returnValue == -1 && arguments.length >= 1 && arguments[0] instanceof String) { TraceDataFlowNativeCallbacks.traceStrstr( @@ -245,22 +342,31 @@ private static void regionMatchesInternal( } @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "startsWith") + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "startsWith") @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "endsWith") - public static void - startsWith(MethodHandle method, String thisObject, Object[] arguments, int hookId, + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "endsWith") + public static void startsWith( + MethodHandle method, + String thisObject, + Object[] arguments, + int hookId, Boolean doesStartOrEndsWith) { if (!doesStartOrEndsWith && arguments.length >= 1 && arguments[0] instanceof String) { TraceDataFlowNativeCallbacks.traceStrstr(thisObject, (String) arguments[0], hookId); } } - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "replace", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "replace", targetMethodDescriptor = "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;") - public static void - replace( + public static void replace( MethodHandle method, Object thisObject, Object[] arguments, int hookId, String returnValue) { String original = (String) thisObject; // Report only if the replacement was not successful. @@ -272,30 +378,50 @@ private static void regionMatchesInternal( // For standard Kotlin packages, which are named according to the pattern kotlin.*, we append a // whitespace to the package name of the target class so that they are not mangled due to shading. - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.jvm.internal.Intrinsics ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.jvm.internal.Intrinsics ", targetMethod = "areEqual") @MethodHook( - type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", targetMethod = "equals") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", + targetMethod = "equals") + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "equals$default") - public static void - equalsKt(MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, + public static void equalsKt( + MethodHandle method, + Object alwaysNull, + Object[] arguments, + int hookId, Boolean equalStrings) { - if (!equalStrings && arguments.length >= 2 && arguments[0] instanceof String + if (!equalStrings + && arguments.length >= 2 + && arguments[0] instanceof String && arguments[1] instanceof String) { TraceDataFlowNativeCallbacks.traceStrcmp( (String) arguments[0], (String) arguments[1], 1, hookId); } } - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "contentEquals") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "contentEquals$default") - public static void - contentEqualKt(MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, + public static void contentEqualKt( + MethodHandle method, + Object alwaysNull, + Object[] arguments, + int hookId, Boolean equalStrings) { - if (!equalStrings && arguments.length >= 2 && arguments[0] instanceof CharSequence + if (!equalStrings + && arguments.length >= 2 + && arguments[0] instanceof CharSequence && arguments[1] instanceof CharSequence) { TraceDataFlowNativeCallbacks.traceStrcmp( arguments[0].toString(), arguments[1].toString(), 1, hookId); @@ -303,13 +429,18 @@ private static void regionMatchesInternal( } @MethodHook( - type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", targetMethod = "compareTo") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", + targetMethod = "compareTo") + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "compareTo$default") - public static void - compareToKt( + public static void compareToKt( MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, Integer returnValue) { - if (returnValue != 0 && arguments.length >= 2 && arguments[0] instanceof String + if (returnValue != 0 + && arguments.length >= 2 + && arguments[0] instanceof String && arguments[1] instanceof String) { TraceDataFlowNativeCallbacks.traceStrcmp( (String) arguments[0], (String) arguments[1], 1, hookId); @@ -317,17 +448,30 @@ private static void regionMatchesInternal( } @MethodHook( - type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", targetMethod = "endsWith") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", + targetMethod = "endsWith") + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "endsWith$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "startsWith") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "startsWith$default") - public static void - startsWithKt(MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, + public static void startsWithKt( + MethodHandle method, + Object alwaysNull, + Object[] arguments, + int hookId, Boolean doesStartOrEndsWith) { - if (!doesStartOrEndsWith && arguments.length >= 2 && arguments[0] instanceof CharSequence + if (!doesStartOrEndsWith + && arguments.length >= 2 + && arguments[0] instanceof CharSequence && arguments[1] instanceof CharSequence) { TraceDataFlowNativeCallbacks.traceStrstr( arguments[0].toString(), arguments[1].toString(), hookId); @@ -335,13 +479,18 @@ private static void regionMatchesInternal( } @MethodHook( - type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", targetMethod = "contains") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", + targetMethod = "contains") + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "contains$default") - public static void - containsKt( + public static void containsKt( MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, Boolean doesContain) { - if (!doesContain && arguments.length >= 2 && arguments[0] instanceof CharSequence + if (!doesContain + && arguments.length >= 2 + && arguments[0] instanceof CharSequence && arguments[1] instanceof CharSequence) { TraceDataFlowNativeCallbacks.traceStrstr( arguments[0].toString(), arguments[1].toString(), hookId); @@ -349,15 +498,22 @@ private static void regionMatchesInternal( } @MethodHook( - type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", targetMethod = "indexOf") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", + targetMethod = "indexOf") + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "indexOf$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "lastIndexOf") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "lastIndexOf$default") - public static void - indexOfKt( + public static void indexOfKt( MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, Integer returnValue) { if (returnValue != -1 || arguments.length < 2 || !(arguments[0] instanceof CharSequence)) { return; @@ -372,31 +528,54 @@ private static void regionMatchesInternal( } @MethodHook( - type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", targetMethod = "replace") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", + targetMethod = "replace") + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replace$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceAfter") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceAfter$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceAfterLast") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceAfterLast$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceBefore") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceBefore$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceBeforeLast") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceBeforeLast$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceFirst") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "replaceFirst$default") - public static void - replaceKt( + public static void replaceKt( MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, String returnValue) { if (arguments.length < 2 || !(arguments[0] instanceof String)) { return; @@ -412,14 +591,21 @@ private static void regionMatchesInternal( } } - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "regionMatches", targetMethodDescriptor = "(Ljava/lang/String;ILjava/lang/String;IIZ)Z") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "regionMatches$default", targetMethodDescriptor = "(Ljava/lang/String;ILjava/lang/String;IIZILjava/lang/Object;)Z") - public static void - regionMatchesKt(MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, + public static void regionMatchesKt( + MethodHandle method, + Object alwaysNull, + Object[] arguments, + int hookId, Boolean doesRegionMatch) { if (!doesRegionMatch) { String thisString = arguments[0].toString(); @@ -431,16 +617,23 @@ private static void regionMatchesInternal( } } - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "indexOfAny") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "indexOfAny$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "lastIndexOfAny") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "lastIndexOfAny$default") - public static void - indexOfAnyKt( + public static void indexOfAnyKt( MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, Integer returnValue) { if (returnValue == -1 && arguments.length >= 2 && arguments[0] instanceof CharSequence) { guideTowardContainmentOfFirstElement(arguments[0].toString(), arguments[1], hookId); @@ -448,15 +641,22 @@ private static void regionMatchesInternal( } @MethodHook( - type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", targetMethod = "findAnyOf") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", + targetMethod = "findAnyOf") + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "findAnyOf$default") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "findLastAnyOf") - @MethodHook(type = HookType.AFTER, targetClassName = "kotlin.text.StringsKt ", + @MethodHook( + type = HookType.AFTER, + targetClassName = "kotlin.text.StringsKt ", targetMethod = "findLastAnyOf$default") - public static void - findAnyKt( + public static void findAnyKt( MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, Object returnValue) { if (returnValue == null && arguments.length >= 2 && arguments[0] instanceof CharSequence) { guideTowardContainmentOfFirstElement(arguments[0].toString(), arguments[1], hookId); @@ -486,25 +686,27 @@ private static void guideTowardContainmentOfFirstElement( } } - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", targetMethod = "equals", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.Arrays", + targetMethod = "equals", targetMethodDescriptor = "([B[B)Z") - public static void - arraysEquals( + public static void arraysEquals( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) { - if (returnValue) - return; + if (returnValue) return; byte[] first = (byte[]) arguments[0]; byte[] second = (byte[]) arguments[1]; TraceDataFlowNativeCallbacks.traceMemcmp(first, second, 1, hookId); } - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", targetMethod = "equals", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.Arrays", + targetMethod = "equals", targetMethodDescriptor = "([BII[BII)Z") - public static void - arraysEqualsRange( + public static void arraysEqualsRange( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) { - if (returnValue) - return; + if (returnValue) return; byte[] first = Arrays.copyOfRange((byte[]) arguments[0], (int) arguments[1], (int) arguments[2]); byte[] second = @@ -512,29 +714,37 @@ private static void guideTowardContainmentOfFirstElement( TraceDataFlowNativeCallbacks.traceMemcmp(first, second, 1, hookId); } - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", targetMethod = "compare", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.Arrays", + targetMethod = "compare", targetMethodDescriptor = "([B[B)I") - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", - targetMethod = "compareUnsigned", targetMethodDescriptor = "([B[B)I") - public static void - arraysCompare( + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.Arrays", + targetMethod = "compareUnsigned", + targetMethodDescriptor = "([B[B)I") + public static void arraysCompare( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Integer returnValue) { - if (returnValue == 0) - return; + if (returnValue == 0) return; byte[] first = (byte[]) arguments[0]; byte[] second = (byte[]) arguments[1]; TraceDataFlowNativeCallbacks.traceMemcmp(first, second, returnValue, hookId); } - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", targetMethod = "compare", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.Arrays", + targetMethod = "compare", + targetMethodDescriptor = "([BII[BII)I") + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.Arrays", + targetMethod = "compareUnsigned", targetMethodDescriptor = "([BII[BII)I") - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", - targetMethod = "compareUnsigned", targetMethodDescriptor = "([BII[BII)I") - public static void - arraysCompareRange( + public static void arraysCompareRange( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Integer returnValue) { - if (returnValue == 0) - return; + if (returnValue == 0) return; byte[] first = Arrays.copyOfRange((byte[]) arguments[0], (int) arguments[1], (int) arguments[2]); byte[] second = @@ -550,19 +760,15 @@ private static void guideTowardContainmentOfFirstElement( @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Map", targetMethod = "get") public static void mapGet( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) { - if (returnValue != null) - return; + if (returnValue != null) return; if (arguments.length != 1) { return; } - if (thisObject == null) - return; + if (thisObject == null) return; final Map map = (Map) thisObject; - if (map.size() == 0) - return; + if (map.size() == 0) return; final Object currentKey = arguments[0]; - if (currentKey == null) - return; + if (currentKey == null) return; // Find two valid map keys that bracket currentKey. // This is a generalization of libFuzzer's __sanitizer_cov_trace_switch: // https://github.com/llvm/llvm-project/blob/318942de229beb3b2587df09e776a50327b5cef0/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L564 @@ -584,8 +790,7 @@ public static void mapGet( // Note: This is not deterministic if map.size() > MAX_NUM_KEYS_TO_ENUMERATE. int enumeratedKeys = 0; for (Object validKey : map.keySet()) { - if (!(validKey instanceof Comparable)) - continue; + if (!(validKey instanceof Comparable)) continue; final Comparable comparableValidKey = (Comparable) validKey; // If the key sorts lower than the non-existing key, but higher than the current lower // bound, update the lower bound and vice versa for the upper bound. @@ -602,8 +807,7 @@ public static void mapGet( // Can be thrown by floorKey and ceilingKey if currentKey is of a type that can't be // compared to the maps keys. } - if (enumeratedKeys++ > MAX_NUM_KEYS_TO_ENUMERATE) - break; + if (enumeratedKeys++ > MAX_NUM_KEYS_TO_ENUMERATE) break; } } } catch (ConcurrentModificationException ignored) { @@ -621,18 +825,24 @@ public static void mapGet( } } - @MethodHook(type = HookType.AFTER, targetClassName = "org.junit.jupiter.api.Assertions", + @MethodHook( + type = HookType.AFTER, + targetClassName = "org.junit.jupiter.api.Assertions", targetMethod = "assertNotEquals", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)V") - @MethodHook(type = HookType.AFTER, targetClassName = "org.junit.jupiter.api.Assertions", + @MethodHook( + type = HookType.AFTER, + targetClassName = "org.junit.jupiter.api.Assertions", targetMethod = "assertNotEquals", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V") - @MethodHook(type = HookType.AFTER, targetClassName = "org.junit.jupiter.api.Assertions", + @MethodHook( + type = HookType.AFTER, + targetClassName = "org.junit.jupiter.api.Assertions", targetMethod = "assertNotEquals", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/Supplier;)V") - public static void - assertEquals(MethodHandle method, Object node, Object[] args, int hookId, Object alwaysNull) { + public static void assertEquals( + MethodHandle method, Object node, Object[] args, int hookId, Object alwaysNull) { if (args[0] != null && args[1] != null && args[0].getClass() == args[1].getClass()) { TraceDataFlowNativeCallbacks.traceGenericCmp(args[0], args[1], hookId); } diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java b/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java index 777eb0a29..79b846143 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java @@ -23,7 +23,7 @@ import org.objectweb.asm.Type; @SuppressWarnings("unused") -final public class TraceDataFlowNativeCallbacks { +public final class TraceDataFlowNativeCallbacks { // Note that we are not encoding as modified UTF-8 here: The FuzzedDataProvider transparently // converts CESU8 into modified UTF-8 by coding null bytes on two bytes. Since the fuzzer is more // likely to insert literal null bytes, having both the fuzzer input and the reported string @@ -102,14 +102,21 @@ public static void traceGenericCmp(Object arg1, Object arg2, int pc) { /* trace-cmp */ public static native void traceCmpInt(int arg1, int arg2, int pc); + public static native void traceConstCmpInt(int arg1, int arg2, int pc); + public static native void traceCmpLong(long arg1, long arg2, int pc); + public static native void traceSwitch(long val, long[] cases, int pc); + /* trace-div */ public static native void traceDivInt(int val, int pc); + public static native void traceDivLong(long val, int pc); + /* trace-gep */ public static native void traceGep(long val, int pc); + /* indirect-calls */ public static native void tracePcIndir(int callee, int caller); diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/TraceDivHooks.java b/src/main/java/com/code_intelligence/jazzer/runtime/TraceDivHooks.java index f1b02fd51..b46839624 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/TraceDivHooks.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/TraceDivHooks.java @@ -19,24 +19,36 @@ import java.lang.invoke.MethodHandle; @SuppressWarnings("unused") -final public class TraceDivHooks { - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer", - targetMethod = "divideUnsigned", targetMethodDescriptor = "(II)I") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer", - targetMethod = "remainderUnsigned", targetMethodDescriptor = "(II)I") - public static void - intUnsignedDivide(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { +public final class TraceDivHooks { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Integer", + targetMethod = "divideUnsigned", + targetMethodDescriptor = "(II)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Integer", + targetMethod = "remainderUnsigned", + targetMethodDescriptor = "(II)I") + public static void intUnsignedDivide( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { // Since the arguments are to be treated as unsigned integers we need a long to fit the // divisor. TraceDataFlowNativeCallbacks.traceDivLong(Integer.toUnsignedLong((int) arguments[1]), hookId); } - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long", - targetMethod = "divideUnsigned", targetMethodDescriptor = "(JJ)J") - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long", - targetMethod = "remainderUnsigned", targetMethodDescriptor = "(JJ)J") - public static void - longUnsignedDivide(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Long", + targetMethod = "divideUnsigned", + targetMethodDescriptor = "(JJ)J") + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Long", + targetMethod = "remainderUnsigned", + targetMethodDescriptor = "(JJ)J") + public static void longUnsignedDivide( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { long divisor = (long) arguments[1]; // Run the callback only if the divisor, which is regarded as an unsigned long, fits in a // signed long, i.e., does not have the sign bit set. @@ -45,11 +57,13 @@ final public class TraceDivHooks { } } - @MethodHook(type = HookType.BEFORE, targetClassName = "clojure.lang.Numbers", + @MethodHook( + type = HookType.BEFORE, + targetClassName = "clojure.lang.Numbers", targetMethod = "divide", targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Number;") - public static void - numberUnsignedDivide(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static void numberUnsignedDivide( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { // Clojure unconditionally casts the argument to Number. // https://github.com/clojure/clojure/blob/2a058814e5fa3e8fb630ae507c3fa7dc865138c6/src/jvm/clojure/lang/Numbers.java#L189 long divisor = ((Number) arguments[1]).longValue(); diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/TraceIndirHooks.java b/src/main/java/com/code_intelligence/jazzer/runtime/TraceIndirHooks.java index 897ede6ce..f666b13ef 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/TraceIndirHooks.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/TraceIndirHooks.java @@ -20,15 +20,18 @@ import java.lang.reflect.Executable; @SuppressWarnings("unused") -final public class TraceIndirHooks { +public final class TraceIndirHooks { // The reflection hook is of type AFTER as it should only report calls that did not fail because // of incorrect arguments passed. @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.reflect.Method", targetMethod = "invoke") - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.reflect.Constructor", + type = HookType.AFTER, + targetClassName = "java.lang.reflect.Method", + targetMethod = "invoke") + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.reflect.Constructor", targetMethod = "newInstance") - public static void - methodInvoke( + public static void methodInvoke( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) { TraceDataFlowNativeCallbacks.traceReflectiveCall((Executable) thisObject, hookId); } diff --git a/src/main/java/com/code_intelligence/jazzer/utils/Log.java b/src/main/java/com/code_intelligence/jazzer/utils/Log.java index bccd3a32d..c5be3ada0 100644 --- a/src/main/java/com/code_intelligence/jazzer/utils/Log.java +++ b/src/main/java/com/code_intelligence/jazzer/utils/Log.java @@ -33,9 +33,7 @@ public class Log { private static PrintStream fixedOut; private static PrintStream fixedErr; - /** - * The {@link PrintStream}s to use for all output from this call on. - */ + /** The {@link PrintStream}s to use for all output from this call on. */ public static void fixOutErr(PrintStream out, PrintStream err) { if (out == null) { throw new IllegalArgumentException("out must not be null"); diff --git a/src/main/java/com/code_intelligence/jazzer/utils/UnsafeProvider.java b/src/main/java/com/code_intelligence/jazzer/utils/UnsafeProvider.java index e36e64c4f..99e21f329 100644 --- a/src/main/java/com/code_intelligence/jazzer/utils/UnsafeProvider.java +++ b/src/main/java/com/code_intelligence/jazzer/utils/UnsafeProvider.java @@ -47,10 +47,11 @@ private static Unsafe getUnsafeInternal() { } } } - throw new IllegalStateException(String.format( - "Please file a bug at https://github.com/CodeIntelligenceTesting/jazzer/issues/new with " - + "this information: Failed to find Unsafe member on Unsafe class, have: " - + Arrays.deepToString(Unsafe.class.getDeclaredFields()))); + throw new IllegalStateException( + String.format( + "Please file a bug at https://github.com/CodeIntelligenceTesting/jazzer/issues/new" + + " with this information: Failed to find Unsafe member on Unsafe class, have: " + + Arrays.deepToString(Unsafe.class.getDeclaredFields()))); } } } diff --git a/src/main/java/com/code_intelligence/jazzer/utils/UnsafeUtils.java b/src/main/java/com/code_intelligence/jazzer/utils/UnsafeUtils.java index 30c88dc92..395a89b3e 100644 --- a/src/main/java/com/code_intelligence/jazzer/utils/UnsafeUtils.java +++ b/src/main/java/com/code_intelligence/jazzer/utils/UnsafeUtils.java @@ -31,8 +31,8 @@ public final class UnsafeUtils { /** * Dynamically creates a concrete class implementing the given abstract class. * - *

            The returned class will not be functional and should only be used to construct instances - * via {@link sun.misc.Unsafe#allocateInstance(Class)}. + *

            The returned class will not be functional and should only be used to construct instances via + * {@link sun.misc.Unsafe#allocateInstance(Class)}. */ public static Class defineAnonymousConcreteSubclass(Class abstractClass) { if (!Modifier.isAbstract(abstractClass.getModifiers())) { @@ -59,15 +59,22 @@ public static Class defineAnonymousConcreteSubclass(Class ab // MethodHandles.Lookup#defineHiddenClass is available as of Java 15. // Unsafe#defineAnonymousClass has been removed in Java 17. if (defineHiddenClass.isPresent() && classOption.isPresent()) { - return ((MethodHandles.Lookup) defineHiddenClass.get().invoke(MethodHandles.lookup(), - cw.toByteArray(), true, Array.newInstance(classOption.get(), 0))) + return ((MethodHandles.Lookup) + defineHiddenClass + .get() + .invoke( + MethodHandles.lookup(), + cw.toByteArray(), + true, + Array.newInstance(classOption.get(), 0))) .lookupClass() .asSubclass(abstractClass); } else { - return (Class) UnsafeProvider.getUnsafe() - .getClass() - .getMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class) - .invoke(UnsafeProvider.getUnsafe(), UnsafeUtils.class, cw.toByteArray(), null); + return (Class) + UnsafeProvider.getUnsafe() + .getClass() + .getMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class) + .invoke(UnsafeProvider.getUnsafe(), UnsafeUtils.class, cw.toByteArray(), null); } } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new IllegalStateException(e); diff --git a/src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java b/src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java index 509e909a4..9c9f995de 100644 --- a/src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java +++ b/src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java @@ -16,30 +16,22 @@ package com.code_intelligence.jazzer.utils; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.IllegalArgumentException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -76,27 +68,31 @@ public static Set mergeZipToZip(String src, ZipOutputStream zos, Set mergeDirectoryToZip(String src, ZipOutputStream zos, - Set skipFiles) throws IllegalArgumentException, IOException { + public static Set mergeDirectoryToZip( + String src, ZipOutputStream zos, Set skipFiles) + throws IllegalArgumentException, IOException { HashSet filesAdded = new HashSet<>(); File sourceDir = new File(src); if (!sourceDir.isDirectory()) { throw new IllegalArgumentException("Argument src must be a directory. Path provided: " + src); } - Files.walkFileTree(sourceDir.toPath(), new SimpleFileVisitor() { - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String zipPath = sourceDir.toPath().relativize(file).toString(); - if (skipFiles.stream().anyMatch(zipPath::endsWith)) { - return FileVisitResult.CONTINUE; - } + Files.walkFileTree( + sourceDir.toPath(), + new SimpleFileVisitor() { + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + String zipPath = sourceDir.toPath().relativize(file).toString(); + if (skipFiles.stream().anyMatch(zipPath::endsWith)) { + return FileVisitResult.CONTINUE; + } - zos.putNextEntry(new ZipEntry(zipPath)); - Files.copy(file, zos); - filesAdded.add(zipPath); - return FileVisitResult.CONTINUE; - } - }); + zos.putNextEntry(new ZipEntry(zipPath)); + Files.copy(file, zos); + filesAdded.add(zipPath); + return FileVisitResult.CONTINUE; + } + }); return filesAdded; } @@ -104,7 +100,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO public static void extractFile(String srcZip, String targetFile, String outputFilePath) throws IOException { try (OutputStream out = new FileOutputStream(outputFilePath); - ZipInputStream zis = new ZipInputStream(new FileInputStream(srcZip));) { + ZipInputStream zis = new ZipInputStream(new FileInputStream(srcZip)); ) { ZipEntry ze = zis.getNextEntry(); while (ze != null) { if (ze.getName().equals(targetFile)) { diff --git a/src/main/java/jaz/Zer.java b/src/main/java/jaz/Zer.java index 16fd712d1..1bb8a4438 100644 --- a/src/main/java/jaz/Zer.java +++ b/src/main/java/jaz/Zer.java @@ -24,28 +24,36 @@ /** * A honeypot class that reports a finding on initialization. * - * Class loading based on externally controlled data could lead to RCE - * depending on available classes on the classpath. Even if no applicable - * gadget class is available, allowing input to control class loading is a bad - * idea and should be prevented. A finding is generated whenever the class - * is loaded and initialized, regardless of its further use. - *

            - * This class needs to implement {@link Serializable} to be considered in - * deserialization scenarios. It also implements common constructors, getter - * and setter and common interfaces to increase chances of passing - * deserialization checks. - *

            - * Note: Jackson provides a nice list of "nasty classes" at - * Class loading based on externally controlled data could lead to RCE depending on available + * classes on the classpath. Even if no applicable gadget class is available, allowing input to + * control class loading is a bad idea and should be prevented. A finding is generated whenever the + * class is loaded and initialized, regardless of its further use. + * + *

            This class needs to implement {@link Serializable} to be considered in deserialization + * scenarios. It also implements common constructors, getter and setter and common interfaces to + * increase chances of passing deserialization checks. + * + *

            Note: Jackson provides a nice list of "nasty classes" at SubTypeValidator. - *

            - * Note: This class must not be referenced in any way by the rest of the code, not even + * + *

            Note: This class must not be referenced in any way by the rest of the code, not even * statically. When referring to it, always use its hardcoded class name {@code jaz.Zer}. */ @SuppressWarnings({"rawtypes", "unused"}) public class Zer - implements Serializable, Cloneable, Comparable, Comparator, Closeable, Flushable, Iterable, - Iterator, Runnable, Callable, Function, Collection, List { + implements Serializable, + Cloneable, + Comparable, + Comparator, + Closeable, + Flushable, + Iterable, + Iterator, + Runnable, + Callable, + Function, + Collection, + List { static final long serialVersionUID = 42L; // serialized size is 41 bytes @@ -97,9 +105,12 @@ private void reportFindingIfEnabled() { } private static void reportFinding() { - Jazzer.reportFindingFromHook(new FuzzerSecurityIssueHigh("Remote Code Execution\n" - + "Unrestricted class/object creation based on externally controlled data may allow\n" - + "remote code execution depending on available classes on the classpath.")); + Jazzer.reportFindingFromHook( + new FuzzerSecurityIssueHigh( + "Remote Code Execution\n" + + "Unrestricted class/object creation based on externally controlled data may" + + " allow\n" + + "remote code execution depending on available classes on the classpath.")); } private static boolean isSanitizerEnabled(byte sanitizerId) { diff --git a/src/test/java/com/code_intelligence/jazzer/JazzerTest.java b/src/test/java/com/code_intelligence/jazzer/JazzerTest.java index 606a54a5f..b7d65d685 100644 --- a/src/test/java/com/code_intelligence/jazzer/JazzerTest.java +++ b/src/test/java/com/code_intelligence/jazzer/JazzerTest.java @@ -29,8 +29,9 @@ void testIsLibFuzzerOptionEnabled() { assertThat(isLibFuzzerOptionEnabled("foo", asList("-bar=1", "-foo=1", "baz"))).isTrue(); assertThat(isLibFuzzerOptionEnabled("foo", asList("-bar=1", "-foo=1", "some/path", "-foo="))) .isFalse(); - assertThat(isLibFuzzerOptionEnabled( - "foo", asList("-bar=1", "-foo=1", "-baz=1", "-foo=0", "--baz=0", "-foo=12"))) + assertThat( + isLibFuzzerOptionEnabled( + "foo", asList("-bar=1", "-foo=1", "-baz=1", "-foo=0", "--baz=0", "-foo=12"))) .isTrue(); } } diff --git a/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java b/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java index ee192d469..d12c82438 100644 --- a/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java +++ b/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java @@ -27,6 +27,7 @@ public class AutofuzzTest { public interface UnimplementedInterface {} public interface ImplementedInterface {} + public static class ImplementingClass implements ImplementedInterface {} private static boolean implIsNotNull(ImplementedInterface impl) { @@ -46,33 +47,45 @@ private static void checkAllTheArguments( @Test public void testConsume() { - FuzzedDataProvider data = CannedFuzzedDataProvider.create( - Arrays.asList((byte) 1 /* do not return null */, 0 /* first class on the classpath */, - (byte) 1 /* do not return null */, 0 /* first constructor */)); + FuzzedDataProvider data = + CannedFuzzedDataProvider.create( + Arrays.asList( + (byte) 1 /* do not return null */, + 0 /* first class on the classpath */, + (byte) 1 /* do not return null */, + 0 /* first constructor */)); ImplementedInterface result = Autofuzz.consume(data, ImplementedInterface.class); assertNotNull(result); } @Test public void testConsumeFailsWithoutException() { - FuzzedDataProvider data = CannedFuzzedDataProvider.create(Collections.singletonList( - (byte) 1 /* do not return null without searching for implementing classes */)); + FuzzedDataProvider data = + CannedFuzzedDataProvider.create( + Collections.singletonList( + (byte) 1 /* do not return null without searching for implementing classes */)); assertNull(Autofuzz.consume(data, UnimplementedInterface.class)); } @Test public void testAutofuzz() { - FuzzedDataProvider data = CannedFuzzedDataProvider.create( - Arrays.asList((byte) 1 /* do not return null */, 0 /* first class on the classpath */, - (byte) 1 /* do not return null */, 0 /* first constructor */)); - assertEquals(Boolean.TRUE, + FuzzedDataProvider data = + CannedFuzzedDataProvider.create( + Arrays.asList( + (byte) 1 /* do not return null */, + 0 /* first class on the classpath */, + (byte) 1 /* do not return null */, + 0 /* first constructor */)); + assertEquals( + Boolean.TRUE, Autofuzz.autofuzz(data, (Function1) AutofuzzTest::implIsNotNull)); } @Test public void testAutofuzzFailsWithException() { - FuzzedDataProvider data = CannedFuzzedDataProvider.create( - Collections.singletonList((byte) 1 /* do not return null */)); + FuzzedDataProvider data = + CannedFuzzedDataProvider.create( + Collections.singletonList((byte) 1 /* do not return null */)); try { Autofuzz.autofuzz(data, (Function1) AutofuzzTest::implIsNotNull); } catch (AutofuzzConstructionException e) { @@ -84,18 +97,32 @@ public void testAutofuzzFailsWithException() { @Test public void testAutofuzzConsumer() { - FuzzedDataProvider data = CannedFuzzedDataProvider.create( - Arrays.asList((byte) 1 /* do not return null */, 6 /* string length */, "foobar", 42, - (byte) 5, (byte) 1 /* do not return null */, 0 /* first class on the classpath */, - (byte) 1 /* do not return null */, 0 /* first constructor */)); + FuzzedDataProvider data = + CannedFuzzedDataProvider.create( + Arrays.asList( + (byte) 1 /* do not return null */, + 6 /* string length */, + "foobar", + 42, + (byte) 5, + (byte) 1 /* do not return null */, + 0 /* first class on the classpath */, + (byte) 1 /* do not return null */, + 0 /* first constructor */)); Autofuzz.autofuzz(data, AutofuzzTest::checkAllTheArguments); } @Test public void testAutofuzzConsumerThrowsException() { FuzzedDataProvider data = - CannedFuzzedDataProvider.create(Arrays.asList((byte) 1 /* do not return null */, - 6 /* string length */, "foobar", 42, (byte) 5, (byte) 0 /* *do* return null */)); + CannedFuzzedDataProvider.create( + Arrays.asList( + (byte) 1 /* do not return null */, + 6 /* string length */, + "foobar", + 42, + (byte) 5, + (byte) 0 /* *do* return null */)); try { Autofuzz.autofuzz(data, AutofuzzTest::checkAllTheArguments); } catch (IllegalArgumentException e) { diff --git a/src/test/java/com/code_intelligence/jazzer/autofuzz/BuilderPatternTest.java b/src/test/java/com/code_intelligence/jazzer/autofuzz/BuilderPatternTest.java index a602d7128..9a424b8e7 100644 --- a/src/test/java/com/code_intelligence/jazzer/autofuzz/BuilderPatternTest.java +++ b/src/test/java/com/code_intelligence/jazzer/autofuzz/BuilderPatternTest.java @@ -28,13 +28,13 @@ class Employee { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; Employee hero = (Employee) o; - return age == hero.age && Objects.equals(firstName, hero.firstName) - && Objects.equals(lastName, hero.lastName) && Objects.equals(jobTitle, hero.jobTitle); + return age == hero.age + && Objects.equals(firstName, hero.firstName) + && Objects.equals(lastName, hero.lastName) + && Objects.equals(jobTitle, hero.jobTitle); } @Override @@ -79,9 +79,12 @@ public Employee build() { public class BuilderPatternTest { @Test public void testBuilderPattern() { - consumeTestCase(new Employee.Builder("foo", "bar").withAge(20).withJobTitle("baz").build(), - "new com.code_intelligence.jazzer.autofuzz.Employee.Builder(\"foo\", \"bar\").withAge(20).withJobTitle(\"baz\").build()", - Arrays.asList((byte) 1, // do not return null + consumeTestCase( + new Employee.Builder("foo", "bar").withAge(20).withJobTitle("baz").build(), + "new com.code_intelligence.jazzer.autofuzz.Employee.Builder(\"foo\"," + + " \"bar\").withAge(20).withJobTitle(\"baz\").build()", + Arrays.asList( + (byte) 1, // do not return null 0, // Select the first Builder 2, // Select two Builder methods returning a builder object (fluent design) 0, // Select the first build method diff --git a/src/test/java/com/code_intelligence/jazzer/autofuzz/InterfaceCreationTest.java b/src/test/java/com/code_intelligence/jazzer/autofuzz/InterfaceCreationTest.java index 3fecb973e..23de691ad 100644 --- a/src/test/java/com/code_intelligence/jazzer/autofuzz/InterfaceCreationTest.java +++ b/src/test/java/com/code_intelligence/jazzer/autofuzz/InterfaceCreationTest.java @@ -27,7 +27,7 @@ public interface InterfaceA { void bar(); } - public static abstract class ClassA1 implements InterfaceA { + public abstract static class ClassA1 implements InterfaceA { @Override public void foo() {} } @@ -44,10 +44,8 @@ public void bar() {} @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; ClassB1 classB1 = (ClassB1) o; return n == classB1.n; } @@ -73,10 +71,8 @@ public void bar() {} @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; ClassB2 classB2 = (ClassB2) o; return Objects.equals(s, classB2.s); } @@ -86,19 +82,28 @@ public int hashCode() { return Objects.hash(s); } } + @Test public void testConsumeInterface() { - consumeTestCase(InterfaceA.class, new ClassB1(5), - "(com.code_intelligence.jazzer.autofuzz.InterfaceCreationTest.InterfaceA) new com.code_intelligence.jazzer.autofuzz.InterfaceCreationTest.ClassB1(5)", - Arrays.asList((byte) 1, // do not return null + consumeTestCase( + InterfaceA.class, + new ClassB1(5), + "(com.code_intelligence.jazzer.autofuzz.InterfaceCreationTest.InterfaceA) new" + + " com.code_intelligence.jazzer.autofuzz.InterfaceCreationTest.ClassB1(5)", + Arrays.asList( + (byte) 1, // do not return null 0, // pick ClassB1 (byte) 1, // do not return null 0, // pick first constructor 5 // arg for ClassB1 constructor )); - consumeTestCase(InterfaceA.class, new ClassB2("test"), - "(com.code_intelligence.jazzer.autofuzz.InterfaceCreationTest.InterfaceA) new com.code_intelligence.jazzer.autofuzz.InterfaceCreationTest.ClassB2(\"test\")", - Arrays.asList((byte) 1, // do not return null + consumeTestCase( + InterfaceA.class, + new ClassB2("test"), + "(com.code_intelligence.jazzer.autofuzz.InterfaceCreationTest.InterfaceA) new" + + " com.code_intelligence.jazzer.autofuzz.InterfaceCreationTest.ClassB2(\"test\")", + Arrays.asList( + (byte) 1, // do not return null 1, // pick ClassB2 (byte) 1, // do not return null 0, // pick first constructor diff --git a/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java b/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java index d2fec3ae0..c055e431d 100644 --- a/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java +++ b/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java @@ -47,44 +47,69 @@ public void testConsume() throws NoSuchMethodException { String testString = "foo\n\t\\\"bar"; // The expected string is obtained from testString by escaping and wrapping into escaped quotes. - consumeTestCase(testString, "\"foo\\n\\t\\\\\\\"bar\"", - Arrays.asList((byte) 1, // do not return null - testString.length(), testString)); + consumeTestCase( + testString, + "\"foo\\n\\t\\\\\\\"bar\"", + Arrays.asList( + (byte) 1, // do not return null + testString.length(), + testString)); consumeTestCase(null, "null", Collections.singletonList((byte) 0)); boolean[] testBooleans = new boolean[] {true, false, true}; - consumeTestCase(testBooleans, "new boolean[]{true, false, true}", - Arrays.asList((byte) 1, // do not return null for the array - 2 * 3, testBooleans)); + consumeTestCase( + testBooleans, + "new boolean[]{true, false, true}", + Arrays.asList( + (byte) 1, // do not return null for the array + 2 * 3, + testBooleans)); char[] testChars = new char[] {'a', '\n', '\''}; - consumeTestCase(testChars, "new char[]{'a', '\\n', '\\''}", - Arrays.asList((byte) 1, // do not return null for the array - 2 * 3 * Character.BYTES + Character.BYTES, testChars[0], 2 * 3 * Character.BYTES, + consumeTestCase( + testChars, + "new char[]{'a', '\\n', '\\''}", + Arrays.asList( + (byte) 1, // do not return null for the array + 2 * 3 * Character.BYTES + Character.BYTES, + testChars[0], + 2 * 3 * Character.BYTES, 2 * 3 * Character.BYTES, // remaining bytes, 2 times what is needed for 3 chars - testChars[1], testChars[2])); + testChars[1], + testChars[2])); char[] testNoChars = new char[] {}; - consumeTestCase(testNoChars, "new char[]{}", - Arrays.asList((byte) 1, // do not return null for the array + consumeTestCase( + testNoChars, + "new char[]{}", + Arrays.asList( + (byte) 1, // do not return null for the array 0, 'a', 0, 0)); short[] testShorts = new short[] {(short) 1, (short) 2, (short) 3}; - consumeTestCase(testShorts, "new short[]{(short) 1, (short) 2, (short) 3}", - Arrays.asList((byte) 1, // do not return null for the array + consumeTestCase( + testShorts, + "new short[]{(short) 1, (short) 2, (short) 3}", + Arrays.asList( + (byte) 1, // do not return null for the array 2 * 3 * Short.BYTES, // remaining bytes testShorts)); long[] testLongs = new long[] {1L, 2L, 3L}; - consumeTestCase(testLongs, "new long[]{1L, 2L, 3L}", - Arrays.asList((byte) 1, // do not return null for the array + consumeTestCase( + testLongs, + "new long[]{1L, 2L, 3L}", + Arrays.asList( + (byte) 1, // do not return null for the array 2 * 3 * Long.BYTES, // remaining bytes testLongs)); - consumeTestCase(new String[] {"foo", "bar", "foo\nbar"}, + consumeTestCase( + new String[] {"foo", "bar", "foo\nbar"}, "new java.lang.String[]{\"foo\", \"bar\", \"foo\\nbar\"}", - Arrays.asList((byte) 1, // do not return null for the array + Arrays.asList( + (byte) 1, // do not return null for the array 32, // remaining bytes (byte) 1, // do not return null for the string 31, // remaining bytes @@ -99,34 +124,48 @@ public void testConsume() throws NoSuchMethodException { "foo\nbar")); byte[] testInputStreamBytes = new byte[] {(byte) 1, (byte) 2, (byte) 3}; - consumeTestCase(new ByteArrayInputStream(testInputStreamBytes), + consumeTestCase( + new ByteArrayInputStream(testInputStreamBytes), "new java.io.ByteArrayInputStream(new byte[]{(byte) 1, (byte) 2, (byte) 3})", - Arrays.asList((byte) 1, // do not return null for the InputStream + Arrays.asList( + (byte) 1, // do not return null for the InputStream 2 * 3, // remaining bytes (twice the desired length) testInputStreamBytes)); - consumeTestCase(TestEnum.BAR, + consumeTestCase( + TestEnum.BAR, String.format("%s.%s", TestEnum.class.getName(), TestEnum.BAR.name()), - Arrays.asList((byte) 1, // do not return null for the enum value - 1 /* second value */ - )); + Arrays.asList( + (byte) 1, // do not return null for the enum value + 1 /* second value */)); - consumeTestCase(YourAverageJavaClass.class, + consumeTestCase( + YourAverageJavaClass.class, "com.code_intelligence.jazzer.autofuzz.YourAverageJavaClass.class", Collections.singletonList((byte) 1)); Type stringStringMapType = MetaTest.class.getDeclaredMethod("returnsStringStringMap").getGenericReturnType(); Map expectedMap = - java.util.stream.Stream - .of(new java.util.AbstractMap.SimpleEntry<>("key0", "value0"), + java.util.stream.Stream.of( + new java.util.AbstractMap.SimpleEntry<>("key0", "value0"), new java.util.AbstractMap.SimpleEntry<>("key1", "value1"), new java.util.AbstractMap.SimpleEntry<>("key2", (java.lang.String) null)) - .collect(java.util.HashMap::new, - (map, e) -> map.put(e.getKey(), e.getValue()), java.util.HashMap::putAll); - consumeTestCase(stringStringMapType, expectedMap, - "java.util.stream.Stream.>of(new java.util.AbstractMap.SimpleEntry<>(\"key0\", \"value0\"), new java.util.AbstractMap.SimpleEntry<>(\"key1\", \"value1\"), new java.util.AbstractMap.SimpleEntry<>(\"key2\", (java.lang.String) null)).collect(java.util.HashMap::new, (map, e) -> map.put(e.getKey(), e.getValue()), java.util.HashMap::putAll)", - Arrays.asList((byte) 1, // do not return null for the map + .collect( + java.util.HashMap::new, + (map, e) -> map.put(e.getKey(), e.getValue()), + java.util.HashMap::putAll); + consumeTestCase( + stringStringMapType, + expectedMap, + "java.util.stream.Stream.>of(new java.util.AbstractMap.SimpleEntry<>(\"key0\", \"value0\")," + + " new java.util.AbstractMap.SimpleEntry<>(\"key1\", \"value1\"), new" + + " java.util.AbstractMap.SimpleEntry<>(\"key2\", (java.lang.String)" + + " null)).collect(java.util.HashMap::new, (map, e) -> map.put(e.getKey()," + + " e.getValue()), java.util.HashMap::putAll)", + Arrays.asList( + (byte) 1, // do not return null for the map 32, // remaining bytes (byte) 1, // do not return null for the string 31, // remaining bytes @@ -164,33 +203,54 @@ public static boolean intEquals(int arg1, int arg2) { @Test public void testAutofuzz() throws NoSuchMethodException { - autofuzzTestCase(true, "com.code_intelligence.jazzer.autofuzz.MetaTest.isFive(5)", - MetaTest.class.getMethod("isFive", int.class), Collections.singletonList(5)); - autofuzzTestCase(false, "com.code_intelligence.jazzer.autofuzz.MetaTest.intEquals(5, 4)", - MetaTest.class.getMethod("intEquals", int.class, int.class), Arrays.asList(5, 4)); - autofuzzTestCase("foobar", "(\"foo\").concat(\"bar\")", + autofuzzTestCase( + true, + "com.code_intelligence.jazzer.autofuzz.MetaTest.isFive(5)", + MetaTest.class.getMethod("isFive", int.class), + Collections.singletonList(5)); + autofuzzTestCase( + false, + "com.code_intelligence.jazzer.autofuzz.MetaTest.intEquals(5, 4)", + MetaTest.class.getMethod("intEquals", int.class, int.class), + Arrays.asList(5, 4)); + autofuzzTestCase( + "foobar", + "(\"foo\").concat(\"bar\")", String.class.getMethod("concat", String.class), Arrays.asList((byte) 1, 6, "foo", (byte) 1, 6, "bar")); - autofuzzTestCase("jazzer", "new java.lang.String(\"jazzer\")", - String.class.getConstructor(String.class), Arrays.asList((byte) 1, 12, "jazzer")); - autofuzzTestCase("\"jazzer\"", "com.google.json.JsonSanitizer.sanitize(\"jazzer\")", + autofuzzTestCase( + "jazzer", + "new java.lang.String(\"jazzer\")", + String.class.getConstructor(String.class), + Arrays.asList((byte) 1, 12, "jazzer")); + autofuzzTestCase( + "\"jazzer\"", + "com.google.json.JsonSanitizer.sanitize(\"jazzer\")", JsonSanitizer.class.getMethod("sanitize", String.class), Arrays.asList((byte) 1, 12, "jazzer")); FuzzedDataProvider data = - CannedFuzzedDataProvider.create(Arrays.asList((byte) 1, // do not return null - 8, // remainingBytes - "buzz")); - assertEquals("fizzbuzz", new Meta(null).autofuzz(data, "fizz" ::concat)); + CannedFuzzedDataProvider.create( + Arrays.asList( + (byte) 1, // do not return null + 8, // remainingBytes + "buzz")); + assertEquals("fizzbuzz", new Meta(null).autofuzz(data, "fizz"::concat)); } // Regression test for https://github.com/CodeIntelligenceTesting/jazzer/issues/465. @Test public void testPrivateInterface() { - autofuzzTestCase(null, - "com.code_intelligence.jazzer.autofuzz.OpinionatedClass.doStuffWithPrivateInterface(((java.util.function.Supplier) (() -> {com.code_intelligence.jazzer.autofuzz.OpinionatedClass.PublicImplementation autofuzzVariable0 = new com.code_intelligence.jazzer.autofuzz.OpinionatedClass.PublicImplementation(); return autofuzzVariable0;})).get())", + autofuzzTestCase( + null, + "com.code_intelligence.jazzer.autofuzz.OpinionatedClass.doStuffWithPrivateInterface(((java.util.function.Supplier)" + + " (() -> {com.code_intelligence.jazzer.autofuzz.OpinionatedClass.PublicImplementation" + + " autofuzzVariable0 = new" + + " com.code_intelligence.jazzer.autofuzz.OpinionatedClass.PublicImplementation();" + + " return autofuzzVariable0;})).get())", OpinionatedClass.class.getDeclaredMethods()[0], - Arrays.asList((byte) 1, // do not return null + Arrays.asList( + (byte) 1, // do not return null 0, // first (and only) class on the classpath (byte) 1, // do not return null 0 /* first (and only) constructor*/)); diff --git a/src/test/java/com/code_intelligence/jazzer/autofuzz/SettersTest.java b/src/test/java/com/code_intelligence/jazzer/autofuzz/SettersTest.java index 7c869531e..07580477a 100644 --- a/src/test/java/com/code_intelligence/jazzer/autofuzz/SettersTest.java +++ b/src/test/java/com/code_intelligence/jazzer/autofuzz/SettersTest.java @@ -27,9 +27,16 @@ public void testEmptyConstructorWithSetters() { employee.setFirstName("foo"); employee.setAge(26); - consumeTestCase(employee, - "((java.util.function.Supplier) (() -> {com.code_intelligence.jazzer.autofuzz.testdata.EmployeeWithSetters autofuzzVariable0 = new com.code_intelligence.jazzer.autofuzz.testdata.EmployeeWithSetters(); autofuzzVariable0.setFirstName(\"foo\"); autofuzzVariable0.setAge(26); return autofuzzVariable0;})).get()", - Arrays.asList((byte) 1, // do not return null for EmployeeWithSetters + consumeTestCase( + employee, + "((java.util.function.Supplier)" + + " (() -> {com.code_intelligence.jazzer.autofuzz.testdata.EmployeeWithSetters" + + " autofuzzVariable0 = new" + + " com.code_intelligence.jazzer.autofuzz.testdata.EmployeeWithSetters();" + + " autofuzzVariable0.setFirstName(\"foo\"); autofuzzVariable0.setAge(26); return" + + " autofuzzVariable0;})).get()", + Arrays.asList( + (byte) 1, // do not return null for EmployeeWithSetters 0, // pick first constructor 2, // pick two setters 1, // pick second setter diff --git a/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java b/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java index 89f9c9685..f8c9b4592 100644 --- a/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java +++ b/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java @@ -64,7 +64,10 @@ static void consumeTestCase( assertEquals(expectedResultString, visitor.generate()); } - static void autofuzzTestCase(Object expectedResult, String expectedResultString, Executable func, + static void autofuzzTestCase( + Object expectedResult, + String expectedResultString, + Executable func, List cannedData) { AutofuzzCodegenVisitor visitor = new AutofuzzCodegenVisitor(); FuzzedDataProvider data = CannedFuzzedDataProvider.create(cannedData); diff --git a/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/EmployeeWithSetters.java b/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/EmployeeWithSetters.java index 2c76a61f8..4282f1b42 100644 --- a/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/EmployeeWithSetters.java +++ b/src/test/java/com/code_intelligence/jazzer/autofuzz/testdata/EmployeeWithSetters.java @@ -24,13 +24,13 @@ public class EmployeeWithSetters { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; EmployeeWithSetters hero = (EmployeeWithSetters) o; - return age == hero.age && Objects.equals(firstName, hero.firstName) - && Objects.equals(lastName, hero.lastName) && Objects.equals(jobTitle, hero.jobTitle); + return age == hero.age + && Objects.equals(firstName, hero.firstName) + && Objects.equals(lastName, hero.lastName) + && Objects.equals(jobTitle, hero.jobTitle); } @Override diff --git a/src/test/java/com/code_intelligence/jazzer/driver/FuzzTargetRunnerTest.java b/src/test/java/com/code_intelligence/jazzer/driver/FuzzTargetRunnerTest.java index e0ff31316..5bc1e4f8e 100644 --- a/src/test/java/com/code_intelligence/jazzer/driver/FuzzTargetRunnerTest.java +++ b/src/test/java/com/code_intelligence/jazzer/driver/FuzzTargetRunnerTest.java @@ -121,12 +121,12 @@ public static void main(String[] args) { assert UNSAFE.getByte(CoverageMap.countersAddress + 3) == 0; String errOutput = new String(recordedErr.toByteArray(), StandardCharsets.UTF_8); - List unexpectedLines = Arrays.stream(errOutput.split("\n")) - .filter(line -> !line.startsWith("INFO: ")) - .collect(Collectors.toList()); + List unexpectedLines = + Arrays.stream(errOutput.split("\n")) + .filter(line -> !line.startsWith("INFO: ")) + .collect(Collectors.toList()); assert unexpectedLines.isEmpty() - : "Unexpected output on System.err: '" - + String.join("\n", unexpectedLines) + "'"; + : "Unexpected output on System.err: '" + String.join("\n", unexpectedLines) + "'"; String outOutput = new String(recordedOut.toByteArray(), StandardCharsets.UTF_8); assert outOutput.isEmpty() : "Non-empty System.out: '" + outOutput + "'"; } @@ -174,7 +174,8 @@ public static void main(String[] args) { // Verify that the StackOverflowError is wrapped in security issue and contains reproducer // information. assert errOutput.contains( - "== Java Exception: com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow: Stack overflow (use "); + "== Java Exception: com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow: Stack" + + " overflow (use "); assert !errOutput.contains("not reported"); Matcher dedupTokenMatcher = DEDUP_TOKEN_PATTERN.matcher(outOutput); assert dedupTokenMatcher.matches() : "Unexpected output on System.out: '" + outOutput + "'"; @@ -194,11 +195,10 @@ public static void main(String[] args) { throw new IllegalStateException("Expected FuzzTargetRunner to call fuzzerTearDown"); } - /** - * An OutputStream that prints to two OutputStreams simultaneously. - */ + /** An OutputStream that prints to two OutputStreams simultaneously. */ private static class TeeOutputStream extends PrintStream { private final PrintStream otherOut; + public TeeOutputStream(PrintStream out1, PrintStream out2) { super(out1, true); this.otherOut = out2; diff --git a/src/test/java/com/code_intelligence/jazzer/driver/FuzzedDataProviderImplTest.java b/src/test/java/com/code_intelligence/jazzer/driver/FuzzedDataProviderImplTest.java index 26ebc0dfd..fc21b1159 100644 --- a/src/test/java/com/code_intelligence/jazzer/driver/FuzzedDataProviderImplTest.java +++ b/src/test/java/com/code_intelligence/jazzer/driver/FuzzedDataProviderImplTest.java @@ -23,12 +23,12 @@ public class FuzzedDataProviderImplTest { public static void main(String[] args) { try (FuzzedDataProviderImpl fuzzedDataProvider = - FuzzedDataProviderImpl.withJavaData(INPUT_BYTES)) { + FuzzedDataProviderImpl.withJavaData(INPUT_BYTES)) { verifyFuzzedDataProvider(fuzzedDataProvider); } } - private strictfp static void verifyFuzzedDataProvider(FuzzedDataProvider data) { + private static strictfp void verifyFuzzedDataProvider(FuzzedDataProvider data) { assertEqual(true, data.consumeBoolean()); assertEqual((byte) 0x7F, data.consumeByte()); @@ -48,9 +48,11 @@ private strictfp static void verifyFuzzedDataProvider(FuzzedDataProvider data) { assertEqual("ja\u0000zer", data.consumeAsciiString(6)); assertEqual("\u0062\u0002\u002C\u0043\u001F", data.consumeAsciiString(5)); - assertEqual(true, + assertEqual( + true, Arrays.equals(new boolean[] {false, false, true, false, true}, data.consumeBooleans(5))); - assertEqual(true, + assertEqual( + true, Arrays.equals(new long[] {0x0123456789abdcefL, 0xfedcba9876543210L}, data.consumeLongs(2))); assertAtLeastAsPrecise((float) 0.28969181, data.consumeProbabilityFloat()); @@ -131,108 +133,319 @@ private static > void assertEqual(T a, T b) { } } - private static final byte[] INPUT_BYTES = new byte[] { - // Bytes read from the start - 0x01, 0x02, // consumeBytes(2): {0x01, 0x02} - - 'j', 'a', 'z', 'z', 'e', 'r', // consumeString(6): "jazzer" - 'j', 'a', 0x00, 'z', 'e', 'r', // consumeString(6): "ja\u0000zer" - (byte) 0xE2, (byte) 0x82, (byte) 0xAC, (byte) 0xC3, (byte) 0x9F, // consumeString(2): "€ẞ" - - 'j', 'a', 'z', 'z', 'e', 'r', // consumeAsciiString(6): "jazzer" - 'j', 'a', 0x00, 'z', 'e', 'r', // consumeAsciiString(6): "ja\u0000zer" - (byte) 0xE2, (byte) 0x82, (byte) 0xAC, (byte) 0xC3, - (byte) 0x9F, // consumeAsciiString(5): "\u0062\u0002\u002C\u0043\u001F" - - 0, 0, 1, 0, 1, // consumeBooleans(5): { false, false, true, false, true } - (byte) 0xEF, (byte) 0xDC, (byte) 0xAB, (byte) 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, - 0x76, (byte) 0x98, (byte) 0xBA, (byte) 0xDC, (byte) 0xFE, - // consumeLongs(2): { 0x0123456789ABCDEF, 0xFEDCBA9876543210 } - - 0x78, 0x56, 0x34, 0x12, // consumeInts(3): { 0x12345678 } - 0x56, 0x34, 0x12, // consumeLong(): - - // Bytes read from the end - 0x02, 0x03, 0x02, 0x04, // 4x pickValue in array with five elements - - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 10, // -max for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 9, // max for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 8, // -min for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 7, // min for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 6, // -denorm_min for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 5, // denorm_min for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 4, // NaN for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 3, // -infinity for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 2, // infinity for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 1, // -0.0 for next consumeDouble - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56, - 0x78, // consumed but unused by consumeDouble() - 0, // 0.0 for next consumeDouble - - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 10, // -max for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 9, // max for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 8, // -min for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 7, // min for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 6, // -denorm_min for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 5, // denorm_min for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 4, // NaN for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 3, // -infinity for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 2, // infinity for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 1, // -0.0 for next consumeFloat - 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat() - 0, // 0.0 for next consumeFloat - - (byte) 0x88, (byte) 0xAB, 0x61, (byte) 0xCB, 0x32, (byte) 0xEB, 0x30, (byte) 0xF9, - // consumeDouble(13.37, 31.337): 30.859126145478349 (small range) - 0x51, (byte) 0xF6, 0x1F, 0x3A, // consumeFloat(123.0, 777.0): 271.49084 (small range) - 0x11, 0x4D, (byte) 0xFD, 0x54, (byte) 0xD6, 0x3D, 0x43, 0x73, 0x39, - // consumeRegularDouble(): 8.0940194040236032e+307 - 0x16, (byte) 0xCF, 0x3D, 0x29, 0x4A, // consumeRegularFloat(): -2.8546307e+38 - - 0x61, (byte) 0xCB, 0x32, (byte) 0xEB, 0x30, (byte) 0xF9, 0x51, (byte) 0xF6, - // consumeProbabilityDouble(): 0.96218831486039413 - 0x1F, 0x3A, 0x11, 0x4D, // consumeProbabilityFloat(): 0.30104411 - (byte) 0xFD, 0x54, (byte) 0xD6, 0x3D, 0x43, 0x73, 0x39, 0x16, - // consumeProbabilityDouble(): 0.086814121166605432 - (byte) 0xCF, 0x3D, 0x29, 0x4A, // consumeProbabilityFloat(): 0.28969181 - - 0x01, // consumeInt(0x12345678, 0x12345679): 0x12345679 - 0x78, // consumeInt(-0x12345678, -0x12345600): -0x12345600 - 0x78, 0x56, 0x34, 0x12, // consumeInt(): 0x12345678 - - 0x02, // consumeByte(0x12, 0x22): 0x14 - 0x7F, // consumeByte(): 0x7F - - 0x01, // consumeBool(): true - }; + private static final byte[] INPUT_BYTES = + new byte[] { + // Bytes read from the start + 0x01, + 0x02, // consumeBytes(2): {0x01, 0x02} + 'j', + 'a', + 'z', + 'z', + 'e', + 'r', // consumeString(6): "jazzer" + 'j', + 'a', + 0x00, + 'z', + 'e', + 'r', // consumeString(6): "ja\u0000zer" + (byte) 0xE2, + (byte) 0x82, + (byte) 0xAC, + (byte) 0xC3, + (byte) 0x9F, // consumeString(2): "€ẞ" + 'j', + 'a', + 'z', + 'z', + 'e', + 'r', // consumeAsciiString(6): "jazzer" + 'j', + 'a', + 0x00, + 'z', + 'e', + 'r', // consumeAsciiString(6): "ja\u0000zer" + (byte) 0xE2, + (byte) 0x82, + (byte) 0xAC, + (byte) 0xC3, + (byte) 0x9F, // consumeAsciiString(5): "\u0062\u0002\u002C\u0043\u001F" + 0, + 0, + 1, + 0, + 1, // consumeBooleans(5): { false, false, true, false, true } + (byte) 0xEF, + (byte) 0xDC, + (byte) 0xAB, + (byte) 0x89, + 0x67, + 0x45, + 0x23, + 0x01, + 0x10, + 0x32, + 0x54, + 0x76, + (byte) 0x98, + (byte) 0xBA, + (byte) 0xDC, + (byte) 0xFE, + // consumeLongs(2): { 0x0123456789ABCDEF, 0xFEDCBA9876543210 } + + 0x78, + 0x56, + 0x34, + 0x12, // consumeInts(3): { 0x12345678 } + 0x56, + 0x34, + 0x12, // consumeLong(): + + // Bytes read from the end + 0x02, + 0x03, + 0x02, + 0x04, // 4x pickValue in array with five elements + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 10, // -max for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 9, // max for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 8, // -min for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 7, // min for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 6, // -denorm_min for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 5, // denorm_min for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 4, // NaN for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 3, // -infinity for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 2, // infinity for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 1, // -0.0 for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, + 0x12, + 0x34, + 0x56, + 0x78, // consumed but unused by consumeDouble() + 0, // 0.0 for next consumeDouble + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 10, // -max for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 9, // max for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 8, // -min for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 7, // min for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 6, // -denorm_min for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 5, // denorm_min for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 4, // NaN for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 3, // -infinity for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 2, // infinity for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 1, // -0.0 for next consumeFloat + 0x12, + 0x34, + 0x56, + 0x78, + (byte) 0x90, // consumed but unused by consumeFloat() + 0, // 0.0 for next consumeFloat + (byte) 0x88, + (byte) 0xAB, + 0x61, + (byte) 0xCB, + 0x32, + (byte) 0xEB, + 0x30, + (byte) 0xF9, + // consumeDouble(13.37, 31.337): 30.859126145478349 (small range) + 0x51, + (byte) 0xF6, + 0x1F, + 0x3A, // consumeFloat(123.0, 777.0): 271.49084 (small range) + 0x11, + 0x4D, + (byte) 0xFD, + 0x54, + (byte) 0xD6, + 0x3D, + 0x43, + 0x73, + 0x39, + // consumeRegularDouble(): 8.0940194040236032e+307 + 0x16, + (byte) 0xCF, + 0x3D, + 0x29, + 0x4A, // consumeRegularFloat(): -2.8546307e+38 + 0x61, + (byte) 0xCB, + 0x32, + (byte) 0xEB, + 0x30, + (byte) 0xF9, + 0x51, + (byte) 0xF6, + // consumeProbabilityDouble(): 0.96218831486039413 + 0x1F, + 0x3A, + 0x11, + 0x4D, // consumeProbabilityFloat(): 0.30104411 + (byte) 0xFD, + 0x54, + (byte) 0xD6, + 0x3D, + 0x43, + 0x73, + 0x39, + 0x16, + // consumeProbabilityDouble(): 0.086814121166605432 + (byte) 0xCF, + 0x3D, + 0x29, + 0x4A, // consumeProbabilityFloat(): 0.28969181 + 0x01, // consumeInt(0x12345678, 0x12345679): 0x12345679 + 0x78, // consumeInt(-0x12345678, -0x12345600): -0x12345600 + 0x78, + 0x56, + 0x34, + 0x12, // consumeInt(): 0x12345678 + 0x02, // consumeByte(0x12, 0x22): 0x14 + 0x7F, // consumeByte(): 0x7F + 0x01, // consumeBool(): true + }; } diff --git a/src/test/java/com/code_intelligence/jazzer/driver/OptItemTest.java b/src/test/java/com/code_intelligence/jazzer/driver/OptItemTest.java index 8837517e3..971ed4cb1 100644 --- a/src/test/java/com/code_intelligence/jazzer/driver/OptItemTest.java +++ b/src/test/java/com/code_intelligence/jazzer/driver/OptItemTest.java @@ -18,8 +18,6 @@ import static com.google.common.truth.Truth.assertThat; import static java.util.Collections.singletonList; -import static java.util.Collections.unmodifiableList; -import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertThrows; import com.code_intelligence.jazzer.driver.OptItem.StrList; @@ -30,7 +28,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Stream; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -65,12 +62,34 @@ static void setAdditionalSources() { void optItem_precedence() { // See BUILD.bazel for environment variables and manifest entries. assertThat(testOptItem("some_arg").get()) - .containsExactly("manifest_3", "manifest_4", "manifest_5", "manifest_7", "env_1", "env_2", - "property_1", "property_2", "config_1", "config_2", "cli_1", "cli_2", "cli_6") + .containsExactly( + "manifest_3", + "manifest_4", + "manifest_5", + "manifest_7", + "env_1", + "env_2", + "property_1", + "property_2", + "config_1", + "config_2", + "cli_1", + "cli_2", + "cli_6") .inOrder(); assertThat(testOptItem("other_arg").get()) - .containsExactly("manifest_1", "manifest_2", "manifest_6", "env_3", "property_3", - "property_4", "config_3", "config_4", "cli_3", "cli_4", "cli_5") + .containsExactly( + "manifest_1", + "manifest_2", + "manifest_6", + "env_3", + "property_3", + "property_4", + "config_3", + "config_4", + "cli_3", + "cli_4", + "cli_5") .inOrder(); } @@ -84,7 +103,8 @@ void optItem_setIfDefault() { OptItem> unsetArg = testOptItem("unset_arg"); assertThat(unsetArg.setIfDefault(Arrays.asList("not", "default"))).isTrue(); assertThat(unsetArg.get()).containsExactly("not", "default").inOrder(); - assertThrows(IllegalStateException.class, + assertThrows( + IllegalStateException.class, () -> unsetArg.setIfDefault(Arrays.asList("also", "not", "default"))); } @@ -93,7 +113,8 @@ void optItem_setIfDefault_ignored() { OptItem> setArg = testOptItem("some_arg"); assertThat(setArg.setIfDefault(singletonList("not_default"))).isFalse(); assertThat(setArg.get()).doesNotContain("not_default"); - assertThrows(IllegalStateException.class, + assertThrows( + IllegalStateException.class, () -> setArg.setIfDefault(Arrays.asList("also", "not", "default"))); } diff --git a/src/test/java/com/code_intelligence/jazzer/driver/RecordingFuzzedDataProviderTest.java b/src/test/java/com/code_intelligence/jazzer/driver/RecordingFuzzedDataProviderTest.java index de8e3a416..d6ecd107d 100644 --- a/src/test/java/com/code_intelligence/jazzer/driver/RecordingFuzzedDataProviderTest.java +++ b/src/test/java/com/code_intelligence/jazzer/driver/RecordingFuzzedDataProviderTest.java @@ -16,7 +16,6 @@ import com.code_intelligence.jazzer.api.CannedFuzzedDataProvider; import com.code_intelligence.jazzer.api.FuzzedDataProvider; -import com.code_intelligence.jazzer.driver.RecordingFuzzedDataProvider; import java.io.IOException; import java.util.Arrays; import java.util.stream.Collectors; @@ -48,10 +47,10 @@ private String sampleFuzzTarget(FuzzedDataProvider data) { result.append(Arrays.stream(ints).mapToObj(Integer::toString).collect(Collectors.joining(","))); result.append(data.pickValue(ints)); result.append(data.consumeString(20)); - result.append(data.pickValues(Arrays.stream(ints).boxed().collect(Collectors.toSet()), 5) - .stream() - .map(Integer::toHexString) - .collect(Collectors.joining(","))); + result.append( + data.pickValues(Arrays.stream(ints).boxed().collect(Collectors.toSet()), 5).stream() + .map(Integer::toHexString) + .collect(Collectors.joining(","))); result.append(data.remainingBytes()); return result.toString(); } diff --git a/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooks.java b/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooks.java index f8d6782cf..e5bf2faa5 100644 --- a/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooks.java +++ b/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooks.java @@ -21,41 +21,44 @@ public class AfterHooks { static AfterHooksTargetContract instance; - @MethodHook(type = HookType.AFTER, + @MethodHook( + type = HookType.AFTER, targetClassName = "com.code_intelligence.jazzer.instrumentor.AfterHooksTarget", targetMethod = "func1") - public static void - patchFunc1( + public static void patchFunc1( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) { instance = (AfterHooksTargetContract) thisObject; ((AfterHooksTargetContract) thisObject).registerHasFunc1BeenCalled(); } - @MethodHook(type = HookType.AFTER, + @MethodHook( + type = HookType.AFTER, targetClassName = "com.code_intelligence.jazzer.instrumentor.AfterHooksTarget", - targetMethod = "registerTimesCalled", targetMethodDescriptor = "()V") - public static void - patchRegisterTimesCalled(MethodHandle method, Object thisObject, Object[] arguments, int hookId, - Object returnValue) throws Throwable { + targetMethod = "registerTimesCalled", + targetMethodDescriptor = "()V") + public static void patchRegisterTimesCalled( + MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) + throws Throwable { // Invoke registerTimesCalled() again to pass the test. method.invoke(); } - @MethodHook(type = HookType.AFTER, + @MethodHook( + type = HookType.AFTER, targetClassName = "com.code_intelligence.jazzer.instrumentor.AfterHooksTarget", - targetMethod = "getFirstSecret", targetMethodDescriptor = "()Ljava/lang/String;") - public static void - patchGetFirstSecret( + targetMethod = "getFirstSecret", + targetMethodDescriptor = "()Ljava/lang/String;") + public static void patchGetFirstSecret( MethodHandle method, Object thisObject, Object[] arguments, int hookId, String returnValue) { // Use the returned secret to pass the test. ((AfterHooksTargetContract) thisObject).verifyFirstSecret(returnValue); } - @MethodHook(type = HookType.AFTER, + @MethodHook( + type = HookType.AFTER, targetClassName = "com.code_intelligence.jazzer.instrumentor.AfterHooksTarget", targetMethod = "getSecondSecret") - public static void - patchGetSecondSecret( + public static void patchGetSecondSecret( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) { // Use the returned secret to pass the test. ((AfterHooksTargetContract) thisObject).verifySecondSecret((String) returnValue); @@ -64,17 +67,19 @@ public class AfterHooks { // Verify the interaction of a BEFORE and an AFTER hook. The BEFORE hook modifies the argument of // the StringBuilder constructor. @MethodHook( - type = HookType.BEFORE, targetClassName = "java.lang.StringBuilder", targetMethod = "") - public static void - patchStringBuilderBeforeInit( + type = HookType.BEFORE, + targetClassName = "java.lang.StringBuilder", + targetMethod = "") + public static void patchStringBuilderBeforeInit( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { arguments[0] = "hunter3"; } @MethodHook( - type = HookType.AFTER, targetClassName = "java.lang.StringBuilder", targetMethod = "") - public static void - patchStringBuilderInit( + type = HookType.AFTER, + targetClassName = "java.lang.StringBuilder", + targetMethod = "") + public static void patchStringBuilderInit( MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) { String secret = ((StringBuilder) thisObject).toString(); // Verify that the argument passed to this AFTER hook agrees with the argument passed to the diff --git a/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooks.java b/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooks.java index 31577dada..7a084859e 100644 --- a/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooks.java +++ b/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooks.java @@ -19,31 +19,35 @@ import java.lang.invoke.MethodHandle; public class BeforeHooks { - @MethodHook(type = HookType.BEFORE, + @MethodHook( + type = HookType.BEFORE, targetClassName = "com.code_intelligence.jazzer.instrumentor.BeforeHooksTarget", - targetMethod = "hasFunc1BeenCalled", targetMethodDescriptor = "()Z") - public static void - patchHasFunc1BeenCalled(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + targetMethod = "hasFunc1BeenCalled", + targetMethodDescriptor = "()Z") + public static void patchHasFunc1BeenCalled( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { ((BeforeHooksTargetContract) thisObject).func1(); } - @MethodHook(type = HookType.BEFORE, + @MethodHook( + type = HookType.BEFORE, targetClassName = "com.code_intelligence.jazzer.instrumentor.BeforeHooksTarget", - targetMethod = "getTimesCalled", targetMethodDescriptor = "()Ljava/lang/Integer;") - public static void - patchHasBeenCalled(MethodHandle method, Object thisObject, Object[] arguments, int hookId) - throws Throwable { + targetMethod = "getTimesCalled", + targetMethodDescriptor = "()Ljava/lang/Integer;") + public static void patchHasBeenCalled( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable { // Invoke static method getTimesCalled() again to pass the test. method.invoke(); } - @MethodHook(type = HookType.BEFORE, + @MethodHook( + type = HookType.BEFORE, targetClassName = "com.code_intelligence.jazzer.instrumentor.BeforeHooksTarget", targetMethod = "hasFuncWithArgsBeenCalled") - public static void - patchHasFuncWithArgsBeenCalled( + public static void patchHasFuncWithArgsBeenCalled( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { - if (arguments.length == 2 && arguments[0] instanceof Boolean + if (arguments.length == 2 + && arguments[0] instanceof Boolean && arguments[1] instanceof String) { // only if the arguments passed to the hook match the expected argument types and count invoke // the method to pass the test diff --git a/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksTarget.java b/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksTarget.java index 869e04bf7..39807a569 100644 --- a/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksTarget.java +++ b/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksTarget.java @@ -19,7 +19,7 @@ // selfCheck() only passes with the hooks in BeforeHooks.java applied. public class BeforeHooksTarget implements BeforeHooksTargetContract { - static private int timesCalled = 0; + private static int timesCalled = 0; Map results = new HashMap<>(); Boolean func1Called = false; Boolean funcWithArgsCalled = false; diff --git a/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java b/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java index 0df349cab..3467ae618 100644 --- a/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java +++ b/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java @@ -25,63 +25,95 @@ public static void incorrectHookIdType( MethodHandle method, String thisObject, Object[] arguments, long hookId) {} @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "equals") - private static void invalidAfterHook(MethodHandle method, String thisObject, Object[] arguments, - int hookId, Boolean returnValue) {} + private static void invalidAfterHook( + MethodHandle method, + String thisObject, + Object[] arguments, + int hookId, + Boolean returnValue) {} @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "equals") - public void invalidAfterHook2(MethodHandle method, String thisObject, Object[] arguments, - int hookId, boolean returnValue) {} + public void invalidAfterHook2( + MethodHandle method, + String thisObject, + Object[] arguments, + int hookId, + boolean returnValue) {} - @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.String", - targetMethod = "equals", targetMethodDescriptor = "(Ljava/lang/Object;)Z") - public static String - incorrectReturnType(MethodHandle method, String thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.lang.String", + targetMethod = "equals", + targetMethodDescriptor = "(Ljava/lang/Object;)Z") + public static String incorrectReturnType( + MethodHandle method, String thisObject, Object[] arguments, int hookId) { return "foo"; } @MethodHook( - type = HookType.REPLACE, targetClassName = "java.lang.String", targetMethod = "equals") - public static boolean - invalidReplaceHook2(MethodHandle method, Integer thisObject, Object[] arguments, int hookId) { + type = HookType.REPLACE, + targetClassName = "java.lang.String", + targetMethod = "equals") + public static boolean invalidReplaceHook2( + MethodHandle method, Integer thisObject, Object[] arguments, int hookId) { return true; } - @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.System", targetMethod = "gc", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.lang.System", + targetMethod = "gc", targetMethodDescriptor = "()V") - public static Object - invalidReplaceVoidMethod(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static Object invalidReplaceVoidMethod( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return null; } - @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.StringBuilder", - targetMethod = "", targetMethodDescriptor = "(Ljava/lang/String;)V") - public static Object - invalidReturnType(MethodHandle method, Object thisObject, Object[] arguments, int hookId) - throws Throwable { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.StringBuilder", + targetMethod = "", + targetMethodDescriptor = "(Ljava/lang/String;)V") + public static Object invalidReturnType( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable { return null; } - @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", - targetMethod = "startsWith", targetMethodDescriptor = "(Ljava/lang/String;)Z") - public static void - primitiveReturnValueMustBeWrapped(MethodHandle method, String thisObject, Object[] arguments, - int hookId, boolean returnValue) {} + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.lang.String", + targetMethod = "startsWith", + targetMethodDescriptor = "(Ljava/lang/String;)Z") + public static void primitiveReturnValueMustBeWrapped( + MethodHandle method, + String thisObject, + Object[] arguments, + int hookId, + boolean returnValue) {} - @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.StringBuilder", - targetMethod = "", targetMethodDescriptor = "(Ljava/lang/String;)V") - public static void - replaceOnInitWithoutReturnType( + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.lang.StringBuilder", + targetMethod = "", + targetMethodDescriptor = "(Ljava/lang/String;)V") + public static void replaceOnInitWithoutReturnType( MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable {} - @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.StringBuilder", - targetMethod = "", targetMethodDescriptor = "(Ljava/lang/String;)V") - public static Object - replaceOnInitWithIncompatibleType( + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.lang.StringBuilder", + targetMethod = "", + targetMethodDescriptor = "(Ljava/lang/String;)V") + public static Object replaceOnInitWithIncompatibleType( MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable { return new Object(); } @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "equals") - public static void primitiveReturnType(MethodHandle method, String thisObject, Object[] arguments, - int hookId, boolean returnValue) {} + public static void primitiveReturnType( + MethodHandle method, + String thisObject, + Object[] arguments, + int hookId, + boolean returnValue) {} } diff --git a/src/test/java/com/code_intelligence/jazzer/instrumentor/MockTraceDataFlowCallbacks.java b/src/test/java/com/code_intelligence/jazzer/instrumentor/MockTraceDataFlowCallbacks.java index ad659da00..f9d865a9d 100644 --- a/src/test/java/com/code_intelligence/jazzer/instrumentor/MockTraceDataFlowCallbacks.java +++ b/src/test/java/com/code_intelligence/jazzer/instrumentor/MockTraceDataFlowCallbacks.java @@ -29,8 +29,13 @@ public static void init() { public static boolean hookCall(String expectedCall) { if (assertedCalls >= hookCalls.size()) { - System.err.println("Not seen (" + hookCalls.size() + " calls, but " + (assertedCalls + 1) - + " expected): " + expectedCall); + System.err.println( + "Not seen (" + + hookCalls.size() + + " calls, but " + + (assertedCalls + 1) + + " expected): " + + expectedCall); return false; } @@ -44,8 +49,7 @@ public static boolean hookCall(String expectedCall) { } public static boolean finish() { - if (assertedCalls == hookCalls.size()) - return true; + if (assertedCalls == hookCalls.size()) return true; System.err.println("The following calls were not asserted:"); for (int i = assertedCalls; i < hookCalls.size(); i++) { System.err.println(hookCalls.get(i)); diff --git a/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java b/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java index 7e31b77b1..c67eaebe0 100644 --- a/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java +++ b/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java @@ -20,117 +20,138 @@ @SuppressWarnings("unused") public class ReplaceHooks { - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldReturnTrue1") - public static boolean - patchShouldReturnTrue1(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static boolean patchShouldReturnTrue1( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return true; } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldReturnTrue2") - public static Boolean - patchShouldReturnTrue2(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static Boolean patchShouldReturnTrue2( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return true; } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldReturnTrue3") - public static Object - patchShouldReturnTrue3(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static Object patchShouldReturnTrue3( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return true; } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldReturnFalse1") - public static Boolean - patchShouldReturnFalse1(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static Boolean patchShouldReturnFalse1( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return false; } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldReturnFalse2") - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldReturnFalse3") - public static Object - patchShouldReturnFalse2(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static Object patchShouldReturnFalse2( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return false; } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldReturnReversed", targetMethodDescriptor = "(Ljava/lang/String;)Ljava/lang/String;") - public static String - patchShouldReturnReversed( + public static String patchShouldReturnReversed( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return new StringBuilder((String) arguments[0]).reverse().toString(); } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldIncrement") - public static int - patchShouldIncrement(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static int patchShouldIncrement( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return ((int) arguments[0]) + 1; } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", targetMethod = "shouldCallPass") - public static void - patchShouldCallPass(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static void patchShouldCallPass( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { ((ReplaceHooksTargetContract) thisObject).pass("shouldCallPass"); } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget", - targetMethod = "idempotent", targetMethodDescriptor = "(I)I") - public static int - patchIdempotent(MethodHandle method, Object thisObject, Object[] arguments, int hookId) - throws Throwable { + targetMethod = "idempotent", + targetMethodDescriptor = "(I)I") + public static int patchIdempotent( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable { // Iterate the function twice to pass the test. int input = (int) arguments[0]; int temp = (int) method.invokeWithArguments(thisObject, input); return (int) method.invokeWithArguments(thisObject, temp); } - @MethodHook(type = HookType.REPLACE, targetClassName = "java.util.AbstractList", - targetMethod = "get", targetMethodDescriptor = "(I)Ljava/lang/Object;") - public static Object - patchAbstractListGet(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.util.AbstractList", + targetMethod = "get", + targetMethodDescriptor = "(I)Ljava/lang/Object;") + public static Object patchAbstractListGet( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return true; } - @MethodHook(type = HookType.REPLACE, targetClassName = "java.util.Set", targetMethod = "contains", + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.util.Set", + targetMethod = "contains", targetMethodDescriptor = "(Ljava/lang/Object;)Z") - public static boolean - patchSetGet(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + public static boolean patchSetGet( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return true; } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksInit", - targetMethod = "", targetMethodDescriptor = "()V") - public static ReplaceHooksInit - patchInit(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + targetMethod = "", + targetMethodDescriptor = "()V") + public static ReplaceHooksInit patchInit( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { // Test with subclass return new ReplaceHooksInit() { - { initialized = true; } + { + initialized = true; + } }; } - @MethodHook(type = HookType.REPLACE, + @MethodHook( + type = HookType.REPLACE, targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksInit", - targetMethod = "", targetMethodDescriptor = "(ZLjava/lang/String;)V") - public static ReplaceHooksInit - patchInitWithParams(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + targetMethod = "", + targetMethodDescriptor = "(ZLjava/lang/String;)V") + public static ReplaceHooksInit patchInitWithParams( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { return new ReplaceHooksInit(true, ""); } } diff --git a/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java b/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java index a919242b5..9e3a88d3c 100644 --- a/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java +++ b/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java @@ -24,22 +24,33 @@ public static void validBeforeHook( MethodHandle method, String thisObject, Object[] arguments, int hookId) {} @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "equals") - public static void validAfterHook(MethodHandle method, String thisObject, Object[] arguments, - int hookId, Boolean returnValue) {} + public static void validAfterHook( + MethodHandle method, + String thisObject, + Object[] arguments, + int hookId, + Boolean returnValue) {} - @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.String", - targetMethod = "equals", targetMethodDescriptor = "(Ljava/lang/Object;)Z") - public static Boolean - validReplaceHook(MethodHandle method, String thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.lang.String", + targetMethod = "equals", + targetMethodDescriptor = "(Ljava/lang/Object;)Z") + public static Boolean validReplaceHook( + MethodHandle method, String thisObject, Object[] arguments, int hookId) { return true; } @MethodHook( - type = HookType.REPLACE, targetClassName = "java.lang.String", targetMethod = "equals") - @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.String", + type = HookType.REPLACE, + targetClassName = "java.lang.String", + targetMethod = "equals") + @MethodHook( + type = HookType.REPLACE, + targetClassName = "java.lang.String", targetMethod = "equalsIgnoreCase") - public static boolean - validReplaceHook2(MethodHandle method, String thisObject, Object[] arguments, int hookId) { + public static boolean validReplaceHook2( + MethodHandle method, String thisObject, Object[] arguments, int hookId) { return true; } } diff --git a/src/test/java/com/code_intelligence/jazzer/junit/AutofuzzTest.java b/src/test/java/com/code_intelligence/jazzer/junit/AutofuzzTest.java index b9abd3fea..3744043cb 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/AutofuzzTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/AutofuzzTest.java @@ -39,8 +39,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -63,15 +61,18 @@ public void fuzzingEnabled() throws IOException { // crash file into it. Path testResourceDir = baseDir.resolve("src").resolve("test").resolve("resources"); Files.createDirectories(testResourceDir); - Path inputsDirectory = testResourceDir.resolve("com") - .resolve("example") - .resolve("AutofuzzFuzzTestInputs") - .resolve("autofuzz"); + Path inputsDirectory = + testResourceDir + .resolve("com") + .resolve("example") + .resolve("AutofuzzFuzzTestInputs") + .resolve("autofuzz"); EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") - .selectors(selectMethod( - "com.example.AutofuzzFuzzTest#autofuzz(java.lang.String,com.example.AutofuzzFuzzTest$IntHolder)")) + .selectors( + selectMethod( + "com.example.AutofuzzFuzzTest#autofuzz(java.lang.String,com.example.AutofuzzFuzzTest$IntHolder)")) .configurationParameter("jazzer.internal.basedir", baseDir.toAbsolutePath().toString()) .execute(); @@ -81,31 +82,48 @@ public void fuzzingEnabled() throws IOException { "test-template:autofuzz(java.lang.String, com.example.AutofuzzFuzzTest$IntHolder)"; final String invocation = "test-template-invocation:#"; - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(engine)), - event(type(STARTED), container(uniqueIdSubstrings(engine, clazz))), - event(type(STARTED), container(uniqueIdSubstrings(engine, clazz, autofuzz))), - event(type(FINISHED), container(uniqueIdSubstrings(engine, clazz, autofuzz)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(engine, clazz)), finishedSuccessfully()), - event(type(FINISHED), container(engine), finishedSuccessfully())); - - results.testEvents().assertEventsMatchExactly(event(type(DYNAMIC_TEST_REGISTERED)), - event(type(STARTED)), - event(test(uniqueIdSubstrings(engine, clazz, autofuzz, invocation + 1)), - displayName(""), - abortedWithReason(instanceOf(TestAbortedException.class))), - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(engine, clazz, autofuzz))), - event(type(STARTED), test(uniqueIdSubstrings(engine, clazz, autofuzz, invocation + 2)), - displayName("Fuzzing...")), - event(type(FINISHED), test(uniqueIdSubstrings(engine, clazz, autofuzz, invocation + 2)), - displayName("Fuzzing..."), finishedWithFailure(instanceOf(RuntimeException.class)))); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(engine)), + event(type(STARTED), container(uniqueIdSubstrings(engine, clazz))), + event(type(STARTED), container(uniqueIdSubstrings(engine, clazz, autofuzz))), + event( + type(FINISHED), + container(uniqueIdSubstrings(engine, clazz, autofuzz)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(engine, clazz)), + finishedSuccessfully()), + event(type(FINISHED), container(engine), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchExactly( + event(type(DYNAMIC_TEST_REGISTERED)), + event(type(STARTED)), + event( + test(uniqueIdSubstrings(engine, clazz, autofuzz, invocation + 1)), + displayName(""), + abortedWithReason(instanceOf(TestAbortedException.class))), + event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(engine, clazz, autofuzz))), + event( + type(STARTED), + test(uniqueIdSubstrings(engine, clazz, autofuzz, invocation + 2)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(engine, clazz, autofuzz, invocation + 2)), + displayName("Fuzzing..."), + finishedWithFailure(instanceOf(RuntimeException.class)))); // Should crash on an input that contains "jazzer", with the crash emitted into the // automatically created inputs directory. Path crashingInput; try (Stream crashFiles = - Files.list(inputsDirectory) - .filter(path -> path.getFileName().toString().startsWith("crash-"))) { + Files.list(inputsDirectory) + .filter(path -> path.getFileName().toString().startsWith("crash-"))) { List crashFilesList = crashFiles.collect(Collectors.toList()); assertWithMessage("Expected crashing input in " + baseDir).that(crashFilesList).hasSize(1); crashingInput = crashFilesList.get(0); @@ -133,30 +151,45 @@ public void fuzzingDisabled() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") - .selectors(selectMethod( - "com.example.AutofuzzWithCorpusFuzzTest#autofuzzWithCorpus(java.lang.String,int)")) + .selectors( + selectMethod( + "com.example.AutofuzzWithCorpusFuzzTest#autofuzzWithCorpus(java.lang.String,int)")) .execute(); final String engine = "engine:junit-jupiter"; final String clazz = "class:com.example.AutofuzzWithCorpusFuzzTest"; final String autofuzzWithCorpus = "test-template:autofuzzWithCorpus(java.lang.String, int)"; - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(engine)), - event(type(STARTED), container(uniqueIdSubstrings(engine, clazz))), - event(type(STARTED), container(uniqueIdSubstrings(engine, clazz, autofuzzWithCorpus))), - // "No fuzzing has been performed..." - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(engine, clazz, autofuzzWithCorpus))), - event(type(FINISHED), container(uniqueIdSubstrings(engine, clazz, autofuzzWithCorpus)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(engine, clazz)), finishedSuccessfully()), - event(type(FINISHED), container(engine), finishedSuccessfully())); - - results.testEvents().assertEventsMatchExactly(event(type(DYNAMIC_TEST_REGISTERED)), - event(type(STARTED)), - event(test("autofuzzWithCorpus", ""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED)), event(type(STARTED)), - event(test("autofuzzWithCorpus", "crashing_input"), - finishedWithFailure(instanceOf(RuntimeException.class)))); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(engine)), + event(type(STARTED), container(uniqueIdSubstrings(engine, clazz))), + event(type(STARTED), container(uniqueIdSubstrings(engine, clazz, autofuzzWithCorpus))), + // "No fuzzing has been performed..." + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(engine, clazz, autofuzzWithCorpus))), + event( + type(FINISHED), + container(uniqueIdSubstrings(engine, clazz, autofuzzWithCorpus)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(engine, clazz)), + finishedSuccessfully()), + event(type(FINISHED), container(engine), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchExactly( + event(type(DYNAMIC_TEST_REGISTERED)), + event(type(STARTED)), + event(test("autofuzzWithCorpus", ""), finishedSuccessfully()), + event(type(DYNAMIC_TEST_REGISTERED)), + event(type(STARTED)), + event( + test("autofuzzWithCorpus", "crashing_input"), + finishedWithFailure(instanceOf(RuntimeException.class)))); } } diff --git a/src/test/java/com/code_intelligence/jazzer/junit/CorpusDirectoryTest.java b/src/test/java/com/code_intelligence/jazzer/junit/CorpusDirectoryTest.java index 372718efb..b8ea85f58 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/CorpusDirectoryTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/CorpusDirectoryTest.java @@ -34,7 +34,6 @@ import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -68,8 +67,16 @@ public void fuzzingEnabled() throws IOException { // Create a fake test resource directory structure with an inputs directory to verify that // Jazzer uses it and emits a crash file into it. - Path artifactsDirectory = baseDir.resolve(Paths.get("src", "test", "resources", "com", - "example", "CorpusDirectoryFuzzTestInputs", "corpusDirectoryFuzz")); + Path artifactsDirectory = + baseDir.resolve( + Paths.get( + "src", + "test", + "resources", + "com", + "example", + "CorpusDirectoryFuzzTestInputs", + "corpusDirectoryFuzz")); Files.createDirectories(artifactsDirectory); // An explicitly stated corpus directory should be used to save new corpus entries. @@ -78,8 +85,10 @@ public void fuzzingEnabled() throws IOException { // The default generated corpus directory should only be used if no explicit corpus directory // is given. - Path defaultGeneratedCorpus = baseDir.resolve( - Paths.get(".cifuzz-corpus", "com.example.CorpusDirectoryFuzzTest", "corpusDirectoryFuzz")); + Path defaultGeneratedCorpus = + baseDir.resolve( + Paths.get( + ".cifuzz-corpus", "com.example.CorpusDirectoryFuzzTest", "corpusDirectoryFuzz")); EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") @@ -91,29 +100,59 @@ public void fuzzingEnabled() throws IOException { "jazzer.internal.arg.1", explicitGeneratedCorpus.toAbsolutePath().toString()) .execute(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - - results.testEvents().assertEventsMatchExactly( - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), - displayName("seed"), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), - displayName("Fuzzing...")), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), - displayName("Fuzzing..."), - finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), + displayName("seed"), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), + displayName("Fuzzing..."), + finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); // Crash file should be emitted into the artifacts directory and not into corpus directory. assertCrashFileExistsIn(artifactsDirectory); @@ -144,39 +183,62 @@ public void fuzzingDisabled() throws IOException { "jazzer.internal.arg.1", corpusDirectory.toAbsolutePath().toString()) .execute(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); // Verify that corpus_entry is not picked up and corpus directory is ignored in regression mode. - results.testEvents().assertEventsMatchExactly( - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), - displayName("seed"), finishedSuccessfully())); + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), + displayName("seed"), + finishedSuccessfully())); } private static void assertCrashFileExistsIn(Path artifactsDirectory) throws IOException { try (Stream crashFiles = - Files.list(artifactsDirectory) - .filter(path -> path.getFileName().toString().startsWith("crash-"))) { + Files.list(artifactsDirectory) + .filter(path -> path.getFileName().toString().startsWith("crash-"))) { assertThat(crashFiles).isNotEmpty(); } } private static void assertNoCrashFileExistsIn(Path generatedCorpus) throws IOException { try (Stream crashFiles = - Files.list(generatedCorpus) - .filter(path -> path.getFileName().toString().startsWith("crash-"))) { + Files.list(generatedCorpus) + .filter(path -> path.getFileName().toString().startsWith("crash-"))) { assertThat(crashFiles).isEmpty(); } } diff --git a/src/test/java/com/code_intelligence/jazzer/junit/DirectoryInputsTest.java b/src/test/java/com/code_intelligence/jazzer/junit/DirectoryInputsTest.java index 7ef27a374..258d66da7 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/DirectoryInputsTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/DirectoryInputsTest.java @@ -68,8 +68,16 @@ public void fuzzingEnabled() throws IOException { // Create a fake test resource directory structure with an inputs directory to verify that // Jazzer uses it and emits a crash file into it. - Path inputsDirectory = baseDir.resolve(Paths.get("src", "test", "resources", "com", "example", - "DirectoryInputsFuzzTestInputs", "inputsFuzz")); + Path inputsDirectory = + baseDir.resolve( + Paths.get( + "src", + "test", + "resources", + "com", + "example", + "DirectoryInputsFuzzTestInputs", + "inputsFuzz")); Files.createDirectories(inputsDirectory); EngineExecutionResults results = @@ -78,48 +86,81 @@ public void fuzzingEnabled() throws IOException { .configurationParameter("jazzer.internal.basedir", baseDir.toAbsolutePath().toString()) .execute(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - - results.testEvents().assertEventsMatchExactly( - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), - displayName("seed"), finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class))), - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), - displayName("Fuzzing...")), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), - displayName("Fuzzing..."), - finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), + displayName("seed"), + finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), + displayName("Fuzzing..."), + finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); // Should crash on the exact input "directory" as provided by the seed, with the crash emitted // into the seed corpus. - try (Stream crashFiles = Files.list(baseDir).filter( - path -> path.getFileName().toString().startsWith("crash-"))) { + try (Stream crashFiles = + Files.list(baseDir).filter(path -> path.getFileName().toString().startsWith("crash-"))) { assertThat(crashFiles).isEmpty(); } try (Stream seeds = Files.list(inputsDirectory)) { - assertThat(seeds).containsExactly( - inputsDirectory.resolve("crash-8d392f56d616a516ceabb82ed8906418bce4647d")); + assertThat(seeds) + .containsExactly( + inputsDirectory.resolve("crash-8d392f56d616a516ceabb82ed8906418bce4647d")); } - assertThat(Files.readAllBytes( - inputsDirectory.resolve("crash-8d392f56d616a516ceabb82ed8906418bce4647d"))) + assertThat( + Files.readAllBytes( + inputsDirectory.resolve("crash-8d392f56d616a516ceabb82ed8906418bce4647d"))) .isEqualTo("directory".getBytes(StandardCharsets.UTF_8)); // Verify that the engine created the generated corpus directory. Since the crash was found on a // seed, it should be empty. - Path generatedCorpus = baseDir.resolve( - Paths.get(".cifuzz-corpus", "com.example.DirectoryInputsFuzzTest", "inputsFuzz")); + Path generatedCorpus = + baseDir.resolve( + Paths.get(".cifuzz-corpus", "com.example.DirectoryInputsFuzzTest", "inputsFuzz")); assertThat(Files.isDirectory(generatedCorpus)).isTrue(); try (Stream entries = Files.list(generatedCorpus)) { assertThat(entries).isEmpty(); @@ -135,24 +176,47 @@ public void fuzzingDisabled() { .selectors(selectClass("com.example.DirectoryInputsFuzzTest")) .execute(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - - results.testEvents().assertEventsMatchExactly( - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), - displayName("seed"), finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), + displayName("seed"), + finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); // Verify that the generated corpus directory hasn't been created. Path generatedCorpus = diff --git a/src/test/java/com/code_intelligence/jazzer/junit/FindingsBaseDirTest.java b/src/test/java/com/code_intelligence/jazzer/junit/FindingsBaseDirTest.java index b31001400..d769adb8f 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/FindingsBaseDirTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/FindingsBaseDirTest.java @@ -62,16 +62,25 @@ public void fuzzingEnabledNoFindingsDir() throws IOException { .configurationParameter("jazzer.internal.basedir", baseDir.toAbsolutePath().toString()) .execute(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - // Warning because the inputs directory hasn't been found in the source tree. - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + // Warning because the inputs directory hasn't been found in the source tree. + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); // Crash should be emitted into the base directory, as no findings dir available. try (Stream baseDirFiles = Files.list(baseDir)) { diff --git a/src/test/java/com/code_intelligence/jazzer/junit/FuzzingWithCrashTest.java b/src/test/java/com/code_intelligence/jazzer/junit/FuzzingWithCrashTest.java index 5cc2d1c48..8ef73704b 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/FuzzingWithCrashTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/FuzzingWithCrashTest.java @@ -74,8 +74,9 @@ public void setup() throws IOException { baseDir = temp.getRoot().toPath(); // Create a fake test resource directory structure with an inputs directory to verify that // Jazzer uses it and emits a crash file into it. - inputsDirectory = baseDir.resolve( - Paths.get("src", "test", "resources", "com", "example", "ValidFuzzTestsInputs")); + inputsDirectory = + baseDir.resolve( + Paths.get("src", "test", "resources", "com", "example", "ValidFuzzTestsInputs")); // populate the same seed in all test directories for (String method : Arrays.asList(BYTE_FUZZ.getName(), NO_CRASH_FUZZ.getName(), DATA_FUZZ.getName())) { @@ -101,36 +102,51 @@ public void fuzzingEnabled() throws IOException { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId()))), - event(type(FINISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId())), - finishedSuccessfully()), - event(type(SKIPPED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ.getDescriptorId()))), - event(type(SKIPPED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, DATA_FUZZ.getDescriptorId()))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - - results.testEvents().assertEventsMatchLooselyInOrder( - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId()))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId(), INVOCATION)), - displayName("Fuzzing...")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId(), INVOCATION)), - displayName("Fuzzing..."), - finishedWithFailure(instanceOf(AssertionFailedError.class)))); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event( + type(STARTED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId()))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId())), + finishedSuccessfully()), + event( + type(SKIPPED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ.getDescriptorId()))), + event( + type(SKIPPED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, DATA_FUZZ.getDescriptorId()))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchLooselyInOrder( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId()))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId(), INVOCATION)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId(), INVOCATION)), + displayName("Fuzzing..."), + finishedWithFailure(instanceOf(AssertionFailedError.class)))); // Jazzer first tries the empty input, which doesn't crash the ByteFuzzTest. The second input is // the seed we planted, which is crashing, so verify that a crash file with the same content is // created in our fake seed corpus, but not in the current working directory. - try (Stream crashFiles = Files.list(baseDir).filter( - path -> path.getFileName().toString().startsWith("crash-"))) { + try (Stream crashFiles = + Files.list(baseDir).filter(path -> path.getFileName().toString().startsWith("crash-"))) { assertThat(crashFiles).isEmpty(); } @@ -138,9 +154,10 @@ public void fuzzingEnabled() throws IOException { // byteFuzz and will not exist in the directories of the other tests Path byteFuzzInputDirectory = inputsDirectory.resolve(BYTE_FUZZ.getName()); try (Stream seeds = Files.list(byteFuzzInputDirectory)) { - assertThat(seeds).containsExactly( - byteFuzzInputDirectory.resolve("crash-" + CRASHING_SEED_DIGEST), - byteFuzzInputDirectory.resolve(CRASHING_SEED_NAME)); + assertThat(seeds) + .containsExactly( + byteFuzzInputDirectory.resolve("crash-" + CRASHING_SEED_DIGEST), + byteFuzzInputDirectory.resolve(CRASHING_SEED_NAME)); } assertThat(Files.readAllBytes(byteFuzzInputDirectory.resolve("crash-" + CRASHING_SEED_DIGEST))) .isEqualTo(CRASHING_SEED_CONTENT); @@ -169,29 +186,44 @@ public void fuzzingDisabled() throws IOException { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId()))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId()))), - event(type(FINISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId())), - finishedSuccessfully()), - event(type(STARTED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ.getDescriptorId()))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ.getDescriptorId()))), - event(type(FINISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ.getDescriptorId()))), - event(type(STARTED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, DATA_FUZZ.getDescriptorId()))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, DATA_FUZZ.getDescriptorId()))), - event(type(FINISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, DATA_FUZZ.getDescriptorId()))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event( + type(STARTED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId()))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId()))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, BYTE_FUZZ.getDescriptorId())), + finishedSuccessfully()), + event( + type(STARTED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ.getDescriptorId()))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ.getDescriptorId()))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ.getDescriptorId()))), + event( + type(STARTED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, DATA_FUZZ.getDescriptorId()))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, DATA_FUZZ.getDescriptorId()))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, DATA_FUZZ.getDescriptorId()))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); // No fuzzing means no crashes means no new seeds. // Check against all methods' input directories diff --git a/src/test/java/com/code_intelligence/jazzer/junit/FuzzingWithoutCrashTest.java b/src/test/java/com/code_intelligence/jazzer/junit/FuzzingWithoutCrashTest.java index 01fe62529..c2d48746b 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/FuzzingWithoutCrashTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/FuzzingWithoutCrashTest.java @@ -23,18 +23,14 @@ import static org.junit.platform.testkit.engine.EventConditions.displayName; import static org.junit.platform.testkit.engine.EventConditions.event; import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully; -import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; import static org.junit.platform.testkit.engine.EventConditions.test; import static org.junit.platform.testkit.engine.EventConditions.type; import static org.junit.platform.testkit.engine.EventConditions.uniqueIdSubstrings; import static org.junit.platform.testkit.engine.EventType.DYNAMIC_TEST_REGISTERED; import static org.junit.platform.testkit.engine.EventType.FINISHED; import static org.junit.platform.testkit.engine.EventType.REPORTING_ENTRY_PUBLISHED; -import static org.junit.platform.testkit.engine.EventType.SKIPPED; import static org.junit.platform.testkit.engine.EventType.STARTED; -import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; -import com.google.common.truth.Truth8; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -47,11 +43,7 @@ import org.junit.Test; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; -import org.junit.platform.testkit.engine.Event; -import org.junit.platform.testkit.engine.EventType; -import org.junit.platform.testkit.engine.Events; import org.junit.rules.TemporaryFolder; -import org.opentest4j.AssertionFailedError; public class FuzzingWithoutCrashTest { private static final String ENGINE = "engine:junit-jupiter"; @@ -81,27 +73,46 @@ public void fuzzingEnabled() throws IOException { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), - // Warning because the inputs directory hasn't been found in the source tree. - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), - // Warning because the inputs directory has been found on the classpath, but only in a JAR. - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - - results.testEvents().assertEventsMatchLooselyInOrder( - event( - type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ, INVOCATION)), - displayName("Fuzzing...")), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ, INVOCATION)), - displayName("Fuzzing..."), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), + // Warning because the inputs directory hasn't been found in the source tree. + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), + // Warning because the inputs directory has been found on the classpath, but only in a + // JAR. + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchLooselyInOrder( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ, INVOCATION)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ, INVOCATION)), + displayName("Fuzzing..."), + finishedSuccessfully())); // Verify that the engine created the generated corpus directory. As the fuzz test produces // coverage (but no crash), it should not be empty. @@ -119,27 +130,45 @@ public void fuzzingDisabled() { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - - results.testEvents().assertEventsMatchExactly( - IntStream.rangeClosed(1, 6) - .boxed() - .flatMap(i - -> Stream.of(event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ, INVOCATION + i))), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ, INVOCATION + i)), - finishedSuccessfully()))) - .toArray(Condition[] ::new)); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), + event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchExactly( + IntStream.rangeClosed(1, 6) + .boxed() + .flatMap( + i -> + Stream.of( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, NO_CRASH_FUZZ))), + event( + type(STARTED), + test( + uniqueIdSubstrings( + ENGINE, CLAZZ, NO_CRASH_FUZZ, INVOCATION + i))), + event( + type(FINISHED), + test( + uniqueIdSubstrings( + ENGINE, CLAZZ, NO_CRASH_FUZZ, INVOCATION + i)), + finishedSuccessfully()))) + .toArray(Condition[]::new)); // Verify that the generated corpus directory hasn't been created. Path generatedCorpus = diff --git a/src/test/java/com/code_intelligence/jazzer/junit/HermeticInstrumentationTest.java b/src/test/java/com/code_intelligence/jazzer/junit/HermeticInstrumentationTest.java index dabbf352f..a2abfe2a4 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/HermeticInstrumentationTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/HermeticInstrumentationTest.java @@ -72,37 +72,63 @@ public void fuzzingDisabled() { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchLoosely(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchLoosely( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1))), + event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2))), + event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - results.testEvents().assertEventsMatchLoosely( - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1, INVOCATION)), - displayName("")), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1, INVOCATION)), - displayName(""), - finishedWithFailure(instanceOf(FuzzerSecurityIssueLow.class))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, UNIT_TEST_1))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, UNIT_TEST_1)), - finishedWithFailure(instanceOf(PatternSyntaxException.class))), - event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2, INVOCATION)), - displayName("")), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2, INVOCATION)), - displayName(""), - finishedWithFailure(instanceOf(FuzzerSecurityIssueLow.class))), - event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, UNIT_TEST_2))), - event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, UNIT_TEST_2)), - finishedWithFailure(instanceOf(PatternSyntaxException.class)))); + results + .testEvents() + .assertEventsMatchLoosely( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1, INVOCATION)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_1, INVOCATION)), + displayName(""), + finishedWithFailure(instanceOf(FuzzerSecurityIssueLow.class))), + event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, UNIT_TEST_1))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, UNIT_TEST_1)), + finishedWithFailure(instanceOf(PatternSyntaxException.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2, INVOCATION)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, FUZZ_TEST_2, INVOCATION)), + displayName(""), + finishedWithFailure(instanceOf(FuzzerSecurityIssueLow.class))), + event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, UNIT_TEST_2))), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, UNIT_TEST_2)), + finishedWithFailure(instanceOf(PatternSyntaxException.class)))); } } diff --git a/src/test/java/com/code_intelligence/jazzer/junit/LifecycleTest.java b/src/test/java/com/code_intelligence/jazzer/junit/LifecycleTest.java index 8a682ad31..33ebaf34e 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/LifecycleTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/LifecycleTest.java @@ -32,9 +32,7 @@ import static org.junit.platform.testkit.engine.EventType.STARTED; import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; -import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; import com.example.TestSuccessfulException; -import java.io.IOException; import java.nio.file.Path; import org.junit.Before; import org.junit.Rule; @@ -72,36 +70,54 @@ public void fuzzingEnabled() { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(SKIPPED), container(uniqueIdSubstrings(ENGINE, CLAZZ, DISABLED_FUZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - // Warning because the seed corpus directory hasn't been found. - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), - finishedWithFailure(instanceOf(TestSuccessfulException.class))), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(SKIPPED), container(uniqueIdSubstrings(ENGINE, CLAZZ, DISABLED_FUZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + // Warning because the seed corpus directory hasn't been found. + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedWithFailure(instanceOf(TestSuccessfulException.class))), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - results.testEvents().assertEventsMatchExactly( - event( - type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event( - type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), - displayName("Fuzzing...")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), - displayName("Fuzzing..."), finishedSuccessfully())); + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), + displayName("Fuzzing..."), + finishedSuccessfully())); } @Test @@ -110,25 +126,37 @@ public void fuzzingDisabled() { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(SKIPPED), container(uniqueIdSubstrings(ENGINE, CLAZZ, DISABLED_FUZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), - finishedWithFailure(instanceOf(TestSuccessfulException.class))), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(SKIPPED), container(uniqueIdSubstrings(ENGINE, CLAZZ, DISABLED_FUZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedWithFailure(instanceOf(TestSuccessfulException.class))), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - results.testEvents().assertEventsMatchExactly( - event( - type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully())); + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully())); } } diff --git a/src/test/java/com/code_intelligence/jazzer/junit/MutatorTest.java b/src/test/java/com/code_intelligence/jazzer/junit/MutatorTest.java index 4a1ef3df8..13ab80730 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/MutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/MutatorTest.java @@ -22,7 +22,6 @@ import static org.junit.platform.testkit.engine.EventConditions.event; import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully; import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; -import static org.junit.platform.testkit.engine.EventConditions.reportEntry; import static org.junit.platform.testkit.engine.EventConditions.test; import static org.junit.platform.testkit.engine.EventConditions.type; import static org.junit.platform.testkit.engine.EventConditions.uniqueIdSubstrings; @@ -54,7 +53,8 @@ public class MutatorTest { private static final String INVOCATION = "test-template-invocation:#"; private static final String INVALID_SIGNATURE_ENTRY = "Some files in the seed corpus do not match the fuzz target signature.\n" - + "This indicates that they were generated with a different signature and may cause issues reproducing previous findings."; + + "This indicates that they were generated with a different signature and may cause" + + " issues reproducing previous findings."; @Rule public TemporaryFolder temp = new TemporaryFolder(); private Path baseDir; @@ -62,8 +62,16 @@ public class MutatorTest { @Before public void setup() throws IOException { baseDir = temp.getRoot().toPath(); - Path inputsDirectory = baseDir.resolve(Paths.get( - "src", "test", "resources", "com", "example", "MutatorFuzzTestInputs", "mutatorFuzz")); + Path inputsDirectory = + baseDir.resolve( + Paths.get( + "src", + "test", + "resources", + "com", + "example", + "MutatorFuzzTestInputs", + "mutatorFuzz")); Files.createDirectories(inputsDirectory); Files.write(inputsDirectory.resolve("invalid"), "invalid input".getBytes()); } @@ -83,46 +91,70 @@ public void fuzzingEnabled() { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - // Invalid corpus input warning - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ)), - new Condition<>( - Event.byPayload(ReportEntry.class, - (it) -> it.getKeyValuePairs().values().contains(INVALID_SIGNATURE_ENTRY)), - "has invalid signature entry reporting entry")), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + // Invalid corpus input warning + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ)), + new Condition<>( + Event.byPayload( + ReportEntry.class, + (it) -> it.getKeyValuePairs().values().contains(INVALID_SIGNATURE_ENTRY)), + "has invalid signature entry reporting entry")), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - results.testEvents().assertEventsMatchExactly( - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), - displayName("invalid")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), - displayName("invalid"), finishedSuccessfully()), - event( - type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 3)), - displayName("Fuzzing...")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 3)), - displayName("Fuzzing..."), finishedWithFailure(instanceOf(AssertionError.class)))); + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), + displayName("invalid")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), + displayName("invalid"), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 3)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 3)), + displayName("Fuzzing..."), + finishedWithFailure(instanceOf(AssertionError.class)))); } @Test @@ -131,35 +163,53 @@ public void fuzzingDisabled() { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - // Deactivated fuzzing warning - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - // Invalid corpus input warning - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + // Deactivated fuzzing warning + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + // Invalid corpus input warning + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - results.testEvents().assertEventsMatchExactly( - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), - displayName("invalid")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), - displayName("invalid"), finishedSuccessfully())); + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), + displayName("invalid")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, LIFECYCLE_FUZZ, INVOCATION + 2)), + displayName("invalid"), + finishedSuccessfully())); } } diff --git a/src/test/java/com/code_intelligence/jazzer/junit/RegressionTestTest.java b/src/test/java/com/code_intelligence/jazzer/junit/RegressionTestTest.java index 586727778..f7722c3f7 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/RegressionTestTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/RegressionTestTest.java @@ -59,7 +59,8 @@ public class RegressionTestTest { private static final String INVALID_PARAMETER_COUNT_FUZZ = "test-template:invalidParameterCountFuzz()"; private static final String PARAMETER_RESOLVER_FUZZ = - "test-template:parameterResolverFuzz(com.code_intelligence.jazzer.api.FuzzedDataProvider, org.junit.jupiter.api.TestInfo)"; + "test-template:parameterResolverFuzz(com.code_intelligence.jazzer.api.FuzzedDataProvider," + + " org.junit.jupiter.api.TestInfo)"; private static final String AUTOFUZZ_WITH_CORPUS = "test-template:autofuzzWithCorpus(java.lang.String, int)"; private static final String INVOCATION = "test-template-invocation:#"; @@ -90,175 +91,258 @@ public void regressionTestEnabled() { for (String line : stderrLines) { System.err.println(line); } - assertThat(Arrays.stream(stderrLines) - .filter(line -> line.startsWith("WARN:") || line.startsWith("ERROR:"))) + assertThat( + Arrays.stream(stderrLines) + .filter(line -> line.startsWith("WARN:") || line.startsWith("ERROR:"))) .isEmpty(); - results.containerEvents().debug().assertEventsMatchLoosely( - event(type(STARTED), container(ENGINE)), - event( - type(STARTED), container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, NO_CRASH_FUZZ))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, NO_CRASH_FUZZ))), - event(type(FINISHED), - container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, NO_CRASH_FUZZ)), - finishedSuccessfully()), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS)), - finishedSuccessfully()), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST))), - event(type(STARTED), - container( - uniqueIdSubstrings(ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS))), - event(type(REPORTING_ENTRY_PUBLISHED), - container( - uniqueIdSubstrings(ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS))), - event(type(FINISHED), - container( - uniqueIdSubstrings(ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST)), - finishedSuccessfully()), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ))), - event(type(REPORTING_ENTRY_PUBLISHED), - container(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ)), - finishedSuccessfully()), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS))), - event(type(STARTED), - container( - uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS, INVALID_PARAMETER_COUNT_FUZZ))), - event(type(FINISHED), - container(uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS, INVALID_PARAMETER_COUNT_FUZZ)), - finishedWithFailure(instanceOf(FuzzTestConfigurationError.class), - message("Methods annotated with @FuzzTest must take at least one parameter"))), - event(type(FINISHED), - container(uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS, PARAMETER_RESOLVER_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS)), - finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + results + .containerEvents() + .debug() + .assertEventsMatchLoosely( + event(type(STARTED), container(ENGINE)), + event( + type(STARTED), + container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, NO_CRASH_FUZZ))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, NO_CRASH_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, NO_CRASH_FUZZ)), + finishedSuccessfully()), + event( + type(STARTED), container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS)), + finishedSuccessfully()), + event( + type(STARTED), + container(uniqueIdSubstrings(ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST))), + event( + type(STARTED), + container( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS))), + event( + type(FINISHED), + container( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST)), + finishedSuccessfully()), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ))), + event( + type(REPORTING_ENTRY_PUBLISHED), + container(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ)), + finishedSuccessfully()), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS))), + event( + type(STARTED), + container( + uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS, INVALID_PARAMETER_COUNT_FUZZ))), + event( + type(FINISHED), + container( + uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS, INVALID_PARAMETER_COUNT_FUZZ)), + finishedWithFailure( + instanceOf(FuzzTestConfigurationError.class), + message("Methods annotated with @FuzzTest must take at least one parameter"))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS, PARAMETER_RESOLVER_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, INVALID_FUZZ_TESTS)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - results.testEvents().debug().assertEventsMatchLoosely( - event(type(FINISHED), - test(uniqueIdSubstrings( - ENGINE, INVALID_FUZZ_TESTS, PARAMETER_RESOLVER_FUZZ, INVOCATION)), - displayName(""), - finishedWithFailure(instanceOf(FuzzTestConfigurationError.class))), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName(""), - finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class))), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("no_crash")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("no_crash")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("no_crash"), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("assert")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("assert")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("assert"), finishedWithFailure(instanceOf(AssertionFailedError.class))), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("honeypot")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("honeypot")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("honeypot"), - finishedWithFailure(instanceOf(FuzzerSecurityIssueHigh.class))), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("sanitizer_internal_class")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("sanitizer_internal_class")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("sanitizer_internal_class"), - finishedWithFailure(instanceOf(FuzzerSecurityIssueCritical.class))), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("sanitizer_user_class")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("sanitizer_user_class")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), - displayName("sanitizer_user_class"), - finishedWithFailure(instanceOf(FuzzerSecurityIssueLow.class))), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName("")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName("succeeds")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName("succeeds")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName("succeeds"), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName("fails")), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName("fails")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), - displayName("fails"), finishedWithFailure(instanceOf(AssertionFailedError.class))), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings( - ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), - displayName("")), - event(type(STARTED), - test(uniqueIdSubstrings( - ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings( - ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings( - ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), - displayName("crashing_input")), - event(type(STARTED), - test(uniqueIdSubstrings( - ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), - displayName("crashing_input")), - event(type(FINISHED), - test(uniqueIdSubstrings( - ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), - displayName("crashing_input"), - finishedWithFailure(instanceOf(RuntimeException.class)))); + results + .testEvents() + .debug() + .assertEventsMatchLoosely( + event( + type(FINISHED), + test( + uniqueIdSubstrings( + ENGINE, INVALID_FUZZ_TESTS, PARAMETER_RESOLVER_FUZZ, INVOCATION)), + displayName(""), + finishedWithFailure(instanceOf(FuzzTestConfigurationError.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName(""), + finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("no_crash")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("no_crash")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("no_crash"), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("assert")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("assert")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("assert"), + finishedWithFailure(instanceOf(AssertionFailedError.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("honeypot")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("honeypot")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("honeypot"), + finishedWithFailure(instanceOf(FuzzerSecurityIssueHigh.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("sanitizer_internal_class")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("sanitizer_internal_class")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("sanitizer_internal_class"), + finishedWithFailure(instanceOf(FuzzerSecurityIssueCritical.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("sanitizer_user_class")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("sanitizer_user_class")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, VALID_FUZZ_TESTS, DATA_FUZZ, INVOCATION)), + displayName("sanitizer_user_class"), + finishedWithFailure(instanceOf(FuzzerSecurityIssueLow.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName("")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName("succeeds")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName("succeeds")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName("succeeds"), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName("fails")), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName("fails")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, BYTE_FUZZ_TEST, BYTE_FUZZ, INVOCATION)), + displayName("fails"), + finishedWithFailure(instanceOf(AssertionFailedError.class))), + event( + type(DYNAMIC_TEST_REGISTERED), + test( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), + displayName("")), + event( + type(STARTED), + test( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), + displayName("")), + event( + type(FINISHED), + test( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), + displayName("crashing_input")), + event( + type(STARTED), + test( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), + displayName("crashing_input")), + event( + type(FINISHED), + test( + uniqueIdSubstrings( + ENGINE, AUTOFUZZ_WITH_CORPUS_FUZZ_TEST, AUTOFUZZ_WITH_CORPUS, INVOCATION)), + displayName("crashing_input"), + finishedWithFailure(instanceOf(RuntimeException.class)))); } } diff --git a/src/test/java/com/code_intelligence/jazzer/junit/TestMethod.java b/src/test/java/com/code_intelligence/jazzer/junit/TestMethod.java index bb542ccf0..8f5871bf1 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/TestMethod.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/TestMethod.java @@ -22,10 +22,9 @@ /** * Small class that allows us to capture the methods that we're using as test data. We need similar - * but slightly different data at various points: - * 1. the method name with parameters for finding the method initially and for referring to it in - * JUnit - * 2. the method name without parameters for the findings directories + * but slightly different data at various points: 1. the method name with parameters for finding the + * method initially and for referring to it in JUnit 2. the method name without parameters for the + * findings directories */ public class TestMethod { Method method; @@ -36,16 +35,12 @@ public class TestMethod { method = selectMethod(className + "#" + methodName).getJavaMethod(); } - /** - * Returns the {@link org.junit.platform.engine.TestDescriptor} ID for this method - */ + /** Returns the {@link org.junit.platform.engine.TestDescriptor} ID for this method */ String getDescriptorId() { return "test-template:" + nameWithParams; } - /** - * Returns just the name of the method without parameters - */ + /** Returns just the name of the method without parameters */ String getName() { return method.getName(); } diff --git a/src/test/java/com/code_intelligence/jazzer/junit/UtilsTest.java b/src/test/java/com/code_intelligence/jazzer/junit/UtilsTest.java index d45741794..71e828a2b 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/UtilsTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/UtilsTest.java @@ -80,11 +80,21 @@ void testParseJUnitTimeoutValueToSeconds() { assertThrows(IllegalArgumentException.class, () -> parseJUnitTimeoutValueToSeconds("5 5")); } - @ValueSource(classes = {int.class, Class.class, Object.class, String.class, HashMap.class, - Map.class, int[].class, int[][].class, AbstractMap.class, AbstractList.class}) + @ValueSource( + classes = { + int.class, + Class.class, + Object.class, + String.class, + HashMap.class, + Map.class, + int[].class, + int[][].class, + AbstractMap.class, + AbstractList.class + }) @ParameterizedTest - void - testMarkedInstances(Class clazz) { + void testMarkedInstances(Class clazz) { Object instance = getMarkedInstance(clazz); if (clazz == int.class) { assertThat(instance).isInstanceOf(Integer.class); @@ -111,14 +121,21 @@ static Stream testWithMarkedNamedParametersSource() { @MethodSource("testWithMarkedNamedParametersSource") @ExtendWith(UtilsTest.class) @ParameterizedTest - void testWithMarkedNamedParameters(String str, int num, AbstractMap map, - List list, Class clazz, int[] array) {} + void testWithMarkedNamedParameters( + String str, + int num, + AbstractMap map, + List list, + Class clazz, + int[] array) {} boolean argumentsExpectedToBeMarked = false; @Override - public void interceptTestTemplateMethod(Invocation invocation, - ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) + public void interceptTestTemplateMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) throws Throwable { assertThat(isMarkedInvocation(invocationContext)).isEqualTo(argumentsExpectedToBeMarked); argumentsExpectedToBeMarked = !argumentsExpectedToBeMarked; @@ -147,10 +164,12 @@ public void testGetClassPathBasedInstrumentationFilter() throws IOException { Path firstJar = createFile(temp.resolve("first.jar")); Path secondJar = createFile(temp.resolve("second.jar")); - assertThat(Utils.getClassPathBasedInstrumentationFilter(makeClassPath( - firstDir, firstJar, nonExistentDir, secondDir, secondJar, emptyDir))) - .hasValue(asList( - "*", "com.example.other_project.**", "com.example.project.**", "org.example.**")); + assertThat( + Utils.getClassPathBasedInstrumentationFilter( + makeClassPath(firstDir, firstJar, nonExistentDir, secondDir, secondJar, emptyDir))) + .hasValue( + asList( + "*", "com.example.other_project.**", "com.example.project.**", "org.example.**")); } @Test diff --git a/src/test/java/com/code_intelligence/jazzer/junit/ValueProfileTest.java b/src/test/java/com/code_intelligence/jazzer/junit/ValueProfileTest.java index a1cc21cfb..57251c38f 100644 --- a/src/test/java/com/code_intelligence/jazzer/junit/ValueProfileTest.java +++ b/src/test/java/com/code_intelligence/jazzer/junit/ValueProfileTest.java @@ -29,7 +29,6 @@ import static org.junit.platform.testkit.engine.EventConditions.uniqueIdSubstrings; import static org.junit.platform.testkit.engine.EventType.DYNAMIC_TEST_REGISTERED; import static org.junit.platform.testkit.engine.EventType.FINISHED; -import static org.junit.platform.testkit.engine.EventType.SKIPPED; import static org.junit.platform.testkit.engine.EventType.STARTED; import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; @@ -45,7 +44,6 @@ import org.junit.Test; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; -import org.junit.platform.testkit.engine.EventType; import org.junit.rules.TemporaryFolder; public class ValueProfileTest { @@ -66,8 +64,16 @@ public void setup() throws IOException { baseDir = temp.getRoot().toPath(); // Create a fake test resource directory structure with an input directory to verify that // Jazzer uses it and emits a crash file into it. - inputsDirectories = baseDir.resolve(Paths.get("src", "test", "resources", "com", "example", - "ValueProfileFuzzTestInputs", "valueProfileFuzz")); + inputsDirectories = + baseDir.resolve( + Paths.get( + "src", + "test", + "resources", + "com", + "example", + "ValueProfileFuzzTestInputs", + "valueProfileFuzz")); Files.createDirectories(inputsDirectories); } @@ -87,52 +93,75 @@ public void valueProfileEnabled() throws IOException { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - - results.testEvents().assertEventsMatchExactly( - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 1)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 2)), - displayName("empty_seed")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 2)), - displayName("empty_seed"), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 3)), - displayName("Fuzzing...")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 3)), - displayName("Fuzzing..."), - finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 1)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 2)), + displayName("empty_seed")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 2)), + displayName("empty_seed"), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 3)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 3)), + displayName("Fuzzing..."), + finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); // Should crash on the exact input "Jazzer", with the crash emitted into the seed corpus. - try (Stream crashFiles = Files.list(baseDir).filter( - path -> path.getFileName().toString().startsWith("crash-"))) { + try (Stream crashFiles = + Files.list(baseDir).filter(path -> path.getFileName().toString().startsWith("crash-"))) { assertThat(crashFiles).isEmpty(); } try (Stream seeds = Files.list(inputsDirectories)) { - assertThat(seeds).containsExactly( - inputsDirectories.resolve("crash-131db69c7fadc408fe5031079dad3a441df09aff")); + assertThat(seeds) + .containsExactly( + inputsDirectories.resolve("crash-131db69c7fadc408fe5031079dad3a441df09aff")); } - assertThat(Files.readAllBytes( - inputsDirectories.resolve("crash-131db69c7fadc408fe5031079dad3a441df09aff"))) + assertThat( + Files.readAllBytes( + inputsDirectories.resolve("crash-131db69c7fadc408fe5031079dad3a441df09aff"))) .isEqualTo("Jazzer".getBytes(StandardCharsets.UTF_8)); // Verify that the engine created the generated corpus directory and emitted inputs into it. @@ -150,43 +179,65 @@ public void valueProfileDisabled() throws IOException { EngineExecutionResults results = executeTests(); - results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), - event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ)), - finishedSuccessfully()), - event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), - event(type(FINISHED), container(ENGINE), finishedSuccessfully())); - - results.testEvents().assertEventsMatchExactly( - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 1)), - displayName("")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 1)), - displayName(""), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 2)), - displayName("empty_seed")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 2)), - displayName("empty_seed"), finishedSuccessfully()), - event(type(DYNAMIC_TEST_REGISTERED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), - event(type(STARTED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 3)), - displayName("Fuzzing...")), - event(type(FINISHED), - test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 3)), - displayName("Fuzzing..."), finishedSuccessfully())); + results + .containerEvents() + .assertEventsMatchExactly( + event(type(STARTED), container(ENGINE)), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), + event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ)), + finishedSuccessfully()), + event( + type(FINISHED), + container(uniqueIdSubstrings(ENGINE, CLAZZ)), + finishedSuccessfully()), + event(type(FINISHED), container(ENGINE), finishedSuccessfully())); + + results + .testEvents() + .assertEventsMatchExactly( + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 1)), + displayName("")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 1)), + displayName(""), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 2)), + displayName("empty_seed")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 2)), + displayName("empty_seed"), + finishedSuccessfully()), + event( + type(DYNAMIC_TEST_REGISTERED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ))), + event( + type(STARTED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 3)), + displayName("Fuzzing...")), + event( + type(FINISHED), + test(uniqueIdSubstrings(ENGINE, CLAZZ, VALUE_PROFILE_FUZZ, INVOCATION + 3)), + displayName("Fuzzing..."), + finishedSuccessfully())); // No crash means no crashing input is emitted anywhere. - try (Stream crashFiles = Files.list(baseDir).filter( - path -> path.getFileName().toString().startsWith("crash-"))) { + try (Stream crashFiles = + Files.list(baseDir).filter(path -> path.getFileName().toString().startsWith("crash-"))) { assertThat(crashFiles).isEmpty(); } try (Stream seeds = Files.list(inputsDirectories)) { diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/ArgumentsMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/ArgumentsMutatorTest.java index 9a5bafd85..a07d7c9c4 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/ArgumentsMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/ArgumentsMutatorTest.java @@ -52,25 +52,26 @@ void testStaticMethod() throws Throwable { assertThat(maybeMutator).isPresent(); ArgumentsMutator mutator = maybeMutator.get(); - try (MockPseudoRandom prng = mockPseudoRandom( - // outer list not null - false, - // outer list size 1 - 1, - // inner list not null - false, - // inner list size 1 - 1, - // boolean - true, - // outer list not null - false, - // outer list size 1 - 1, - // Boolean not null - false, - // boolean - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // outer list not null + false, + // outer list size 1 + 1, + // inner list not null + false, + // inner list size 1 + 1, + // boolean + true, + // outer list not null + false, + // outer list size 1 + 1, + // Boolean not null + false, + // boolean + false)) { mutator.init(prng); } @@ -80,25 +81,26 @@ void testStaticMethod() throws Throwable { assertThat(fuzzThisFunctionArgument1).containsExactly(singletonList(true)); assertThat(fuzzThisFunctionArgument2).containsExactly(false); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first argument - 0, - // Nullable mutator - false, - // Action mutate in outer list - 2, - // Mutate one element, - 1, - // index to get to inner list - 0, - // Nullable mutator - false, - // Action mutate inner list - 2, - // Mutate one element, - 1, - // index to get boolean value - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first argument + 0, + // Nullable mutator + false, + // Action mutate in outer list + 2, + // Mutate one element, + 1, + // index to get to inner list + 0, + // Nullable mutator + false, + // Action mutate inner list + 2, + // Mutate one element, + 1, + // index to get boolean value + 0)) { mutator.mutate(prng); } @@ -112,25 +114,26 @@ void testStaticMethod() throws Throwable { fuzzThisFunctionArgument1.get(0).clear(); fuzzThisFunctionArgument2.clear(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first argument - 0, - // Nullable mutator - false, - // Action mutate in outer list - 2, - // Mutate one element, - 1, - // index to get to inner list - 0, - // Nullable mutator - false, - // Action mutate inner list - 2, - // Mutate one element, - 1, - // index to get boolean value - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first argument + 0, + // Nullable mutator + false, + // Action mutate in outer list + 2, + // Mutate one element, + 1, + // index to get to inner list + 0, + // Nullable mutator + false, + // Action mutate inner list + 2, + // Mutate one element, + 1, + // index to get boolean value + 0)) { mutator.mutate(prng); } @@ -158,25 +161,26 @@ void testInstanceMethod() throws Throwable { assertThat(maybeMutator).isPresent(); ArgumentsMutator mutator = maybeMutator.get(); - try (MockPseudoRandom prng = mockPseudoRandom( - // outer list not null - false, - // outer list size 1 - 1, - // inner list not null - false, - // inner list size 1 - 1, - // boolean - true, - // outer list not null - false, - // outer list size 1 - 1, - // Boolean not null - false, - // boolean - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // outer list not null + false, + // outer list size 1 + 1, + // inner list not null + false, + // inner list size 1 + 1, + // boolean + true, + // outer list not null + false, + // outer list size 1 + 1, + // Boolean not null + false, + // boolean + false)) { mutator.init(prng); } @@ -186,25 +190,26 @@ void testInstanceMethod() throws Throwable { assertThat(mutableFuzzThisFunctionArgument1).containsExactly(singletonList(true)); assertThat(mutableFuzzThisFunctionArgument2).containsExactly(false); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first argument - 0, - // Nullable mutator - false, - // Action mutate in outer list - 2, - // Mutate one element, - 1, - // index to get to inner list - 0, - // Nullable mutator - false, - // Action mutate inner list - 2, - // Mutate one element, - 1, - // index to get boolean value - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first argument + 0, + // Nullable mutator + false, + // Action mutate in outer list + 2, + // Mutate one element, + 1, + // index to get to inner list + 0, + // Nullable mutator + false, + // Action mutate inner list + 2, + // Mutate one element, + 1, + // index to get boolean value + 0)) { mutator.mutate(prng); } @@ -218,25 +223,26 @@ void testInstanceMethod() throws Throwable { mutableFuzzThisFunctionArgument1.get(0).clear(); mutableFuzzThisFunctionArgument2.clear(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first argument - 0, - // Nullable mutator - false, - // Action mutate in outer list - 2, - // Mutate one element, - 1, - // index to get to inner list - 0, - // Nullable mutator - false, - // Action mutate inner list - 2, - // Mutate one element, - 1, - // index to get boolean value - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first argument + 0, + // Nullable mutator + false, + // Action mutate in outer list + 2, + // Mutate one element, + 1, + // index to get to inner list + 0, + // Nullable mutator + false, + // Action mutate inner list + 2, + // Mutate one element, + 1, + // index to get boolean value + 0)) { mutator.mutate(prng); } @@ -259,30 +265,32 @@ void testCrossOver() throws Throwable { assertThat(maybeMutator).isPresent(); ArgumentsMutator mutator = maybeMutator.get(); - try (MockPseudoRandom prng = mockPseudoRandom( - // list not null - false, - // list size 1 - 1, - // not null, - false, - // boolean - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // list not null + false, + // list size 1 + 1, + // not null, + false, + // boolean + true)) { mutator.init(prng); } ByteArrayOutputStream baos1 = new ByteArrayOutputStream(); mutator.write(baos1); byte[] out1 = baos1.toByteArray(); - try (MockPseudoRandom prng = mockPseudoRandom( - // list not null - false, - // list size 1 - 1, - // not null - false, - // boolean - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // list not null + false, + // list size 1 + 1, + // not null + false, + // boolean + false)) { mutator.init(prng); } ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinatorsTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinatorsTest.java index d0d06f226..4435f610f 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinatorsTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinatorsTest.java @@ -27,7 +27,6 @@ import static com.code_intelligence.jazzer.mutation.support.TestSupport.mockCrossOver; import static com.code_intelligence.jazzer.mutation.support.TestSupport.mockCrossOverInPlace; import static com.code_intelligence.jazzer.mutation.support.TestSupport.mockInitInPlace; -import static com.code_intelligence.jazzer.mutation.support.TestSupport.mockInitializer; import static com.code_intelligence.jazzer.mutation.support.TestSupport.mockMutator; import static com.code_intelligence.jazzer.mutation.support.TestSupport.mockPseudoRandom; import static com.code_intelligence.jazzer.mutation.support.TestSupport.nullDataOutputStream; @@ -81,21 +80,24 @@ void testCrossOverProperty() { mutateProperty(Foo::getValue, mockCrossOver((a, b) -> 42), Foo::setValue); Foo foo = new Foo(0); Foo otherFoo = new Foo(1); - try (MockPseudoRandom prng = mockPseudoRandom( - // use foo value - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // use foo value + 0)) { mutator.crossOverInPlace(foo, otherFoo, prng); assertThat(foo.getValue()).isEqualTo(0); } - try (MockPseudoRandom prng = mockPseudoRandom( - // use otherFoo value - 1)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // use otherFoo value + 1)) { mutator.crossOverInPlace(foo, otherFoo, prng); assertThat(foo.getValue()).isEqualTo(1); } - try (MockPseudoRandom prng = mockPseudoRandom( - // use property type cross over - 2)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // use property type cross over + 2)) { mutator.crossOverInPlace(foo, otherFoo, prng); assertThat(foo.getValue()).isEqualTo(42); } @@ -103,27 +105,30 @@ void testCrossOverProperty() { @Test void testMutateViaView() { - InPlaceMutator mutator = mutateViaView(Foo::getList, new InPlaceMutator>() { - @Override - public void initInPlace(List reference, PseudoRandom prng) { - reference.clear(); - reference.add(21); - } - - @Override - public void mutateInPlace(List reference, PseudoRandom prng) { - reference.add(reference.get(reference.size() - 1) + 1); - } - - @Override - public void crossOverInPlace( - List reference, List otherReference, PseudoRandom prng) {} - - @Override - public String toDebugString(Predicate isInCycle) { - return "List"; - } - }); + InPlaceMutator mutator = + mutateViaView( + Foo::getList, + new InPlaceMutator>() { + @Override + public void initInPlace(List reference, PseudoRandom prng) { + reference.clear(); + reference.add(21); + } + + @Override + public void mutateInPlace(List reference, PseudoRandom prng) { + reference.add(reference.get(reference.size() - 1) + 1); + } + + @Override + public void crossOverInPlace( + List reference, List otherReference, PseudoRandom prng) {} + + @Override + public String toDebugString(Predicate isInCycle) { + return "List"; + } + }); assertThat(mutator.toString()).isEqualTo("Foo via List"); @@ -145,10 +150,14 @@ public String toDebugString(Predicate isInCycle) { @Test void testCrossOverViaView() { - InPlaceMutator mutator = mutateViaView(Foo::getList, mockCrossOverInPlace((a, b) -> { - a.clear(); - a.add(42); - })); + InPlaceMutator mutator = + mutateViaView( + Foo::getList, + mockCrossOverInPlace( + (a, b) -> { + a.clear(); + a.add(42); + })); Foo foo = new Foo(0, singletonList(0)); Foo otherFoo = new Foo(0, singletonList(1)); @@ -164,27 +173,29 @@ void testMutateCombine() { mutateProperty(Foo::getValue, mockMutator(21, value -> 2 * value), Foo::setValue); InPlaceMutator listMutator = - mutateViaView(Foo::getList, new InPlaceMutator>() { - @Override - public void initInPlace(List reference, PseudoRandom prng) { - reference.clear(); - reference.add(21); - } - - @Override - public void mutateInPlace(List reference, PseudoRandom prng) { - reference.add(reference.get(reference.size() - 1) + 1); - } - - @Override - public void crossOverInPlace( - List reference, List otherReference, PseudoRandom prng) {} - - @Override - public String toDebugString(Predicate isInCycle) { - return "List"; - } - }); + mutateViaView( + Foo::getList, + new InPlaceMutator>() { + @Override + public void initInPlace(List reference, PseudoRandom prng) { + reference.clear(); + reference.add(21); + } + + @Override + public void mutateInPlace(List reference, PseudoRandom prng) { + reference.add(reference.get(reference.size() - 1) + 1); + } + + @Override + public void crossOverInPlace( + List reference, List otherReference, PseudoRandom prng) {} + + @Override + public String toDebugString(Predicate isInCycle) { + return "List"; + } + }); InPlaceMutator mutator = combine(valueMutator, listMutator); assertThat(mutator.toString()).isEqualTo("{Foo.Integer, Foo via List}"); @@ -214,18 +225,23 @@ public String toDebugString(Predicate isInCycle) { void testCrossOverCombine() { InPlaceMutator valueMutator = mutateProperty(Foo::getValue, mockCrossOver((a, b) -> 42), Foo::setValue); - InPlaceMutator listMutator = mutateViaView(Foo::getList, mockCrossOverInPlace((a, b) -> { - a.clear(); - a.add(42); - })); + InPlaceMutator listMutator = + mutateViaView( + Foo::getList, + mockCrossOverInPlace( + (a, b) -> { + a.clear(); + a.add(42); + })); InPlaceMutator mutator = combine(valueMutator, listMutator); Foo foo = new Foo(0, singletonList(0)); Foo fooOther = new Foo(1, singletonList(1)); - try (MockPseudoRandom prng = mockPseudoRandom( - // call cross over in property mutator - 2)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // call cross over in property mutator + 2)) { mutator.crossOverInPlace(foo, fooOther, prng); } assertThat(foo.getValue()).isEqualTo(42); @@ -250,43 +266,49 @@ void testMutateAssemble() { mutateProperty(Foo::getValue, mockMutator(21, value -> 2 * value), Foo::setValue); InPlaceMutator listMutator = - mutateViaView(Foo::getList, new InPlaceMutator>() { - @Override - public void initInPlace(List reference, PseudoRandom prng) { - reference.clear(); - reference.add(21); - } - - @Override - public void mutateInPlace(List reference, PseudoRandom prng) { - reference.add(reference.get(reference.size() - 1) + 1); - } - - @Override - public void crossOverInPlace( - List reference, List otherReference, PseudoRandom prng) {} - - @Override - public String toDebugString(Predicate isInCycle) { - return "List"; - } - }); + mutateViaView( + Foo::getList, + new InPlaceMutator>() { + @Override + public void initInPlace(List reference, PseudoRandom prng) { + reference.clear(); + reference.add(21); + } + + @Override + public void mutateInPlace(List reference, PseudoRandom prng) { + reference.add(reference.get(reference.size() - 1) + 1); + } + + @Override + public void crossOverInPlace( + List reference, List otherReference, PseudoRandom prng) {} + + @Override + public String toDebugString(Predicate isInCycle) { + return "List"; + } + }); SerializingInPlaceMutator mutator = - assemble((m) -> {}, () -> new Foo(0, singletonList(0)), new Serializer() { - @Override - public Foo read(DataInputStream in) { - return null; - } - - @Override - public void write(Foo value, DataOutputStream out) {} - - @Override - public Foo detach(Foo value) { - return null; - } - }, () -> combine(valueMutator, listMutator)); + assemble( + (m) -> {}, + () -> new Foo(0, singletonList(0)), + new Serializer() { + @Override + public Foo read(DataInputStream in) { + return null; + } + + @Override + public void write(Foo value, DataOutputStream out) {} + + @Override + public Foo detach(Foo value) { + return null; + } + }, + () -> combine(valueMutator, listMutator)); assertThat(mutator.toString()).isEqualTo("{Foo.Integer, Foo via List}"); @@ -316,33 +338,42 @@ void testCrossOverAssemble() { InPlaceMutator valueMutator = mutateProperty(Foo::getValue, mockCrossOver((a, b) -> 42), Foo::setValue); - InPlaceMutator listMutator = mutateViaView(Foo::getList, mockCrossOverInPlace((a, b) -> { - a.clear(); - a.add(42); - })); + InPlaceMutator listMutator = + mutateViaView( + Foo::getList, + mockCrossOverInPlace( + (a, b) -> { + a.clear(); + a.add(42); + })); SerializingInPlaceMutator mutator = - assemble((m) -> {}, () -> new Foo(0, singletonList(0)), new Serializer() { - @Override - public Foo read(DataInputStream in) { - return null; - } - - @Override - public void write(Foo value, DataOutputStream out) {} - - @Override - public Foo detach(Foo value) { - return null; - } - }, () -> combine(valueMutator, listMutator)); + assemble( + (m) -> {}, + () -> new Foo(0, singletonList(0)), + new Serializer() { + @Override + public Foo read(DataInputStream in) { + return null; + } + + @Override + public void write(Foo value, DataOutputStream out) {} + + @Override + public Foo detach(Foo value) { + return null; + } + }, + () -> combine(valueMutator, listMutator)); Foo foo = new Foo(0, singletonList(0)); Foo fooOther = new Foo(1, singletonList(1)); - try (MockPseudoRandom prng = mockPseudoRandom( - // cross over in property mutator - 2)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // cross over in property mutator + 2)) { mutator.crossOverInPlace(foo, fooOther, prng); } assertThat(foo.getValue()).isEqualTo(42); @@ -352,13 +383,15 @@ public Foo detach(Foo value) { @Test void testMutateThenMapToImmutable() throws IOException { SerializingMutator charMutator = - mockMutator(new char[] {'H', 'e', 'l', 'l', 'o'}, chars -> { - for (int i = 0; i < chars.length; i++) { - chars[i] ^= (1 << 5); - } - chars[chars.length - 1]++; - return chars; - }); + mockMutator( + new char[] {'H', 'e', 'l', 'l', 'o'}, + chars -> { + for (int i = 0; i < chars.length; i++) { + chars[i] ^= (1 << 5); + } + chars[chars.length - 1]++; + return chars; + }); SerializingMutator mutator = mutateThenMapToImmutable(charMutator, String::new, String::toCharArray); @@ -388,17 +421,20 @@ void testMutateThenMapToImmutable() throws IOException { assertThat(value).isEqualTo("hELLP"); final String capturedValue = value; - assertThrows(UnsupportedOperationException.class, + assertThrows( + UnsupportedOperationException.class, () -> mutator.write(capturedValue, nullDataOutputStream())); } @Test void testCrossOverThenMapToImmutable() { - SerializingMutator charMutator = mockCrossOver((a, b) -> { - assertThat(a).isEqualTo(new char[] {'H', 'e', 'l', 'l', 'o'}); - assertThat(b).isEqualTo(new char[] {'W', 'o', 'r', 'l', 'd'}); - return new char[] {'T', 'e', 's', 't', 'e', 'd'}; - }); + SerializingMutator charMutator = + mockCrossOver( + (a, b) -> { + assertThat(a).isEqualTo(new char[] {'H', 'e', 'l', 'l', 'o'}); + assertThat(b).isEqualTo(new char[] {'W', 'o', 'r', 'l', 'd'}); + return new char[] {'T', 'e', 's', 't', 'e', 'd'}; + }); SerializingMutator mutator = mutateThenMapToImmutable(charMutator, String::new, String::toCharArray); @@ -415,31 +451,34 @@ void testCrossOverProduct() { SerializingMutator mutator2 = mockCrossOver((a, b) -> 42); ProductMutator mutator = mutateProduct(mutator1, mutator2); - try (MockPseudoRandom prng = mockPseudoRandom( - // use first value in mutator1 - 0, - // use second value in mutator2 - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // use first value in mutator1 + 0, + // use second value in mutator2 + 0)) { Object[] crossedOver = mutator.crossOver(new Object[] {false, 0}, new Object[] {true, 1}, prng); assertThat(crossedOver).isEqualTo(new Object[] {false, 0}); } - try (MockPseudoRandom prng = mockPseudoRandom( - // use first value in mutator1 - 1, - // use second value in mutator2 - 1)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // use first value in mutator1 + 1, + // use second value in mutator2 + 1)) { Object[] crossedOver = mutator.crossOver(new Object[] {false, 0}, new Object[] {true, 1}, prng); assertThat(crossedOver).isEqualTo(new Object[] {true, 1}); } - try (MockPseudoRandom prng = mockPseudoRandom( - // use cross over in mutator1 - 2, - // use cross over in mutator2 - 2)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // use cross over in mutator1 + 2, + // use cross over in mutator2 + 2)) { Object[] crossedOver = mutator.crossOver(new Object[] {false, 0}, new Object[] {true, 2}, prng); assertThat(crossedOver).isEqualTo(new Object[] {true, 42}); @@ -449,7 +488,11 @@ void testCrossOverProduct() { @Test void testCrossOverSumInPlaceSameType() { ToIntFunction> mutotarIndexFromValue = (r) -> 0; - InPlaceMutator> mutator1 = mockCrossOverInPlace((a, b) -> { a.add(42); }); + InPlaceMutator> mutator1 = + mockCrossOverInPlace( + (a, b) -> { + a.add(42); + }); InPlaceMutator> mutator2 = mockCrossOverInPlace((a, b) -> {}); InPlaceMutator> mutator = mutateSumInPlace(mutotarIndexFromValue, mutator1, mutator2); @@ -467,7 +510,7 @@ void testCrossOverSumInPlaceSameType() { void testCrossOverSumInPlaceIndeterminate() { InPlaceMutator> mutator1 = mockCrossOverInPlace((a, b) -> {}); InPlaceMutator> mutator2 = mockCrossOverInPlace((a, b) -> {}); - ToIntFunction> bothIndeterminate = (r) -> - 1; + ToIntFunction> bothIndeterminate = (r) -> -1; InPlaceMutator> mutator = mutateSumInPlace(bothIndeterminate, mutator1, mutator2); @@ -487,7 +530,11 @@ void testCrossOverSumInPlaceFirstIndeterminate() { List otherReference = new ArrayList<>(); InPlaceMutator> mutator1 = mockCrossOverInPlace((a, b) -> {}); - InPlaceMutator> mutator2 = mockInitInPlace((l) -> { l.add(42); }); + InPlaceMutator> mutator2 = + mockInitInPlace( + (l) -> { + l.add(42); + }); ToIntFunction> firstIndeterminate = (r) -> r == reference ? -1 : 1; InPlaceMutator> mutator = @@ -506,6 +553,7 @@ static class Foo { public Foo(int value) { this(value, new ArrayList<>()); } + public Foo(int value, List list) { this.value = value; this.list = new ArrayList<>(list); diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandomTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandomTest.java index 38ab2eb2f..15d273a99 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandomTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandomTest.java @@ -33,7 +33,8 @@ public class SeededPseudoRandomTest { static Stream doubleClosedRange() { - return Stream.of(arguments(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, false), + return Stream.of( + arguments(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, false), arguments(Double.MAX_VALUE, Double.POSITIVE_INFINITY, false), arguments(Double.NEGATIVE_INFINITY, -Double.MAX_VALUE, false), arguments(-Double.MAX_VALUE, Double.MAX_VALUE, false), @@ -42,37 +43,51 @@ static Stream doubleClosedRange() { arguments(-Double.MAX_VALUE * 0.5, Math.nextUp(Double.MAX_VALUE * 0.5), false), arguments(Double.MAX_VALUE, Double.MAX_VALUE, false), arguments(-Double.MIN_VALUE, Double.MIN_VALUE, false), - arguments(-Double.MIN_VALUE, 0, false), arguments(0, Double.MIN_VALUE, false), - arguments(-Double.MAX_VALUE, 0, false), arguments(0, Double.MAX_VALUE, false), - arguments(1000.0, Double.MAX_VALUE, false), arguments(0, Double.POSITIVE_INFINITY, false), + arguments(-Double.MIN_VALUE, 0, false), + arguments(0, Double.MIN_VALUE, false), + arguments(-Double.MAX_VALUE, 0, false), + arguments(0, Double.MAX_VALUE, false), + arguments(1000.0, Double.MAX_VALUE, false), + arguments(0, Double.POSITIVE_INFINITY, false), arguments(1e200, Double.POSITIVE_INFINITY, false), - arguments(Double.NEGATIVE_INFINITY, -1e200, false), arguments(0.0, 1.0, false), - arguments(-1.0, 1.0, false), arguments(-1e300, 1e300, false), + arguments(Double.NEGATIVE_INFINITY, -1e200, false), + arguments(0.0, 1.0, false), + arguments(-1.0, 1.0, false), + arguments(-1e300, 1e300, false), arguments(0.0, 0.0 + Double.MIN_VALUE, false), arguments(-Double.MAX_VALUE, -Double.MAX_VALUE + 1e292, false), - arguments(-Double.NaN, 0.0, true), arguments(0.0, Double.NaN, true), + arguments(-Double.NaN, 0.0, true), + arguments(0.0, Double.NaN, true), arguments(Double.NaN, Double.NaN, true)); } static Stream floatClosedRange() { - return Stream.of(arguments(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, false), + return Stream.of( + arguments(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, false), arguments(Float.MAX_VALUE, Float.POSITIVE_INFINITY, false), arguments(Float.NEGATIVE_INFINITY, -Float.MAX_VALUE, false), arguments(-Float.MAX_VALUE, Float.MAX_VALUE, false), arguments(-Float.MAX_VALUE, -Float.MAX_VALUE, false), arguments(Float.MAX_VALUE, Float.MAX_VALUE, false), arguments(-Float.MAX_VALUE / 2f, Float.MAX_VALUE / 2f, false), - arguments(-Float.MIN_VALUE, Float.MIN_VALUE, false), arguments(-Float.MIN_VALUE, 0f, false), - arguments(0f, Float.MIN_VALUE, false), arguments(-Float.MAX_VALUE, 0f, false), - arguments(0f, Float.MAX_VALUE, false), arguments(-Float.MAX_VALUE, -0f, false), - arguments(-0f, Float.MAX_VALUE, false), arguments(1000f, Float.MAX_VALUE, false), + arguments(-Float.MIN_VALUE, Float.MIN_VALUE, false), + arguments(-Float.MIN_VALUE, 0f, false), + arguments(0f, Float.MIN_VALUE, false), + arguments(-Float.MAX_VALUE, 0f, false), + arguments(0f, Float.MAX_VALUE, false), + arguments(-Float.MAX_VALUE, -0f, false), + arguments(-0f, Float.MAX_VALUE, false), + arguments(1000f, Float.MAX_VALUE, false), arguments(0f, Float.POSITIVE_INFINITY, false), arguments(1e38f, Float.POSITIVE_INFINITY, false), - arguments(Float.NEGATIVE_INFINITY, -1e38f, false), arguments(0f, 1f, false), - arguments(-1f, 1f, false), arguments(-1e38f, 1e38f, false), + arguments(Float.NEGATIVE_INFINITY, -1e38f, false), + arguments(0f, 1f, false), + arguments(-1f, 1f, false), + arguments(-1e38f, 1e38f, false), arguments(0f, 0f + Float.MIN_VALUE, false), arguments(-Float.MAX_VALUE, -Float.MAX_VALUE + 1e32f, false), - arguments(-Float.NaN, 0f, true), arguments(0f, Float.NaN, true), + arguments(-Float.NaN, 0f, true), + arguments(0f, Float.NaN, true), arguments(Float.NaN, Float.NaN, true)); } @@ -82,9 +97,9 @@ void testDoubleForceInRange(double minValue, double maxValue, boolean throwsExce SeededPseudoRandom seededPseudoRandom = new SeededPseudoRandom(1337); for (int i = 0; i < 1000; i++) { if (throwsException) { - assertThrows(IllegalArgumentException.class, - () - -> seededPseudoRandom.closedRange(minValue, maxValue), + assertThrows( + IllegalArgumentException.class, + () -> seededPseudoRandom.closedRange(minValue, maxValue), "minValue: " + minValue + ", maxValue: " + maxValue); } else { double inClosedRange = seededPseudoRandom.closedRange(minValue, maxValue); @@ -101,9 +116,9 @@ void testFloatForceInRange(float minValue, float maxValue, boolean throwsExcepti SeededPseudoRandom seededPseudoRandom = new SeededPseudoRandom(1337); for (int i = 0; i < 1000; i++) { if (throwsException) { - assertThrows(IllegalArgumentException.class, - () - -> seededPseudoRandom.closedRange(minValue, maxValue), + assertThrows( + IllegalArgumentException.class, + () -> seededPseudoRandom.closedRange(minValue, maxValue), "minValue: " + minValue + ", maxValue: " + maxValue); } else { float inClosedRange = seededPseudoRandom.closedRange(minValue, maxValue); @@ -137,7 +152,8 @@ void testClosedRangeBiasedTowardsSmall_distribution() { // https://www.wolframalpha.com/input?i=N%5BTable%5BPDF%5BZipfDistribution%5B10%2C+1%5D%2C+i%5D%2C+%7Bi%2C+1%2C+10%7D%5D%5D assertThat(frequencies) .comparingValuesUsing(Correspondence.tolerance(0.0005)) - .containsExactly(0, 0.645, 1, 0.161, 2, 0.072, 3, 0.040, 4, 0.026, 5, 0.018, 6, 0.013, 7, - 0.01, 8, 0.008, 9, 0.006); + .containsExactly( + 0, 0.645, 1, 0.161, 2, 0.072, 3, 0.040, 4, 0.026, 5, 0.018, 6, 0.013, 7, 0.01, 8, 0.008, + 9, 0.006); } } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/StressTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/StressTest.java index 94b693915..87f4f5853 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/StressTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/StressTest.java @@ -93,9 +93,16 @@ public class StressTest { private static final int NUM_MUTATE_PER_INIT = 100; private static final double MANY_DISTINCT_ELEMENTS_RATIO = 0.5; - private enum TestEnumTwo { A, B } + private enum TestEnumTwo { + A, + B + } - private enum TestEnumThree { A, B, C } + private enum TestEnumThree { + A, + B, + C + } @SuppressWarnings("unused") static Message getTestProtobufDefaultInstance() { @@ -103,37 +110,54 @@ static Message getTestProtobufDefaultInstance() { } public static Stream stressTestCases() { - return Stream.of(arguments(asAnnotatedType(boolean.class), "Boolean", exactly(false, true), - exactly(false, true)), - arguments(new TypeHolder<@NotNull Boolean>() {}.annotatedType(), "Boolean", - exactly(false, true), exactly(false, true)), - arguments(new TypeHolder() {}.annotatedType(), "Nullable", - exactly(null, false, true), exactly(null, false, true)), - arguments(new TypeHolder<@NotNull List<@NotNull Boolean>>() {}.annotatedType(), - "List", exactly(emptyList(), singletonList(false), singletonList(true)), + return Stream.of( + arguments( + asAnnotatedType(boolean.class), "Boolean", exactly(false, true), exactly(false, true)), + arguments( + new TypeHolder<@NotNull Boolean>() {}.annotatedType(), + "Boolean", + exactly(false, true), + exactly(false, true)), + arguments( + new TypeHolder() {}.annotatedType(), + "Nullable", + exactly(null, false, true), + exactly(null, false, true)), + arguments( + new TypeHolder<@NotNull List<@NotNull Boolean>>() {}.annotatedType(), + "List", + exactly(emptyList(), singletonList(false), singletonList(true)), manyDistinctElements()), - arguments(new TypeHolder<@NotNull List>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull List>() {}.annotatedType(), "List>", exactly(emptyList(), singletonList(null), singletonList(false), singletonList(true)), manyDistinctElements()), - arguments(new TypeHolder>() {}.annotatedType(), + arguments( + new TypeHolder>() {}.annotatedType(), "Nullable>", exactly(null, emptyList(), singletonList(false), singletonList(true)), distinctElementsRatio(0.30)), - arguments(new TypeHolder>() {}.annotatedType(), + arguments( + new TypeHolder>() {}.annotatedType(), "Nullable>>", exactly( null, emptyList(), singletonList(null), singletonList(false), singletonList(true)), distinctElementsRatio(0.30)), arguments( new TypeHolder<@NotNull Map<@NotNull String, @NotNull String>>() {}.annotatedType(), - "Map", distinctElementsRatio(0.45), distinctElementsRatio(0.45)), - arguments(new TypeHolder>() {}.annotatedType(), - "Nullable>", distinctElementsRatio(0.46), + "Map", + distinctElementsRatio(0.45), + distinctElementsRatio(0.45)), + arguments( + new TypeHolder>() {}.annotatedType(), + "Nullable>", + distinctElementsRatio(0.46), distinctElementsRatio(0.48)), arguments( - new TypeHolder<@WithSize(max = 3) @NotNull Map<@NotNull Integer, @NotNull Integer>>() { - }.annotatedType(), + new TypeHolder< + @WithSize(max = 3) @NotNull Map< + @NotNull Integer, @NotNull Integer>>() {}.annotatedType(), "Map", // Half of all maps are empty, the other half is heavily biased towards special values. all(mapSizeInClosedRange(0, 3), distinctElementsRatio(0.2)), @@ -145,123 +169,184 @@ null, emptyList(), singletonList(null), singletonList(false), singletonList(true distinctElements(1 + 4), // 1 0-element map, 4 1-element maps, 4 2-element maps distinctElements(1 + 4 + 4)), - arguments(asAnnotatedType(byte.class), "Byte", + arguments( + asAnnotatedType(byte.class), + "Byte", // init is heavily biased towards special values and only returns a uniformly random // value in 1 out of 5 calls. - all(expectedNumberOfDistinctElements(1 << Byte.SIZE, boundHits(NUM_INITS, 0.2)), + all( + expectedNumberOfDistinctElements(1 << Byte.SIZE, boundHits(NUM_INITS, 0.2)), contains((byte) 0, (byte) 1, Byte.MIN_VALUE, Byte.MAX_VALUE)), // With mutations, we expect to reach all possible bytes. exactly(rangeClosed(Byte.MIN_VALUE, Byte.MAX_VALUE).mapToObj(i -> (byte) i).toArray())), - arguments(asAnnotatedType(short.class), "Short", + arguments( + asAnnotatedType(short.class), + "Short", // init is heavily biased towards special values and only returns a uniformly random // value in 1 out of 5 calls. - all(expectedNumberOfDistinctElements(1 << Short.SIZE, boundHits(NUM_INITS, 0.2)), + all( + expectedNumberOfDistinctElements(1 << Short.SIZE, boundHits(NUM_INITS, 0.2)), contains((short) 0, (short) 1, Short.MIN_VALUE, Short.MAX_VALUE)), // The integral type mutator does not always return uniformly random values and the // random walk it uses is more likely to produce non-distinct elements, hence the test // only passes with ~90% of the optimal parameters. expectedNumberOfDistinctElements( 1 << Short.SIZE, NUM_INITS * NUM_MUTATE_PER_INIT * 9 / 10)), - arguments(asAnnotatedType(int.class), "Integer", + arguments( + asAnnotatedType(int.class), + "Integer", // init is heavily biased towards special values and only returns a uniformly random // value in 1 out of 5 calls. - all(expectedNumberOfDistinctElements(1L << Integer.SIZE, boundHits(NUM_INITS, 0.2)), + all( + expectedNumberOfDistinctElements(1L << Integer.SIZE, boundHits(NUM_INITS, 0.2)), contains(0, 1, Integer.MIN_VALUE, Integer.MAX_VALUE)), // See "Short" case. expectedNumberOfDistinctElements( 1L << Integer.SIZE, NUM_INITS * NUM_MUTATE_PER_INIT * 9 / 10)), - arguments(new TypeHolder<@NotNull @InRange(min = 0) Long>() {}.annotatedType(), "Long", + arguments( + new TypeHolder<@NotNull @InRange(min = 0) Long>() {}.annotatedType(), + "Long", // init is heavily biased towards special values and only returns a uniformly random // value in 1 out of 5 calls. - all(expectedNumberOfDistinctElements(1L << Long.SIZE - 1, boundHits(NUM_INITS, 0.2)), + all( + expectedNumberOfDistinctElements(1L << Long.SIZE - 1, boundHits(NUM_INITS, 0.2)), contains(0L, 1L, Long.MAX_VALUE)), // See "Short" case. expectedNumberOfDistinctElements( 1L << Integer.SIZE - 1, NUM_INITS * NUM_MUTATE_PER_INIT * 9 / 10)), arguments( - new TypeHolder<@NotNull @InRange(max = Integer.MIN_VALUE + 5) Integer>() { - }.annotatedType(), + new TypeHolder< + @NotNull @InRange(max = Integer.MIN_VALUE + 5) Integer>() {}.annotatedType(), "Integer", exactly(rangeClosed(Integer.MIN_VALUE, Integer.MIN_VALUE + 5).boxed().toArray()), exactly(rangeClosed(Integer.MIN_VALUE, Integer.MIN_VALUE + 5).boxed().toArray())), - arguments(asAnnotatedType(TestEnumTwo.class), "Nullable>", + arguments( + asAnnotatedType(TestEnumTwo.class), + "Nullable>", exactly(null, TestEnumTwo.A, TestEnumTwo.B), exactly(null, TestEnumTwo.A, TestEnumTwo.B)), - arguments(asAnnotatedType(TestEnumThree.class), "Nullable>", + arguments( + asAnnotatedType(TestEnumThree.class), + "Nullable>", exactly(null, TestEnumThree.A, TestEnumThree.B, TestEnumThree.C), exactly(null, TestEnumThree.A, TestEnumThree.B, TestEnumThree.C)), - arguments(new TypeHolder<@NotNull @FloatInRange(min = 0f) Float>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull @FloatInRange(min = 0f) Float>() {}.annotatedType(), "Float", - all(distinctElementsRatio(0.45), + all( + distinctElementsRatio(0.45), doesNotContain(Float.NEGATIVE_INFINITY, -Float.MAX_VALUE, -Float.MIN_VALUE), - contains(Float.NaN, Float.POSITIVE_INFINITY, Float.MAX_VALUE, Float.MIN_VALUE, 0.0f, + contains( + Float.NaN, + Float.POSITIVE_INFINITY, + Float.MAX_VALUE, + Float.MIN_VALUE, + 0.0f, -0.0f)), - all(distinctElementsRatio(0.75), + all( + distinctElementsRatio(0.75), doesNotContain(Float.NEGATIVE_INFINITY, -Float.MAX_VALUE, -Float.MIN_VALUE))), - arguments(new TypeHolder<@NotNull Float>() {}.annotatedType(), "Float", - all(distinctElementsRatio(0.45), - contains(Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, - -Float.MAX_VALUE, Float.MAX_VALUE, -Float.MIN_VALUE, Float.MIN_VALUE, 0.0f, + arguments( + new TypeHolder<@NotNull Float>() {}.annotatedType(), + "Float", + all( + distinctElementsRatio(0.45), + contains( + Float.NaN, + Float.NEGATIVE_INFINITY, + Float.POSITIVE_INFINITY, + -Float.MAX_VALUE, + Float.MAX_VALUE, + -Float.MIN_VALUE, + Float.MIN_VALUE, + 0.0f, -0.0f)), distinctElementsRatio(0.76)), arguments( - new TypeHolder<@NotNull @FloatInRange( - min = -1.0f, max = 1.0f, allowNaN = false) Float>() { - }.annotatedType(), + new TypeHolder< + @NotNull @FloatInRange(min = -1.0f, max = 1.0f, allowNaN = false) + Float>() {}.annotatedType(), "Float", - all(distinctElementsRatio(0.45), - doesNotContain(Float.NaN, -Float.MAX_VALUE, Float.MAX_VALUE, - Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY), + all( + distinctElementsRatio(0.45), + doesNotContain( + Float.NaN, + -Float.MAX_VALUE, + Float.MAX_VALUE, + Float.NEGATIVE_INFINITY, + Float.POSITIVE_INFINITY), contains(-Float.MIN_VALUE, Float.MIN_VALUE, 0.0f, -0.0f)), - all(distinctElementsRatio(0.525), - doesNotContain(Float.NaN, -Float.MAX_VALUE, Float.MAX_VALUE, - Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY), + all( + distinctElementsRatio(0.525), + doesNotContain( + Float.NaN, + -Float.MAX_VALUE, + Float.MAX_VALUE, + Float.NEGATIVE_INFINITY, + Float.POSITIVE_INFINITY), contains(-Float.MIN_VALUE, Float.MIN_VALUE, 0.0f, -0.0f))), - arguments(new TypeHolder<@NotNull Double>() {}.annotatedType(), "Double", - all(distinctElementsRatio(0.45), + arguments( + new TypeHolder<@NotNull Double>() {}.annotatedType(), + "Double", + all( + distinctElementsRatio(0.45), contains(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)), distinctElementsRatio(0.75)), arguments( - new TypeHolder<@NotNull @DoubleInRange( - min = -1.0, max = 1.0, allowNaN = false) Double>() { - }.annotatedType(), - "Double", all(distinctElementsRatio(0.45), doesNotContain(Double.NaN)), + new TypeHolder< + @NotNull @DoubleInRange(min = -1.0, max = 1.0, allowNaN = false) + Double>() {}.annotatedType(), + "Double", + all(distinctElementsRatio(0.45), doesNotContain(Double.NaN)), all(distinctElementsRatio(0.55), doesNotContain(Double.NaN))), - arguments(new TypeHolder<@NotNull FuzzedDataProvider>() {}.annotatedType(), - "FuzzedDataProvider", distinctElementsRatio(0.45), distinctElementsRatio(0.45))); + arguments( + new TypeHolder<@NotNull FuzzedDataProvider>() {}.annotatedType(), + "FuzzedDataProvider", + distinctElementsRatio(0.45), + distinctElementsRatio(0.45))); } public static Stream protoStressTestCases() { return Stream.of( - arguments(new TypeHolder<@NotNull OptionalPrimitiveField3>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull OptionalPrimitiveField3>() {}.annotatedType(), "{Builder.Nullable} -> Message", - exactly(OptionalPrimitiveField3.newBuilder().build(), + exactly( + OptionalPrimitiveField3.newBuilder().build(), OptionalPrimitiveField3.newBuilder().setSomeField(false).build(), OptionalPrimitiveField3.newBuilder().setSomeField(true).build()), - exactly(OptionalPrimitiveField3.newBuilder().build(), + exactly( + OptionalPrimitiveField3.newBuilder().build(), OptionalPrimitiveField3.newBuilder().setSomeField(false).build(), OptionalPrimitiveField3.newBuilder().setSomeField(true).build())), - arguments(new TypeHolder<@NotNull RepeatedRecursiveMessageField3>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull RepeatedRecursiveMessageField3>() {}.annotatedType(), "{Builder.Boolean, WithoutInit(Builder via List<(cycle) -> Message>)} -> Message", // The message field is recursive and thus not initialized. - exactly(RepeatedRecursiveMessageField3.getDefaultInstance(), + exactly( + RepeatedRecursiveMessageField3.getDefaultInstance(), RepeatedRecursiveMessageField3.newBuilder().setSomeField(true).build()), manyDistinctElements()), - arguments(new TypeHolder<@NotNull IntegralField3>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull IntegralField3>() {}.annotatedType(), "{Builder.Integer} -> Message", // init is heavily biased towards special values and only returns a uniformly random // value in 1 out of 5 calls. - all(expectedNumberOfDistinctElements(1L << Integer.SIZE, boundHits(NUM_INITS, 0.2)), - contains(IntegralField3.newBuilder().build(), + all( + expectedNumberOfDistinctElements(1L << Integer.SIZE, boundHits(NUM_INITS, 0.2)), + contains( + IntegralField3.newBuilder().build(), IntegralField3.newBuilder().setSomeField(1).build(), IntegralField3.newBuilder().setSomeField(Integer.MIN_VALUE).build(), IntegralField3.newBuilder().setSomeField(Integer.MAX_VALUE).build())), // Our mutations return uniformly random elements in ~3/8 of all cases. expectedNumberOfDistinctElements( 1L << Integer.SIZE, NUM_INITS * NUM_MUTATE_PER_INIT * 3 / 8)), - arguments(new TypeHolder<@NotNull RepeatedIntegralField3>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull RepeatedIntegralField3>() {}.annotatedType(), "{Builder via List} -> Message", - contains(RepeatedIntegralField3.getDefaultInstance(), + contains( + RepeatedIntegralField3.getDefaultInstance(), RepeatedIntegralField3.newBuilder().addSomeField(0).build(), RepeatedIntegralField3.newBuilder().addSomeField(1).build(), RepeatedIntegralField3.newBuilder().addSomeField(Integer.MAX_VALUE).build(), @@ -270,56 +355,112 @@ public static Stream protoStressTestCases() { // special values combined with the small initial size of the list. When we improve the // list mutator, this may be increased. distinctElementsRatio(0.25)), - arguments(new TypeHolder<@NotNull BytesField3>() {}.annotatedType(), - "{Builder.byte[] -> ByteString} -> Message", manyDistinctElements(), + arguments( + new TypeHolder<@NotNull BytesField3>() {}.annotatedType(), + "{Builder.byte[] -> ByteString} -> Message", + manyDistinctElements(), manyDistinctElements()), - arguments(new TypeHolder<@NotNull StringField3>() {}.annotatedType(), - "{Builder.String} -> Message", manyDistinctElements(), manyDistinctElements()), - arguments(new TypeHolder<@NotNull EnumField3>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull StringField3>() {}.annotatedType(), + "{Builder.String} -> Message", + manyDistinctElements(), + manyDistinctElements()), + arguments( + new TypeHolder<@NotNull EnumField3>() {}.annotatedType(), "{Builder.Enum} -> Message", - exactly(EnumField3.getDefaultInstance(), + exactly( + EnumField3.getDefaultInstance(), EnumField3.newBuilder().setSomeField(TestEnum.VAL2).build()), - exactly(EnumField3.getDefaultInstance(), + exactly( + EnumField3.getDefaultInstance(), EnumField3.newBuilder().setSomeField(TestEnum.VAL2).build())), - arguments(new TypeHolder<@NotNull EnumFieldRepeated3>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull EnumFieldRepeated3>() {}.annotatedType(), "{Builder via List>} -> Message", - exactly(EnumFieldRepeated3.getDefaultInstance(), + exactly( + EnumFieldRepeated3.getDefaultInstance(), EnumFieldRepeated3.newBuilder().addSomeField(TestEnumRepeated.UNASSIGNED).build(), EnumFieldRepeated3.newBuilder().addSomeField(TestEnumRepeated.VAL1).build(), EnumFieldRepeated3.newBuilder().addSomeField(TestEnumRepeated.VAL2).build()), manyDistinctElements()), - arguments(new TypeHolder<@NotNull MapField3>() {}.annotatedType(), - "{Builder.Map} -> Message", distinctElementsRatio(0.47), + arguments( + new TypeHolder<@NotNull MapField3>() {}.annotatedType(), + "{Builder.Map} -> Message", + distinctElementsRatio(0.47), manyDistinctElements()), - arguments(new TypeHolder<@NotNull MessageMapField3>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull MessageMapField3>() {}.annotatedType(), "{Builder.Map} -> Message>} -> Message", - distinctElementsRatio(0.45), distinctElementsRatio(0.45)), - arguments(new TypeHolder<@NotNull DoubleField3>() {}.annotatedType(), - "{Builder.Double} -> Message", distinctElementsRatio(0.45), distinctElementsRatio(0.7)), - arguments(new TypeHolder<@NotNull RepeatedDoubleField3>() {}.annotatedType(), - "{Builder via List} -> Message", distinctElementsRatio(0.2), + distinctElementsRatio(0.45), + distinctElementsRatio(0.45)), + arguments( + new TypeHolder<@NotNull DoubleField3>() {}.annotatedType(), + "{Builder.Double} -> Message", + distinctElementsRatio(0.45), + distinctElementsRatio(0.7)), + arguments( + new TypeHolder<@NotNull RepeatedDoubleField3>() {}.annotatedType(), + "{Builder via List} -> Message", + distinctElementsRatio(0.2), distinctElementsRatio(0.9)), - arguments(new TypeHolder<@NotNull FloatField3>() {}.annotatedType(), - "{Builder.Float} -> Message", distinctElementsRatio(0.45), distinctElementsRatio(0.7)), - arguments(new TypeHolder<@NotNull RepeatedFloatField3>() {}.annotatedType(), - "{Builder via List} -> Message", distinctElementsRatio(0.20), - distinctElementsRatio(0.9), emptyList()), - arguments(new TypeHolder<@NotNull TestProtobuf>() {}.annotatedType(), - "{Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable>, WithoutInit(Builder.Nullable<{Builder.Nullable, Builder via List, WithoutInit(Builder.Nullable<(cycle) -> Message>)} -> Message>), Builder via List, Builder via List, Builder via List, Builder via List, Builder via List, Builder via List, Builder via List, Builder via List, Builder via List>, WithoutInit(Builder via List<(cycle) -> Message>), Builder.Map, Builder.Nullable, Builder.Nullable<{} -> Message>, Builder.Nullable | Builder.Nullable | Builder.Nullable} -> Message", - manyDistinctElements(), manyDistinctElements()), - arguments( - new TypeHolder<@NotNull @WithDefaultInstance( - "com.code_intelligence.jazzer.mutation.mutator.StressTest#getTestProtobufDefaultInstance") - Message>() { - }.annotatedType(), - "{Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable, Builder.Nullable>, WithoutInit(Builder.Nullable<{Builder.Nullable, Builder via List, WithoutInit(Builder.Nullable<(cycle) -> Message>)} -> Message>), Builder via List, Builder via List, Builder via List, Builder via List, Builder via List, Builder via List, Builder via List, Builder via List, Builder via List>, WithoutInit(Builder via List<(cycle) -> Message>), Builder.Map, Builder.Nullable, Builder.Nullable<{} -> Message>, Builder.Nullable | Builder.Nullable | Builder.Nullable} -> Message", - manyDistinctElements(), manyDistinctElements()), - arguments( - new TypeHolder<@NotNull @AnySource( - {PrimitiveField3.class, MessageField3.class}) AnyField3>() { - }.annotatedType(), - "{Builder.Nullable Message | Builder.{Builder.Nullable<(cycle) -> Message>} -> Message -> Message>} -> Message", - exactly(AnyField3.getDefaultInstance(), + arguments( + new TypeHolder<@NotNull FloatField3>() {}.annotatedType(), + "{Builder.Float} -> Message", + distinctElementsRatio(0.45), + distinctElementsRatio(0.7)), + arguments( + new TypeHolder<@NotNull RepeatedFloatField3>() {}.annotatedType(), + "{Builder via List} -> Message", + distinctElementsRatio(0.20), + distinctElementsRatio(0.9), + emptyList()), + arguments( + new TypeHolder<@NotNull TestProtobuf>() {}.annotatedType(), + "{Builder.Nullable, Builder.Nullable, Builder.Nullable," + + " Builder.Nullable, Builder.Nullable, Builder.Nullable," + + " Builder.Nullable, Builder.Nullable," + + " Builder.Nullable>," + + " WithoutInit(Builder.Nullable<{Builder.Nullable, Builder via" + + " List, WithoutInit(Builder.Nullable<(cycle) -> Message>)} -> Message>)," + + " Builder via List, Builder via List, Builder via" + + " List, Builder via List, Builder via List, Builder via" + + " List, Builder via List, Builder via List, Builder via" + + " List>, WithoutInit(Builder via List<(cycle) -> Message>)," + + " Builder.Map, Builder.Nullable," + + " Builder.Nullable<{} -> Message>, Builder.Nullable |" + + " Builder.Nullable | Builder.Nullable} -> Message", + manyDistinctElements(), + manyDistinctElements()), + arguments( + new TypeHolder< + @NotNull + @WithDefaultInstance( + "com.code_intelligence.jazzer.mutation.mutator.StressTest#getTestProtobufDefaultInstance") + Message>() {}.annotatedType(), + "{Builder.Nullable, Builder.Nullable, Builder.Nullable," + + " Builder.Nullable, Builder.Nullable, Builder.Nullable," + + " Builder.Nullable, Builder.Nullable," + + " Builder.Nullable>," + + " WithoutInit(Builder.Nullable<{Builder.Nullable, Builder via" + + " List, WithoutInit(Builder.Nullable<(cycle) -> Message>)} -> Message>)," + + " Builder via List, Builder via List, Builder via" + + " List, Builder via List, Builder via List, Builder via" + + " List, Builder via List, Builder via List, Builder via" + + " List>, WithoutInit(Builder via List<(cycle) -> Message>)," + + " Builder.Map, Builder.Nullable," + + " Builder.Nullable<{} -> Message>, Builder.Nullable |" + + " Builder.Nullable | Builder.Nullable} -> Message", + manyDistinctElements(), + manyDistinctElements()), + arguments( + new TypeHolder< + @NotNull @AnySource({PrimitiveField3.class, MessageField3.class}) + AnyField3>() {}.annotatedType(), + "{Builder.Nullable Message |" + + " Builder.{Builder.Nullable<(cycle) -> Message>} -> Message -> Message>} ->" + + " Message", + exactly( + AnyField3.getDefaultInstance(), AnyField3.newBuilder() .setSomeField(Any.pack(PrimitiveField3.getDefaultInstance())) .build(), @@ -331,17 +472,20 @@ public static Stream protoStressTestCases() { .build(), AnyField3.newBuilder() .setSomeField( - Any.pack(MessageField3.newBuilder() - .setMessageField(PrimitiveField3.getDefaultInstance()) - .build())) + Any.pack( + MessageField3.newBuilder() + .setMessageField(PrimitiveField3.getDefaultInstance()) + .build())) .build(), AnyField3.newBuilder() - .setSomeField(Any.pack( - MessageField3.newBuilder() - .setMessageField(PrimitiveField3.newBuilder().setSomeField(true)) - .build())) + .setSomeField( + Any.pack( + MessageField3.newBuilder() + .setMessageField(PrimitiveField3.newBuilder().setSomeField(true)) + .build())) .build()), - exactly(AnyField3.getDefaultInstance(), + exactly( + AnyField3.getDefaultInstance(), AnyField3.newBuilder() .setSomeField(Any.pack(PrimitiveField3.getDefaultInstance())) .build(), @@ -353,22 +497,27 @@ public static Stream protoStressTestCases() { .build(), AnyField3.newBuilder() .setSomeField( - Any.pack(MessageField3.newBuilder() - .setMessageField(PrimitiveField3.getDefaultInstance()) - .build())) + Any.pack( + MessageField3.newBuilder() + .setMessageField(PrimitiveField3.getDefaultInstance()) + .build())) .build(), AnyField3.newBuilder() - .setSomeField(Any.pack( - MessageField3.newBuilder() - .setMessageField(PrimitiveField3.newBuilder().setSomeField(true)) - .build())) + .setSomeField( + Any.pack( + MessageField3.newBuilder() + .setMessageField(PrimitiveField3.newBuilder().setSomeField(true)) + .build())) .build())), - arguments(new TypeHolder<@NotNull SingleOptionOneOfField3>() {}.annotatedType(), + arguments( + new TypeHolder<@NotNull SingleOptionOneOfField3>() {}.annotatedType(), "{Builder.Nullable} -> Message", - exactly(SingleOptionOneOfField3.getDefaultInstance(), + exactly( + SingleOptionOneOfField3.getDefaultInstance(), SingleOptionOneOfField3.newBuilder().setBoolField(false).build(), SingleOptionOneOfField3.newBuilder().setBoolField(true).build()), - exactly(SingleOptionOneOfField3.getDefaultInstance(), + exactly( + SingleOptionOneOfField3.getDefaultInstance(), SingleOptionOneOfField3.newBuilder().setBoolField(false).build(), SingleOptionOneOfField3.newBuilder().setBoolField(true).build()))); } @@ -413,18 +562,20 @@ private static Consumer> expectedNumberOfDistinctElements( long domainSize, int picks) { // https://www.randomservices.org/random/urn/Birthday.html#mom2 double expectedValue = domainSize * (1 - pow(1 - 1.0 / domainSize, picks)); - double variance = domainSize * (domainSize - 1) * pow(1 - 2.0 / domainSize, picks) - + domainSize * pow(1 - 1.0 / domainSize, picks) - - domainSize * domainSize * pow(1 - 1.0 / domainSize, 2 * picks); + double variance = + domainSize * (domainSize - 1) * pow(1 - 2.0 / domainSize, picks) + + domainSize * pow(1 - 1.0 / domainSize, picks) + - domainSize * domainSize * pow(1 - 1.0 / domainSize, 2 * picks); double standardDeviation = sqrt(variance); // Allow missing the expected value by two standard deviations. For a normal distribution, // this would correspond to 95% of all cases. int almostCertainLowerBound = (int) floor(expectedValue - 2 * standardDeviation); - return list - -> assertWithMessage("V=distinct elements among %s picked out of %s\nE[V]=%s\nσ[V]=%s", - picks, domainSize, expectedValue, standardDeviation) - .that(new HashSet<>(list).size()) - .isAtLeast(almostCertainLowerBound); + return list -> + assertWithMessage( + "V=distinct elements among %s picked out of %s\nE[V]=%s\nσ[V]=%s", + picks, domainSize, expectedValue, standardDeviation) + .that(new HashSet<>(list).size()) + .isAtLeast(almostCertainLowerBound); } private static Consumer> distinctElementsRatio(double ratio) { @@ -447,22 +598,26 @@ private static Consumer> doesNotContain(Object... expected) { private static Consumer> mapSizeInClosedRange(int min, int max) { return list -> { - list.forEach(map -> { - if (map instanceof Map) { - assertThat(((Map) map).size()).isAtLeast(min); - assertThat(((Map) map).size()).isAtMost(max); - } else { - throw new IllegalArgumentException( - "Expected a list of maps, got list of" + map.getClass().getName()); - } - }); + list.forEach( + map -> { + if (map instanceof Map) { + assertThat(((Map) map).size()).isAtLeast(min); + assertThat(((Map) map).size()).isAtMost(max); + } else { + throw new IllegalArgumentException( + "Expected a list of maps, got list of" + map.getClass().getName()); + } + }); }; } @ParameterizedTest(name = "{index} {0}, {1}") @MethodSource({"stressTestCases", "protoStressTestCases"}) - void genericMutatorStressTest(AnnotatedType type, String mutatorTree, - Consumer> expectedInitValues, Consumer> expectedMutatedValues) + void genericMutatorStressTest( + AnnotatedType type, + String mutatorTree, + Consumer> expectedInitValues, + Consumer> expectedMutatedValues) throws IOException { validateAnnotationUsage(type); SerializingMutator mutator = Mutators.newFactory().createOrThrow(type); @@ -534,8 +689,9 @@ private static void testReadWriteRoundtrip(Serializer serializer, T value throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); serializer.write(value, new DataOutputStream(out)); - T newValue = serializer.read( - new DataInputStream(extendWithZeros(new ByteArrayInputStream(out.toByteArray())))); + T newValue = + serializer.read( + new DataInputStream(extendWithZeros(new ByteArrayInputStream(out.toByteArray())))); assertEqualMutatorValues(newValue, value); } @@ -548,15 +704,17 @@ private static T fixFloatingPointsForProtos(T value) { return value; } Message.Builder builder = ((Message) value).toBuilder(); - walkFields(builder, oldValue -> { - if (Objects.equals(oldValue, -0.0)) { - return 0.0; - } else if (Objects.equals(oldValue, -0.0f)) { - return 0.0f; - } else { - return oldValue; - } - }); + walkFields( + builder, + oldValue -> { + if (Objects.equals(oldValue, -0.0)) { + return 0.0; + } else if (Objects.equals(oldValue, -0.0f)) { + return 0.0f; + } else { + return oldValue; + } + }); return (T) builder.build(); } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutationsTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutationsTest.java index 2fa0c1cf5..eb73b4ed4 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutationsTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutationsTest.java @@ -72,8 +72,9 @@ void testInsertRandomChunkSet() { Stream.of(7, 7, 7, 8, 9, 9).collect(toCollection(ArrayDeque::new)); boolean result; try (MockPseudoRandom prng = mockPseudoRandom(3)) { - result = ChunkMutations.insertRandomChunk( - set, set::add, 10, mockInitializer(initReturnValues::remove, v -> v), prng); + result = + ChunkMutations.insertRandomChunk( + set, set::add, 10, mockInitializer(initReturnValues::remove, v -> v), prng); } assertThat(result).isTrue(); assertThat(set).containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9).inOrder(); @@ -87,8 +88,9 @@ void testInsertRandomChunkSet_largeChunk() { IntStream.rangeClosed(1, 10000).boxed().collect(toCollection(ArrayDeque::new)); boolean result; try (MockPseudoRandom prng = mockPseudoRandom(9994)) { - result = ChunkMutations.insertRandomChunk( - set, set::add, 10000, mockInitializer(initReturnValues::remove, v -> v), prng); + result = + ChunkMutations.insertRandomChunk( + set, set::add, 10000, mockInitializer(initReturnValues::remove, v -> v), prng); } assertThat(result).isTrue(); assertThat(set) @@ -105,8 +107,9 @@ void testInsertRandomChunkSet_failsToConstructDistinctValues() { .collect(toCollection(ArrayDeque::new)); boolean result; try (MockPseudoRandom prng = mockPseudoRandom(3)) { - result = ChunkMutations.insertRandomChunk( - set, set::add, 10, mockInitializer(initReturnValues::remove, v -> v), prng); + result = + ChunkMutations.insertRandomChunk( + set, set::add, 10, mockInitializer(initReturnValues::remove, v -> v), prng); } assertThat(result).isFalse(); assertThat(set).containsExactly(1, 2, 3, 4, 5, 6, 7, 8).inOrder(); @@ -134,94 +137,214 @@ void testMutateRandomValuesChunk() { @Test void testMutateRandomKeysChunk() { - Map, Integer> map = asMap(asMutableList(1), 10, asMutableList(2), 20, - asMutableList(3), 30, asMutableList(4), 40, asMutableList(5), 50, asMutableList(6), 60); - SerializingMutator> keyMutator = mockMutator(null, list -> { - List newList = list.stream().map(i -> i + 1).collect(toList()); - list.clear(); - return newList; - }, ArrayList::new); + Map, Integer> map = + asMap( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(4), + 40, + asMutableList(5), + 50, + asMutableList(6), + 60); + SerializingMutator> keyMutator = + mockMutator( + null, + list -> { + List newList = list.stream().map(i -> i + 1).collect(toList()); + list.clear(); + return newList; + }, + ArrayList::new); try (MockPseudoRandom prng = mockPseudoRandom(2, 3)) { boolean result = ChunkMutations.mutateRandomKeysChunk(map, keyMutator, prng); assertThat(result).isTrue(); } assertThat(map) - .containsExactly(asMutableList(1), 10, asMutableList(2), 20, asMutableList(3), 30, - asMutableList(6), 60, asMutableList(7), 40, asMutableList(8), 50) + .containsExactly( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(6), + 60, + asMutableList(7), + 40, + asMutableList(8), + 50) .inOrder(); } @Test void testMutateRandomKeysChunk_failsToConstructSomeDistinctKeys() { - Map, Integer> map = asMap(asMutableList(1), 10, asMutableList(2), 20, - asMutableList(3), 30, asMutableList(4), 40, asMutableList(5), 50, asMutableList(6), 60); - SerializingMutator> keyMutator = mockMutator(null, list -> { - list.clear(); - List newList = new ArrayList<>(); - newList.add(7); - return newList; - }, ArrayList::new); + Map, Integer> map = + asMap( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(4), + 40, + asMutableList(5), + 50, + asMutableList(6), + 60); + SerializingMutator> keyMutator = + mockMutator( + null, + list -> { + list.clear(); + List newList = new ArrayList<>(); + newList.add(7); + return newList; + }, + ArrayList::new); try (MockPseudoRandom prng = mockPseudoRandom(2, 3)) { boolean result = ChunkMutations.mutateRandomKeysChunk(map, keyMutator, prng); assertThat(result).isTrue(); } assertThat(map) - .containsExactly(asMutableList(1), 10, asMutableList(2), 20, asMutableList(3), 30, - asMutableList(5), 50, asMutableList(6), 60, asMutableList(7), 40) + .containsExactly( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(5), + 50, + asMutableList(6), + 60, + asMutableList(7), + 40) .inOrder(); } @Test void testMutateRandomKeysChunk_failsToConstructAnyDistinctKeys() { - Map, Integer> map = asMap(asMutableList(1), 10, asMutableList(2), 20, - asMutableList(3), 30, asMutableList(4), 40, asMutableList(5), 50, asMutableList(6), 60); - SerializingMutator> keyMutator = mockMutator(null, list -> { - list.clear(); - List newList = new ArrayList<>(); - newList.add(1); - return newList; - }, ArrayList::new); + Map, Integer> map = + asMap( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(4), + 40, + asMutableList(5), + 50, + asMutableList(6), + 60); + SerializingMutator> keyMutator = + mockMutator( + null, + list -> { + list.clear(); + List newList = new ArrayList<>(); + newList.add(1); + return newList; + }, + ArrayList::new); try (MockPseudoRandom prng = mockPseudoRandom(2, 3)) { boolean result = ChunkMutations.mutateRandomKeysChunk(map, keyMutator, prng); assertThat(result).isFalse(); } assertThat(map) - .containsExactly(asMutableList(1), 10, asMutableList(2), 20, asMutableList(3), 30, - asMutableList(4), 40, asMutableList(5), 50, asMutableList(6), 60) + .containsExactly( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(4), + 40, + asMutableList(5), + 50, + asMutableList(6), + 60) .inOrder(); } @Test void testMutateRandomKeysChunk_nullKeyAndValue() { - Map, Integer> map = asMap(asMutableList(1), 10, asMutableList(2), 20, - asMutableList(3), 30, asMutableList(4), null, null, 50, asMutableList(6), 60); - SerializingMutator> keyMutator = mockMutator(null, list -> { - if (list != null) { - List newList = list.stream().map(i -> i + 1).collect(toList()); - list.clear(); - return newList; - } else { - return asMutableList(10); - } - }, list -> list != null ? new ArrayList<>(list) : null); + Map, Integer> map = + asMap( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(4), + null, + null, + 50, + asMutableList(6), + 60); + SerializingMutator> keyMutator = + mockMutator( + null, + list -> { + if (list != null) { + List newList = list.stream().map(i -> i + 1).collect(toList()); + list.clear(); + return newList; + } else { + return asMutableList(10); + } + }, + list -> list != null ? new ArrayList<>(list) : null); try (MockPseudoRandom prng = mockPseudoRandom(2, 3)) { boolean result = ChunkMutations.mutateRandomKeysChunk(map, keyMutator, prng); assertThat(result).isTrue(); } assertThat(map) - .containsExactly(asMutableList(1), 10, asMutableList(2), 20, asMutableList(3), 30, - asMutableList(6), 60, asMutableList(5), null, asMutableList(10), 50) + .containsExactly( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(6), + 60, + asMutableList(5), + null, + asMutableList(10), + 50) .inOrder(); } @Test void testMutateRandomKeysChunk_mutateKeyToNull() { - Map, Integer> map = asMap(asMutableList(1), 10, asMutableList(2), 20, - asMutableList(3), 30, asMutableList(4), 40, asMutableList(5), 50, asMutableList(6), 60); + Map, Integer> map = + asMap( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(4), + 40, + asMutableList(5), + 50, + asMutableList(6), + 60); SerializingMutator> keyMutator = mockMutator(null, list -> null, list -> list != null ? new ArrayList<>(list) : null); @@ -230,8 +353,19 @@ void testMutateRandomKeysChunk_mutateKeyToNull() { assertThat(result).isTrue(); } assertThat(map) - .containsExactly(asMutableList(1), 10, asMutableList(2), 20, asMutableList(3), 30, - asMutableList(5), 50, asMutableList(6), 60, null, 40) + .containsExactly( + asMutableList(1), + 10, + asMutableList(2), + 20, + asMutableList(3), + 30, + asMutableList(5), + 50, + asMutableList(6), + 60, + null, + 40) .inOrder(); } } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorTest.java index bc53ed817..728962e6e 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorTest.java @@ -50,11 +50,12 @@ void testInit() { assertThat(mutator.toString()).isEqualTo("List"); List list; - try (MockPseudoRandom prng = mockPseudoRandom( - // targetSize - 1, - // elementMutator.init - 1)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // targetSize + 1, + // elementMutator.init + 1)) { list = mutator.init(prng); } assertThat(list).containsExactly(0); @@ -63,8 +64,8 @@ void testInit() { @Test void testInitMaxSize() { AnnotatedType type = - new TypeHolder<@NotNull @WithSize(min = 2, max = 3) List<@NotNull Integer>>(){} - .annotatedType(); + new TypeHolder< + @NotNull @WithSize(min = 2, max = 3) List<@NotNull Integer>>() {}.annotatedType(); SerializingMutator<@NotNull List<@NotNull Integer>> mutator = (SerializingMutator<@NotNull List<@NotNull Integer>>) FACTORY.createOrThrow(type); @@ -83,13 +84,14 @@ void testRemoveSingleElement() { SerializingMutator<@NotNull List<@NotNull Integer>> mutator = defaultListMutator(); List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)); - try (MockPseudoRandom prng = mockPseudoRandom( - // action - 0, - // number of elements to remove - 1, - // index to remove - 2)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // action + 0, + // number of elements to remove + 1, + // index to remove + 2)) { list = mutator.mutate(list, prng); } assertThat(list).containsExactly(1, 2, 4, 5, 6, 7, 8, 9).inOrder(); @@ -100,13 +102,14 @@ void testRemoveChunk() { SerializingMutator<@NotNull List<@NotNull Integer>> mutator = defaultListMutator(); List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)); - try (MockPseudoRandom prng = mockPseudoRandom( - // action - 0, - // chunk size - 2, - // chunk offset - 3)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // action + 0, + // chunk size + 2, + // chunk offset + 3)) { list = mutator.mutate(list, prng); } assertThat(list).containsExactly(1, 2, 3, 6, 7, 8, 9).inOrder(); @@ -117,17 +120,18 @@ void testAddSingleElement() { SerializingMutator<@NotNull List<@NotNull Integer>> mutator = defaultListMutator(); List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)); - try (MockPseudoRandom prng = mockPseudoRandom( - // action - 1, - // add single element, - 1, - // offset, - 9, - // Integral initImpl sentinel value - 4, - // value - 42L)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // action + 1, + // add single element, + 1, + // offset, + 9, + // Integral initImpl sentinel value + 4, + // value + 42L)) { list = mutator.mutate(list, prng); } assertThat(list).containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9, 42).inOrder(); @@ -138,17 +142,18 @@ void testAddChunk() { SerializingMutator<@NotNull List<@NotNull Integer>> mutator = defaultListMutator(); List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)); - try (MockPseudoRandom prng = mockPseudoRandom( - // action - 1, - // chunkSize - 2, - // chunkOffset - 3, - // Integral initImpl - 4, - // val - 42L)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // action + 1, + // chunkSize + 2, + // chunkOffset + 3, + // Integral initImpl + 4, + // val + 42L)) { list = mutator.mutate(list, prng); } assertThat(list).containsExactly(1, 2, 3, 42, 42, 4, 5, 6, 7, 8, 9).inOrder(); @@ -159,18 +164,19 @@ void testChangeSingleElement() { SerializingMutator<@NotNull List<@NotNull Integer>> mutator = defaultListMutator(); List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)); - try (MockPseudoRandom prng = mockPseudoRandom( - // action - 2, - // number of elements to mutate - 1, - // first index to mutate at - 2, - // mutation choice based on `IntegralMutatorFactory` - // 2 == closedRange - 2, - // value - 55L)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // action + 2, + // number of elements to mutate + 1, + // first index to mutate at + 2, + // mutation choice based on `IntegralMutatorFactory` + // 2 == closedRange + 2, + // value + 55L)) { list = mutator.mutate(list, prng); } assertThat(list).containsExactly(1, 2, 55, 4, 5, 6, 7, 8, 9).inOrder(); @@ -181,19 +187,21 @@ void testChangeChunk() { SerializingMutator<@NotNull List<@NotNull Integer>> mutator = defaultListMutator(); List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); - try (MockPseudoRandom prng = mockPseudoRandom( - // action - 2, - // number of elements to mutate - 2, - // first index to mutate at - 5, - // mutation: 0 == bitflip - 0, - // shift constant - 13, - // and again - 0, 12)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // action + 2, + // number of elements to mutate + 2, + // first index to mutate at + 5, + // mutation: 0 == bitflip + 0, + // shift constant + 13, + // and again + 0, + 12)) { list = mutator.mutate(list, prng); } assertThat(list).containsExactly(1, 2, 3, 4, 5, 8198, 4103, 8, 9, 10, 11).inOrder(); @@ -216,15 +224,16 @@ void testCrossOverInsertChunk() { List list = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); List otherList = new ArrayList<>(Arrays.asList(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)); - try (MockPseudoRandom prng = mockPseudoRandom( - // insert action - 0, - // chunk size - 3, - // fromPos - 2, - // toPos - 5)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // insert action + 0, + // chunk size + 3, + // fromPos + 2, + // toPos + 5)) { list = mutator.crossOver(list, otherList, prng); } assertThat(list).containsExactly(0, 1, 2, 3, 4, 12, 13, 14, 5, 6, 7, 8, 9).inOrder(); @@ -236,15 +245,16 @@ void testCrossOverInsertChunk_chunkBiggerThanList() { List list = new ArrayList<>(emptyList()); List otherList = new ArrayList<>(Arrays.asList(10, 11, 12)); - try (MockPseudoRandom prng = mockPseudoRandom( - // insert action - 0, - // chunk size - 2, - // fromPos - 1, - // toPos - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // insert action + 0, + // chunk size + 2, + // fromPos + 1, + // toPos + 0)) { list = mutator.crossOver(list, otherList, prng); } assertThat(list).containsExactly(11, 12).inOrder(); @@ -257,15 +267,16 @@ void testCrossOverOverwriteChunk() { List list = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); List otherList = new ArrayList<>(Arrays.asList(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)); - try (MockPseudoRandom prng = mockPseudoRandom( - // overwrite action - 1, - // chunk size - 3, - // fromPos - 2, - // toPos - 5)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // overwrite action + 1, + // chunk size + 3, + // fromPos + 2, + // toPos + 5)) { list = mutator.crossOver(list, otherList, prng); } assertThat(list).containsExactly(0, 1, 2, 3, 4, 12, 13, 14, 8, 9).inOrder(); @@ -278,21 +289,22 @@ void testCrossOverCrossOverChunk() { List list = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); List otherList = new ArrayList<>(Arrays.asList(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)); - try (MockPseudoRandom prng = mockPseudoRandom( - // overwrite action - 2, - // chunk size - 3, - // fromPos - 2, - // toPos - 2, - // mean value in sub cross over - 0, - // mean value in sub cross over - 0, - // mean value in sub cross over - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // overwrite action + 2, + // chunk size + 3, + // fromPos + 2, + // toPos + 2, + // mean value in sub cross over + 0, + // mean value in sub cross over + 0, + // mean value in sub cross over + 0)) { list = mutator.crossOver(list, otherList, prng); } assertThat(list).containsExactly(0, 1, 7, 8, 9, 5, 6, 7, 8, 9).inOrder(); diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorTest.java index ed50226c2..2f5cf4e50 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorTest.java @@ -49,51 +49,53 @@ private static SerializingMutator> defaultTestMapMutator() @Test void mapInitInsert() { AnnotatedType type = - new TypeHolder<@NotNull @WithSize(max = 3) Map<@NotNull String, @NotNull String>>(){} - .annotatedType(); + new TypeHolder< + @NotNull @WithSize(max = 3) Map<@NotNull String, @NotNull String>>() {}.annotatedType(); SerializingMutator> mutator = (SerializingMutator>) FACTORY.createOrThrow(type); assertThat(mutator.toString()).isEqualTo("Map"); // Initialize new map Map map; - try (MockPseudoRandom prng = mockPseudoRandom( - // Initial map size - 1, - // Key 1 size - 4, - // Key 1 value - "Key1".getBytes(), - // Value size - 6, - // Value value - "Value1".getBytes())) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // Initial map size + 1, + // Key 1 size + 4, + // Key 1 value + "Key1".getBytes(), + // Value size + 6, + // Value value + "Value1".getBytes())) { map = mutator.init(prng); } assertThat(map).containsExactly("Key1", "Value1"); // Add 2 new entries - try (MockPseudoRandom prng = mockPseudoRandom( - // grow chunk - 1, - // ChunkSize - 2, - // Key 2 size - 4, - // Key 2 value - "Key2".getBytes(), - // Value size - 6, - // Value value - "Value2".getBytes(), - // Key 3 size - 4, - // Key 3 value - "Key3".getBytes(), - // Value size - 6, - // Value value - "Value3".getBytes())) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // grow chunk + 1, + // ChunkSize + 2, + // Key 2 size + 4, + // Key 2 value + "Key2".getBytes(), + // Value size + 6, + // Value value + "Value2".getBytes(), + // Key 3 size + 4, + // Key 3 value + "Key3".getBytes(), + // Value size + 6, + // Value value + "Value3".getBytes())) { map = mutator.mutate(map, prng); } assertThat(map).containsExactly("Key1", "Value1", "Key2", "Value2", "Key3", "Value3").inOrder(); @@ -109,13 +111,14 @@ void mapDelete() { Map map = asMap(1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60); - try (MockPseudoRandom prng = mockPseudoRandom( - // delete chunk - 0, - // chunk size - 2, - // chunk position - 3)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // delete chunk + 0, + // chunk size + 2, + // chunk position + 3)) { map = mutator.mutate(map, prng); } assertThat(map).containsExactly(1, 10, 2, 20, 3, 30, 6, 60).inOrder(); @@ -131,23 +134,24 @@ void mapMutateValues() { Map map = asMap(1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60); - try (MockPseudoRandom prng = mockPseudoRandom( - // change chunk - 2, - // mutate values, - true, - // chunk size - 2, - // chunk position - 3, - // uniform pick - 2, - // random integer - 41L, - // uniform pick - 2, - // random integer - 51L)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // change chunk + 2, + // mutate values, + true, + // chunk size + 2, + // chunk position + 3, + // uniform pick + 2, + // random integer + 41L, + // uniform pick + 2, + // random integer + 51L)) { map = mutator.mutate(map, prng); } assertThat(map).containsExactly(1, 10, 2, 20, 3, 30, 4, 41, 5, 51, 6, 60).inOrder(); @@ -163,23 +167,24 @@ void mapMutateKeys() { Map map = asMap(1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60); - try (MockPseudoRandom prng = mockPseudoRandom( - // change chunk - 2, - // mutate keys, - false, - // chunk size - 2, - // chunk position - 3, - // uniform pick - 2, - // integer - 7L, - // uniform pick - 2, - // random integer - 8L)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // change chunk + 2, + // mutate keys, + false, + // chunk size + 2, + // chunk position + 3, + // uniform pick + 2, + // integer + 7L, + // uniform pick + 2, + // random integer + 8L)) { map = mutator.mutate(map, prng); } assertThat(map).containsExactly(1, 10, 2, 20, 3, 30, 6, 60, 7, 40, 8, 50).inOrder(); @@ -196,19 +201,20 @@ void mapMutateKeysFallbackToValues() { // No new keys can be generated for this map. Map map = asMap(false, false, true, false); - try (MockPseudoRandom prng = mockPseudoRandom( - // change chunk - 2, - // mutate keys, - false, - // chunk size - 1, - // chunk position - 0, - // chunk size for fallback to mutate values - 2, - // chunk position for fallback - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // change chunk + 2, + // mutate keys, + false, + // chunk size + 1, + // chunk position + 0, + // chunk size for fallback to mutate values + 2, + // chunk position for fallback + 0)) { map = mutator.mutate(map, prng); } assertThat(map).containsExactly(false, true, true, true).inOrder(); @@ -233,13 +239,14 @@ void testCrossOverInsertChunk() { Map map = asMap(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6); Map otherMap = asMap(1, 1, 2, 2, 3, 3, 40, 40, 50, 50, 60, 60); - try (MockPseudoRandom prng = mockPseudoRandom( - // insert action - 0, - // chunk size - 3, - // from chunk offset, will skip first element of chunk as it is already present in map - 3)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // insert action + 0, + // chunk size + 3, + // from chunk offset, will skip first element of chunk as it is already present in map + 3)) { map = mutator.crossOver(map, otherMap, prng); assertThat(map) .containsExactly(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 40, 40, 50, 50, 60, 60) @@ -255,13 +262,14 @@ void testCrossOverInsertChunk_chunkBiggerThanMap() { Map map = asMap(3, 3); Map otherMap = asMap(1, 2, 3, 4, 5, 6, 7, 8); - try (MockPseudoRandom prng = mockPseudoRandom( - // insert action - 0, - // chunk size - 2, - // from chunk offset, will skip first element of chunk as it is already present in map - 1)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // insert action + 0, + // chunk size + 2, + // from chunk offset, will skip first element of chunk as it is already present in map + 1)) { map = mutator.crossOver(map, otherMap, prng); assertThat(map).containsExactly(3, 3, 5, 6, 7, 8).inOrder(); } @@ -275,15 +283,16 @@ void testCrossOverOverwriteChunk() { Map map = asMap(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6); Map otherMap = asMap(1, 1, 2, 2, 3, 3, 40, 40, 50, 50, 60, 60); - try (MockPseudoRandom prng = mockPseudoRandom( - // overwrite action - 1, - // chunk size - 3, - // from chunk offset - 2, - // to chunk offset, will not change first element as values are equal - 2)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // overwrite action + 1, + // chunk size + 3, + // from chunk offset + 2, + // to chunk offset, will not change first element as values are equal + 2)) { map = mutator.crossOver(map, otherMap, prng); assertThat(map).containsExactly(1, 1, 2, 2, 3, 3, 4, 40, 5, 50, 6, 6).inOrder(); } @@ -292,40 +301,85 @@ void testCrossOverOverwriteChunk() { @Test void testCrossOverCrossOverChunkKeys() { AnnotatedType type = - new TypeHolder<@NotNull Map<@NotNull List<@NotNull Integer>, @NotNull Integer>>() { - }.annotatedType(); + new TypeHolder< + @NotNull Map<@NotNull List<@NotNull Integer>, @NotNull Integer>>() {}.annotatedType(); SerializingMutator<@NotNull Map<@NotNull List<@NotNull Integer>, @NotNull Integer>> mutator = (SerializingMutator<@NotNull Map<@NotNull List<@NotNull Integer>, @NotNull Integer>>) FACTORY.createOrThrow(type); - Map, Integer> map = asMap(asMutableList(1), 1, asMutableList(2), 2, - asMutableList(3), 3, asMutableList(4), 4, asMutableList(5), 5, asMutableList(6), 6); - Map, Integer> otherMap = asMap(asMutableList(1), 1, asMutableList(2), 2, - asMutableList(3), 3, asMutableList(40), 4, asMutableList(50), 5, asMutableList(60), 6); - - try (MockPseudoRandom prng = mockPseudoRandom( - // cross over action - 2, - // keys - true, - // chunk size - 3, - // from chunk offset - 2, - // to chunk offset, - // first keys ("3") are equal and will be overwritten - 2, - // first key, delegate to list cross over, overwrite 1 entry at offset 0 from offset 0 - 1, 1, 0, 0, - // second key, delegate to list cross over, overwrite 1 entry at offset 0 from offset 0 - 1, 1, 0, 0, - // third key, delegate to list cross over, overwrite 1 entry at offset 0 from offset 0 - 1, 1, 0, 0)) { + Map, Integer> map = + asMap( + asMutableList(1), + 1, + asMutableList(2), + 2, + asMutableList(3), + 3, + asMutableList(4), + 4, + asMutableList(5), + 5, + asMutableList(6), + 6); + Map, Integer> otherMap = + asMap( + asMutableList(1), + 1, + asMutableList(2), + 2, + asMutableList(3), + 3, + asMutableList(40), + 4, + asMutableList(50), + 5, + asMutableList(60), + 6); + + try (MockPseudoRandom prng = + mockPseudoRandom( + // cross over action + 2, + // keys + true, + // chunk size + 3, + // from chunk offset + 2, + // to chunk offset, + // first keys ("3") are equal and will be overwritten + 2, + // first key, delegate to list cross over, overwrite 1 entry at offset 0 from offset 0 + 1, + 1, + 0, + 0, + // second key, delegate to list cross over, overwrite 1 entry at offset 0 from offset 0 + 1, + 1, + 0, + 0, + // third key, delegate to list cross over, overwrite 1 entry at offset 0 from offset 0 + 1, + 1, + 0, + 0)) { map = mutator.crossOver(map, otherMap, prng); assertThat(map) - .containsExactly(asMutableList(1), 1, asMutableList(2), 2, asMutableList(6), 6, + .containsExactly( + asMutableList(1), + 1, + asMutableList(2), + 2, + asMutableList(6), + 6, // Overwritten keys after here - asMutableList(3), 3, asMutableList(40), 4, asMutableList(50), 5) + asMutableList(3), + 3, + asMutableList(40), + 4, + asMutableList(50), + 5) .inOrder(); } } @@ -333,19 +387,43 @@ void testCrossOverCrossOverChunkKeys() { @Test void testCrossOverCrossOverChunkValues() { AnnotatedType type = - new TypeHolder<@NotNull Map<@NotNull Integer, @NotNull List<@NotNull Integer>>>() { - }.annotatedType(); + new TypeHolder< + @NotNull Map<@NotNull Integer, @NotNull List<@NotNull Integer>>>() {}.annotatedType(); SerializingMutator<@NotNull Map<@NotNull Integer, @NotNull List<@NotNull Integer>>> mutator = (SerializingMutator<@NotNull Map<@NotNull Integer, @NotNull List<@NotNull Integer>>>) FACTORY.createOrThrow(type); - Map> map = asMap(1, asMutableList(1), 2, asMutableList(2), 3, - asMutableList(3), 4, asMutableList(4), 5, asMutableList(5), 6, asMutableList(6)); - Map> otherMap = asMap(1, asMutableList(1), 2, asMutableList(2), 3, - asMutableList(30), 40, asMutableList(40), 50, asMutableList(50), 60, asMutableList(60)); - - try ( - MockPseudoRandom prng = mockPseudoRandom( + Map> map = + asMap( + 1, + asMutableList(1), + 2, + asMutableList(2), + 3, + asMutableList(3), + 4, + asMutableList(4), + 5, + asMutableList(5), + 6, + asMutableList(6)); + Map> otherMap = + asMap( + 1, + asMutableList(1), + 2, + asMutableList(2), + 3, + asMutableList(30), + 40, + asMutableList(40), + 50, + asMutableList(50), + 60, + asMutableList(60)); + + try (MockPseudoRandom prng = + mockPseudoRandom( // cross over action 2, // values @@ -357,16 +435,36 @@ void testCrossOverCrossOverChunkValues() { // to chunk offset, 2, // first value, delegate to list cross over, overwrite 1 entry at offset 0 from offset 0 - 1, 1, 0, 0, + 1, + 1, + 0, + 0, // second value, delegate to list cross over, overwrite 1 entry at offset 0 from offset // 0 - 1, 1, 0, 0, + 1, + 1, + 0, + 0, // third value, delegate to list cross over, overwrite 1 entry at offset 0 from offset 0 - 1, 1, 0, 0)) { + 1, + 1, + 0, + 0)) { map = mutator.crossOver(map, otherMap, prng); assertThat(map) - .containsExactly(1, asMutableList(1), 2, asMutableList(2), 3, asMutableList(30), 4, - asMutableList(40), 5, asMutableList(50), 6, asMutableList(6)) + .containsExactly( + 1, + asMutableList(1), + 2, + asMutableList(2), + 3, + asMutableList(30), + 4, + asMutableList(40), + 5, + asMutableList(50), + 6, + asMutableList(6)) .inOrder(); } } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/BooleanMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/BooleanMutatorTest.java index 3bf55bcf1..2fa18fbc8 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/BooleanMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/BooleanMutatorTest.java @@ -47,8 +47,9 @@ void testPrimitive() { @Test void testBoxed() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Boolean>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Boolean>() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("Boolean"); Boolean bool; diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/ByteArrayMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/ByteArrayMutatorTest.java index fa7ec979b..f91ea500b 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/ByteArrayMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/ByteArrayMutatorTest.java @@ -42,8 +42,8 @@ void cleanMockSize() { @Test void testBasicFunction() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory().createOrThrow(new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("Nullable"); byte[] arr; @@ -62,8 +62,10 @@ void testBasicFunction() { @Test void testMaxLength() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("byte[]"); byte[] arr; @@ -76,45 +78,63 @@ void testMaxLength() { try (MockPseudoRandom prng = mockPseudoRandom()) { // the ByteArrayMutator will limit the maximum size of the data requested from libfuzzer to // WithLength::max so setting the mock mutator to make it bigger will cause an exception - assertThrows(ArrayIndexOutOfBoundsException.class, () -> { mutator.mutate(arr, prng); }); + assertThrows( + ArrayIndexOutOfBoundsException.class, + () -> { + mutator.mutate(arr, prng); + }); } } @Test void testMaxLengthInitClamp() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("byte[]"); try (MockPseudoRandom prng = mockPseudoRandom(10)) { // init will call closedRange(min, max) and the mock prng will assert that the given value // above is between those values which we want to fail here to show that we're properly // clamping the range - assertThrows(AssertionError.class, () -> { mutator.init(prng); }); + assertThrows( + AssertionError.class, + () -> { + mutator.init(prng); + }); } } @Test void testMinLengthInitClamp() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("byte[]"); try (MockPseudoRandom prng = mockPseudoRandom(3)) { // init will call closedrange(min, max) and the mock prng will assert that the given value // above is between those values which we want to fail here to show that we're properly // clamping the range - assertThrows(AssertionError.class, () -> { mutator.init(prng); }); + assertThrows( + AssertionError.class, + () -> { + mutator.init(prng); + }); } } @Test void testMinLength() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("byte[]"); byte[] arr; @@ -135,53 +155,57 @@ void testMinLength() { @Test void testCrossOver() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("byte[]"); byte[] value = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; byte[] otherValue = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; byte[] crossedOver; - try (MockPseudoRandom prng = mockPseudoRandom( - // intersect arrays - 0, - // out length - 8, - // copy 3 from first - 3, - // copy 1 from second - 1, - // copy 1 from first, - 1, - // copy 3 from second - 3)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // intersect arrays + 0, + // out length + 8, + // copy 3 from first + 3, + // copy 1 from second + 1, + // copy 1 from first, + 1, + // copy 3 from second + 3)) { crossedOver = mutator.crossOver(value, otherValue, prng); assertThat(crossedOver).isEqualTo(new byte[] {0, 1, 2, 10, 3, 11, 12, 13}); } - try (MockPseudoRandom prng = mockPseudoRandom( - // insert into action - 1, - // copy size - 3, - // from position - 5, - // to position - 2)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // insert into action + 1, + // copy size + 3, + // from position + 5, + // to position + 2)) { crossedOver = mutator.crossOver(value, otherValue, prng); assertThat(crossedOver).isEqualTo(new byte[] {0, 1, 15, 16, 17, 2, 3, 4, 5, 6, 7, 8, 9}); } - try (MockPseudoRandom prng = mockPseudoRandom( - // overwrite action - 2, - // to position - 3, - // copy size - 3, - // from position - 4)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // overwrite action + 2, + // to position + 3, + // copy size + 3, + // from position + 4)) { crossedOver = mutator.crossOver(value, otherValue, prng); assertThat(crossedOver).isEqualTo(new byte[] {0, 1, 2, 14, 15, 16, 6, 7, 8, 9}); } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/EnumMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/EnumMutatorTest.java index d2c613974..030371cf0 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/EnumMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/EnumMutatorTest.java @@ -32,15 +32,22 @@ import org.junit.jupiter.api.Test; class EnumMutatorTest { - enum TestEnumOne { A } + enum TestEnumOne { + A + } - enum TestEnum { A, B, C } + enum TestEnum { + A, + B, + C + } @Test void testBoxed() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull TestEnum>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull TestEnum>() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("Enum"); TestEnum cl; try (MockPseudoRandom prng = mockPseudoRandom(0)) { @@ -71,17 +78,21 @@ void testBoxed() { @Test void testEnumWithOneElementShouldThrow() { - assertThrows(IllegalArgumentException.class, () -> { - LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull TestEnumOne>() {}.annotatedType()); - }, "When trying to build mutators for Enum with one value, an Exception should be thrown."); + assertThrows( + IllegalArgumentException.class, + () -> { + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull TestEnumOne>() {}.annotatedType()); + }, + "When trying to build mutators for Enum with one value, an Exception should be thrown."); } @Test void testEnumBasedOnInvalidInput() throws IOException { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull TestEnum>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull TestEnum>() {}.annotatedType()); ByteArrayOutputStream bo = new ByteArrayOutputStream(); DataOutputStream os = new DataOutputStream(bo); // Valid values diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorTest.java index 9c03b4678..643ab7b1c 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorTest.java @@ -48,7 +48,8 @@ static Stream floatForceInRangeCases() { return Stream.of( // value is already in range: it should stay in range - arguments(0.0f, 0.0f, 1.0f, true), arguments(0.0f, 1.0f, 1.0f, true), + arguments(0.0f, 0.0f, 1.0f, true), + arguments(0.0f, 1.0f, 1.0f, true), arguments(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, 1.0f, true), arguments(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, true), arguments(Float.NaN, 0.0f, 1.0f, true), @@ -73,7 +74,8 @@ static Stream floatForceInRangeCases() { // Values outside the range arguments(-2e30f, -100000.0f, 100000.0f, true), arguments(2e30f, Float.NEGATIVE_INFINITY, -Float.MAX_VALUE, true), - arguments(-1.0f, 0.0f, 1.0f, false), arguments(5.0f, 0.0f, 1.0f, false), + arguments(-1.0f, 0.0f, 1.0f, false), + arguments(5.0f, 0.0f, 1.0f, false), arguments(-Float.MAX_VALUE, -Float.MAX_VALUE, 100.0f, true), // NaN not allowed arguments(Float.NaN, 0.0f, 1.0f, false), @@ -88,7 +90,8 @@ static Stream floatForceInRangeCases() { arguments(Float.NaN, 0.0f, Float.MIN_VALUE, false), // There are many possible NaN values, test a few of them that are different from Float.NaN // (0x7fc00000) - arguments(NaN1, 0.0f, 1.0f, false), arguments(NaN2, 0.0f, 1.0f, false), + arguments(NaN1, 0.0f, 1.0f, false), + arguments(NaN2, 0.0f, 1.0f, false), arguments(NaN3, 0.0f, 1.0f, false)); } @@ -98,12 +101,16 @@ static Stream doubleForceInRangeCases() { double NaN3 = Double.longBitsToDouble(0x7ff0000000000003L); double NaNdeadbeef = Double.longBitsToDouble(0x7ff00000deadbeefL); assertThat( - Double.isNaN(NaN1) && Double.isNaN(NaN2) && Double.isNaN(NaN3) && Double.isNaN(NaNdeadbeef)) + Double.isNaN(NaN1) + && Double.isNaN(NaN2) + && Double.isNaN(NaN3) + && Double.isNaN(NaNdeadbeef)) .isTrue(); return Stream.of( // value is already in range: it should stay in range - arguments(0.0, 0.0, 1.0, true), arguments(0.0, 1.0, 1.0, true), + arguments(0.0, 0.0, 1.0, true), + arguments(0.0, 1.0, 1.0, true), arguments(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1.0, true), arguments( Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, true), @@ -129,12 +136,16 @@ static Stream doubleForceInRangeCases() { // Values outside the range arguments(-2e30, -100000.0, 100000.0, true), arguments(2e30, Double.NEGATIVE_INFINITY, -Double.MAX_VALUE, true), - arguments(-1.0, 0.0, 1.0, false), arguments(5.0, 0.0, 1.0, false), + arguments(-1.0, 0.0, 1.0, false), + arguments(5.0, 0.0, 1.0, false), arguments(-Double.MAX_VALUE, -Double.MAX_VALUE, 100.0, true), arguments( Math.nextDown(Double.MAX_VALUE), -Double.MAX_VALUE * 0.5, Double.MAX_VALUE * 0.5, true), - arguments(Math.nextDown(Double.MAX_VALUE), -Double.MAX_VALUE * 0.5, - Math.nextUp(Double.MAX_VALUE * 0.5), true), + arguments( + Math.nextDown(Double.MAX_VALUE), + -Double.MAX_VALUE * 0.5, + Math.nextUp(Double.MAX_VALUE * 0.5), + true), // NaN not allowed arguments(Double.NaN, 0.0, 1.0, false), arguments(Double.NaN, -Double.MAX_VALUE, 1.0, false), @@ -148,7 +159,8 @@ static Stream doubleForceInRangeCases() { arguments(Double.NaN, 0.0, Double.MIN_VALUE, false), // There are many possible NaN values, test a few of them that are different from Double.NaN // (0x7ff8000000000000L) - arguments(NaN1, 0.0, 1.0, false), arguments(NaN2, 0.0, 1.0, false), + arguments(NaN1, 0.0, 1.0, false), + arguments(NaN2, 0.0, 1.0, false), arguments(NaN3, 0.0, 1.0, false), arguments(NaNdeadbeef, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, false)); } @@ -213,10 +225,12 @@ public Integer get() { static Stream floatInitCasesFullRange() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Float>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Float>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), Float.NEGATIVE_INFINITY, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), Float.NEGATIVE_INFINITY, true), arguments(mutator, Stream.of(true, ctr.get()), -Float.MAX_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -Float.MIN_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -0.0f, true), @@ -230,11 +244,15 @@ static Stream floatInitCasesFullRange() { static Stream floatInitCasesMinusOneToOne() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @FloatInRange(min = -1.0f, max = 1.0f) Float>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @FloatInRange(min = -1.0f, max = 1.0f) + Float>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), -1.0f, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), -1.0f, true), arguments(mutator, Stream.of(true, ctr.get()), -Float.MIN_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -0.0f, true), arguments(mutator, Stream.of(true, ctr.get()), 0.0f, true), @@ -246,12 +264,15 @@ static Stream floatInitCasesMinusOneToOne() { static Stream floatInitCasesMinusMinToMin() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @FloatInRange( - min = -Float.MIN_VALUE, max = Float.MIN_VALUE) Float>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @FloatInRange(min = -Float.MIN_VALUE, max = Float.MIN_VALUE) + Float>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), -Float.MIN_VALUE, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), -Float.MIN_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -0.0f, true), arguments(mutator, Stream.of(true, ctr.get()), 0.0f, true), arguments(mutator, Stream.of(true, ctr.get()), Float.MIN_VALUE, true), @@ -261,12 +282,15 @@ static Stream floatInitCasesMinusMinToMin() { static Stream floatInitCasesMaxToInf() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @FloatInRange( - min = Float.MAX_VALUE, max = Float.POSITIVE_INFINITY) Float>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @FloatInRange(min = Float.MAX_VALUE, max = Float.POSITIVE_INFINITY) + Float>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), Float.MAX_VALUE, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), Float.MAX_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), Float.POSITIVE_INFINITY, true), arguments(mutator, Stream.of(true, ctr.get()), Float.NaN, true), arguments(mutator, Stream.of(true, ctr.get()), UNUSED_FLOAT, false)); @@ -274,12 +298,16 @@ static Stream floatInitCasesMaxToInf() { static Stream floatInitCasesMinusInfToMinusMax() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @FloatInRange( - min = Float.NEGATIVE_INFINITY, max = -Float.MAX_VALUE) Float>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull + @FloatInRange(min = Float.NEGATIVE_INFINITY, max = -Float.MAX_VALUE) + Float>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), Float.NEGATIVE_INFINITY, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), Float.NEGATIVE_INFINITY, true), arguments(mutator, Stream.of(true, ctr.get()), -Float.MAX_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), Float.NaN, true), arguments(mutator, Stream.of(true, ctr.get()), UNUSED_FLOAT, false)); @@ -287,12 +315,19 @@ static Stream floatInitCasesMinusInfToMinusMax() { static Stream floatInitCasesFullRangeWithoutNaN() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @FloatInRange(min = Float.NEGATIVE_INFINITY, - max = Float.POSITIVE_INFINITY, allowNaN = true) Float>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull + @FloatInRange( + min = Float.NEGATIVE_INFINITY, + max = Float.POSITIVE_INFINITY, + allowNaN = true) + Float>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), Float.NEGATIVE_INFINITY, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), Float.NEGATIVE_INFINITY, true), arguments(mutator, Stream.of(true, ctr.get()), -Float.MAX_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -Float.MIN_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -0.0f, true), @@ -305,11 +340,18 @@ static Stream floatInitCasesFullRangeWithoutNaN() { } @ParameterizedTest - @MethodSource({"floatInitCasesMinusOneToOne", "floatInitCasesFullRange", - "floatInitCasesMinusMinToMin", "floatInitCasesMaxToInf", "floatInitCasesMinusInfToMinusMax", - "floatInitCasesFullRangeWithoutNaN"}) - void - testFloatInitCases(SerializingMutator mutator, Stream prngValues, float expected, + @MethodSource({ + "floatInitCasesMinusOneToOne", + "floatInitCasesFullRange", + "floatInitCasesMinusMinToMin", + "floatInitCasesMaxToInf", + "floatInitCasesMinusInfToMinusMax", + "floatInitCasesFullRangeWithoutNaN" + }) + void testFloatInitCases( + SerializingMutator mutator, + Stream prngValues, + float expected, boolean specialValueIndexExists) { assertThat(mutator.toString()).isEqualTo("Float"); if (specialValueIndexExists) { @@ -319,20 +361,28 @@ static Stream floatInitCasesFullRangeWithoutNaN() { } assertThat(n).isEqualTo(expected); } else { // should throw - assertThrows(AssertionError.class, () -> { - try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(prngValues.toArray())) { - mutator.init(prng); - } - }); + assertThrows( + AssertionError.class, + () -> { + try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(prngValues.toArray())) { + mutator.init(prng); + } + }); } } static Stream floatMutateSanityChecksFullRangeCases() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @FloatInRange(min = Float.NEGATIVE_INFINITY, - max = Float.POSITIVE_INFINITY, allowNaN = true) Float>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull + @FloatInRange( + min = Float.NEGATIVE_INFINITY, + max = Float.POSITIVE_INFINITY, + allowNaN = true) + Float>() {}.annotatedType()); // Init value can be set to desired one by giving this to the init method: (false, ) return Stream.of( @@ -347,7 +397,11 @@ static Stream floatMutateSanityChecksFullRangeCases() { arguments(mutator, Stream.of(false, 0f), Stream.of(false, 1, 0B01111111), 1.0f, true), // mutateMantissa arguments(mutator, Stream.of(false, 0f), Stream.of(false, 2, 0, 100), 1.4e-43f, true), - arguments(mutator, Stream.of(false, Float.intBitsToFloat(1)), Stream.of(false, 2, 0, -1), 0, + arguments( + mutator, + Stream.of(false, Float.intBitsToFloat(1)), + Stream.of(false, 2, 0, -1), + 0, true), // mutateWithMathematicalFn arguments( @@ -363,15 +417,22 @@ static Stream floatMutateSanityChecksFullRangeCases() { static Stream floatMutateLimitedRangeCases() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @FloatInRange(min = -1f, max = 1f, allowNaN = false) Float>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @FloatInRange(min = -1f, max = 1f, allowNaN = false) + Float>() {}.annotatedType()); // Init value can be set to desired one by giving this to the init method: (false, ) return Stream.of( // Bit flip; forceInRange(); result equals previous value; adjust value - arguments(mutator, Stream.of(false, 0f), Stream.of(false, 0, 30, true), - 0f - Float.MIN_VALUE, true), + arguments( + mutator, + Stream.of(false, 0f), + Stream.of(false, 0, 30, true), + 0f - Float.MIN_VALUE, + true), arguments(mutator, Stream.of(false, 1f), Stream.of(false, 0, 30), Math.nextDown(1f), true), arguments(mutator, Stream.of(false, -1f), Stream.of(false, 0, 30), Math.nextUp(-1f), true), // NaN after mutateWithMathematicalFn with NaN not allowed; forceInRange will return @@ -381,9 +442,12 @@ static Stream floatMutateLimitedRangeCases() { static Stream floatMutateLimitedRangeCasesWithNaN() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @FloatInRange(min = -1f, max = 1f, allowNaN = true) Float>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @FloatInRange(min = -1f, max = 1f, allowNaN = true) + Float>() {}.annotatedType()); // Init value can be set to desired one by giving this to the init method: (false, ) return Stream.of( @@ -395,11 +459,17 @@ static Stream floatMutateLimitedRangeCasesWithNaN() { } @ParameterizedTest - @MethodSource({"floatMutateSanityChecksFullRangeCases", "floatMutateLimitedRangeCases", - "floatMutateLimitedRangeCasesWithNaN"}) - void - testFloatMutateCases(SerializingMutator mutator, Stream initValues, - Stream mutationValues, float expected, boolean knownMutatorSwitchCase) { + @MethodSource({ + "floatMutateSanityChecksFullRangeCases", + "floatMutateLimitedRangeCases", + "floatMutateLimitedRangeCasesWithNaN" + }) + void testFloatMutateCases( + SerializingMutator mutator, + Stream initValues, + Stream mutationValues, + float expected, + boolean knownMutatorSwitchCase) { assertThat(mutator.toString()).isEqualTo("Float"); Float n; @@ -424,21 +494,24 @@ static Stream floatMutateLimitedRangeCasesWithNaN() { assertThat(n).isAtMost(((FloatMutator) mutator).maxValue); } } else { // Invalid mutation because a case is not handled - assertThrows(AssertionError.class, () -> { - try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(mutationValues.toArray())) { - mutator.mutate(UNUSED_FLOAT, prng); - } - }); + assertThrows( + AssertionError.class, + () -> { + try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(mutationValues.toArray())) { + mutator.mutate(UNUSED_FLOAT, prng); + } + }); } } @Test void testFloatCrossOverMean() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Float>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Float>() {}.annotatedType()); try (TestSupport.MockPseudoRandom prng = - mockPseudoRandom(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) { + mockPseudoRandom(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) { assertThat(mutator.crossOver(0f, 0f, prng)).isWithin(0).of(0f); assertThat(mutator.crossOver(-0f, 0f, prng)).isWithin(0).of(0f); assertThat(mutator.crossOver(0f, 2f, prng)).isWithin(1e-10f).of(1.0f); @@ -474,8 +547,9 @@ void testFloatCrossOverMean() { @Test void testFloatCrossOverExponent() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Float>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Float>() {}.annotatedType()); try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(1, 1, 1)) { assertThat(mutator.crossOver(2.0f, -1.5f, prng)).isWithin(1e-10f).of(1.0f); assertThat(mutator.crossOver(2.0f, Float.POSITIVE_INFINITY, prng)).isPositiveInfinity(); @@ -486,8 +560,9 @@ void testFloatCrossOverExponent() { @Test void testFloatCrossOverMantissa() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Float>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Float>() {}.annotatedType()); try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(2, 2, 2)) { assertThat(mutator.crossOver(4.0f, 3.5f, prng)).isWithin(1e-10f).of(7.0f); assertThat(mutator.crossOver(Float.POSITIVE_INFINITY, 3.0f, prng)).isNaN(); @@ -497,10 +572,12 @@ void testFloatCrossOverMantissa() { static Stream doubleInitCasesFullRange() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Double>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Double>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), Double.NEGATIVE_INFINITY, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), Double.NEGATIVE_INFINITY, true), arguments(mutator, Stream.of(true, ctr.get()), -Double.MAX_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -Double.MIN_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -0.0, true), @@ -514,11 +591,15 @@ static Stream doubleInitCasesFullRange() { static Stream doubleInitCasesMinusOneToOne() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @DoubleInRange(min = -1.0, max = 1.0) Double>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @DoubleInRange(min = -1.0, max = 1.0) + Double>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), -1.0, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), -1.0, true), arguments(mutator, Stream.of(true, ctr.get()), -Double.MIN_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -0.0, true), arguments(mutator, Stream.of(true, ctr.get()), 0.0, true), @@ -530,12 +611,15 @@ static Stream doubleInitCasesMinusOneToOne() { static Stream doubleInitCasesMinusMinToMin() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @DoubleInRange( - min = -Double.MIN_VALUE, max = Double.MIN_VALUE) Double>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @DoubleInRange(min = -Double.MIN_VALUE, max = Double.MIN_VALUE) + Double>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), -Double.MIN_VALUE, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), -Double.MIN_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -0.0, true), arguments(mutator, Stream.of(true, ctr.get()), 0.0, true), arguments(mutator, Stream.of(true, ctr.get()), Double.MIN_VALUE, true), @@ -545,12 +629,16 @@ static Stream doubleInitCasesMinusMinToMin() { static Stream doubleInitCasesMaxToInf() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @DoubleInRange( - min = Double.MAX_VALUE, max = Double.POSITIVE_INFINITY) Double>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull + @DoubleInRange(min = Double.MAX_VALUE, max = Double.POSITIVE_INFINITY) + Double>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), Double.MAX_VALUE, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), Double.MAX_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), Double.POSITIVE_INFINITY, true), arguments(mutator, Stream.of(true, ctr.get()), Double.NaN, true), arguments(mutator, Stream.of(true, ctr.get()), UNUSED_DOUBLE, false)); @@ -558,12 +646,16 @@ static Stream doubleInitCasesMaxToInf() { static Stream doubleInitCasesMinusInfToMinusMax() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @DoubleInRange( - min = Double.NEGATIVE_INFINITY, max = -Double.MAX_VALUE) Double>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull + @DoubleInRange(min = Double.NEGATIVE_INFINITY, max = -Double.MAX_VALUE) + Double>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), Double.NEGATIVE_INFINITY, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), Double.NEGATIVE_INFINITY, true), arguments(mutator, Stream.of(true, ctr.get()), -Double.MAX_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), Double.NaN, true), arguments(mutator, Stream.of(true, ctr.get()), UNUSED_DOUBLE, false)); @@ -571,12 +663,19 @@ static Stream doubleInitCasesMinusInfToMinusMax() { static Stream doubleInitCasesFullRangeWithoutNaN() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @DoubleInRange(min = Double.NEGATIVE_INFINITY, - max = Double.POSITIVE_INFINITY, allowNaN = true) Double>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull + @DoubleInRange( + min = Double.NEGATIVE_INFINITY, + max = Double.POSITIVE_INFINITY, + allowNaN = true) + Double>() {}.annotatedType()); Supplier ctr = makeCounter(); - return Stream.of(arguments(mutator, Stream.of(true, ctr.get()), Double.NEGATIVE_INFINITY, true), + return Stream.of( + arguments(mutator, Stream.of(true, ctr.get()), Double.NEGATIVE_INFINITY, true), arguments(mutator, Stream.of(true, ctr.get()), -Double.MAX_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -Double.MIN_VALUE, true), arguments(mutator, Stream.of(true, ctr.get()), -0.0, true), @@ -589,12 +688,19 @@ static Stream doubleInitCasesFullRangeWithoutNaN() { } @ParameterizedTest - @MethodSource({"doubleInitCasesMinusOneToOne", "doubleInitCasesFullRange", - "doubleInitCasesMinusMinToMin", "doubleInitCasesMaxToInf", - "doubleInitCasesMinusInfToMinusMax", "doubleInitCasesFullRangeWithoutNaN"}) - void - testDoubleInitCases(SerializingMutator mutator, Stream prngValues, - double expected, boolean knownSwitchCase) { + @MethodSource({ + "doubleInitCasesMinusOneToOne", + "doubleInitCasesFullRange", + "doubleInitCasesMinusMinToMin", + "doubleInitCasesMaxToInf", + "doubleInitCasesMinusInfToMinusMax", + "doubleInitCasesFullRangeWithoutNaN" + }) + void testDoubleInitCases( + SerializingMutator mutator, + Stream prngValues, + double expected, + boolean knownSwitchCase) { assertThat(mutator.toString()).isEqualTo("Double"); if (knownSwitchCase) { Double n = null; @@ -603,20 +709,28 @@ static Stream doubleInitCasesFullRangeWithoutNaN() { } assertThat(n).isEqualTo(expected); } else { - assertThrows(AssertionError.class, () -> { - try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(prngValues.toArray())) { - mutator.init(prng); - } - }); + assertThrows( + AssertionError.class, + () -> { + try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(prngValues.toArray())) { + mutator.init(prng); + } + }); } } static Stream doubleMutateSanityChecksFullRangeCases() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @DoubleInRange(min = Double.NEGATIVE_INFINITY, - max = Double.POSITIVE_INFINITY, allowNaN = true) Double>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull + @DoubleInRange( + min = Double.NEGATIVE_INFINITY, + max = Double.POSITIVE_INFINITY, + allowNaN = true) + Double>() {}.annotatedType()); // Init value can be set to desired one by giving this to the init method: (false, ) return Stream.of( @@ -631,8 +745,12 @@ static Stream doubleMutateSanityChecksFullRangeCases() { arguments(mutator, Stream.of(false, 0.0), Stream.of(false, 1, 0B1111111111), 1.0, true), // mutateMantissa arguments(mutator, Stream.of(false, 0.0), Stream.of(false, 2, 0, 100L), 4.94e-322, true), - arguments(mutator, Stream.of(false, Double.longBitsToDouble(1)), - Stream.of(false, 2, 0, -1L), 0, true), + arguments( + mutator, + Stream.of(false, Double.longBitsToDouble(1)), + Stream.of(false, 2, 0, -1L), + 0, + true), // mutateWithMathematicalFn arguments(mutator, Stream.of(false, 10.1), Stream.of(false, 3, 4), 11, true), // Math::ceil arguments( @@ -646,15 +764,22 @@ static Stream doubleMutateSanityChecksFullRangeCases() { static Stream doubleMutateLimitedRangeCases() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @DoubleInRange(min = -1, max = 1, allowNaN = false) Double>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @DoubleInRange(min = -1, max = 1, allowNaN = false) + Double>() {}.annotatedType()); // Init value can be set to desired one by giving this to the init method: (false, ) return Stream.of( // Bit flip; forceInRange(); result equals previous value; adjust value - arguments(mutator, Stream.of(false, 0.0), Stream.of(false, 0, 62, true), - 0.0 - Double.MIN_VALUE, true), + arguments( + mutator, + Stream.of(false, 0.0), + Stream.of(false, 0, 62, true), + 0.0 - Double.MIN_VALUE, + true), arguments( mutator, Stream.of(false, 1.0), Stream.of(false, 0, 62), Math.nextDown(1.0), true), arguments( @@ -666,9 +791,12 @@ static Stream doubleMutateLimitedRangeCases() { static Stream doubleMutateLimitedRangeCasesWithNaN() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @DoubleInRange(min = -1, max = 1, allowNaN = true) Double>() { - }.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder< + @NotNull @DoubleInRange(min = -1, max = 1, allowNaN = true) + Double>() {}.annotatedType()); // Init value can be set to desired one by giving this to the init method: (false, ) return Stream.of( @@ -680,11 +808,17 @@ static Stream doubleMutateLimitedRangeCasesWithNaN() { } @ParameterizedTest - @MethodSource({"doubleMutateSanityChecksFullRangeCases", "doubleMutateLimitedRangeCases", - "doubleMutateLimitedRangeCasesWithNaN"}) - void - testDoubleMutateCases(SerializingMutator mutator, Stream initValues, - Stream mutationValues, double expected, boolean knownSwitchCase) { + @MethodSource({ + "doubleMutateSanityChecksFullRangeCases", + "doubleMutateLimitedRangeCases", + "doubleMutateLimitedRangeCasesWithNaN" + }) + void testDoubleMutateCases( + SerializingMutator mutator, + Stream initValues, + Stream mutationValues, + double expected, + boolean knownSwitchCase) { assertThat(mutator.toString()).isEqualTo("Double"); Double n; @@ -709,21 +843,24 @@ static Stream doubleMutateLimitedRangeCasesWithNaN() { assertThat(n).isAtMost(((DoubleMutator) mutator).maxValue); } } else { // Invalid mutation because a case is not handled - assertThrows(AssertionError.class, () -> { - try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(mutationValues.toArray())) { - mutator.mutate(UNUSED_DOUBLE, prng); - } - }); + assertThrows( + AssertionError.class, + () -> { + try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(mutationValues.toArray())) { + mutator.mutate(UNUSED_DOUBLE, prng); + } + }); } } @Test void testDoubleCrossOverMean() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Double>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Double>() {}.annotatedType()); try (TestSupport.MockPseudoRandom prng = - mockPseudoRandom(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) { + mockPseudoRandom(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) { assertThat(mutator.crossOver(0.0, 0.0, prng)).isWithin(0).of(0f); assertThat(mutator.crossOver(-0.0, 0.0, prng)).isWithin(0).of(0f); assertThat(mutator.crossOver(0.0, 2.0, prng)).isWithin(1e-10f).of(1.0f); @@ -760,8 +897,9 @@ void testDoubleCrossOverMean() { @Test void testDoubleCrossOverExponent() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Double>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Double>() {}.annotatedType()); try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(1, 1, 1)) { assertThat(mutator.crossOver(2.0, -1.5, prng)).isWithin(1e-10f).of(1.0f); assertThat(mutator.crossOver(2.0, Double.POSITIVE_INFINITY, prng)).isPositiveInfinity(); @@ -772,8 +910,9 @@ void testDoubleCrossOverExponent() { @Test void testDoubleCrossOverMantissa() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Double>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Double>() {}.annotatedType()); try (TestSupport.MockPseudoRandom prng = mockPseudoRandom(2, 2, 2)) { assertThat(mutator.crossOver(4.0, 3.5, prng)).isWithin(1e-10f).of(7.0f); assertThat(mutator.crossOver(Double.POSITIVE_INFINITY, 3.0, prng)).isNaN(); diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/IntegralMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/IntegralMutatorTest.java index dda8cfedd..d70872381 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/IntegralMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/IntegralMutatorTest.java @@ -34,8 +34,14 @@ @SuppressWarnings("unchecked") class IntegralMutatorTest { static Stream forceInRangeCases() { - return Stream.of(arguments(0, 0, 1), arguments(5, 0, 1), arguments(-5, -10, -1), - arguments(-200, -10, -1), arguments(10, 0, 3), arguments(-5, 0, 3), arguments(10, -7, 7), + return Stream.of( + arguments(0, 0, 1), + arguments(5, 0, 1), + arguments(-5, -10, -1), + arguments(-200, -10, -1), + arguments(10, 0, 3), + arguments(-5, 0, 3), + arguments(10, -7, 7), arguments(Long.MIN_VALUE, Long.MIN_VALUE, Long.MAX_VALUE), arguments(Long.MIN_VALUE, Long.MIN_VALUE, 100), arguments(Long.MIN_VALUE + 100, Long.MIN_VALUE, 100), @@ -62,8 +68,9 @@ void testForceInRange(long value, long minValue, long maxValue) { @Test void testCrossOver() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull Long>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow(new TypeHolder<@NotNull Long>() {}.annotatedType()); // cross over mean values try (MockPseudoRandom prng = mockPseudoRandom(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) { assertThat(mutator.crossOver(0L, 0L, prng)).isEqualTo(0); diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorTest.java index df8029f9d..982e1ad04 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/lang/StringMutatorTest.java @@ -46,8 +46,9 @@ void cleanMockSize() { @RepeatedTest(10) void testFixAscii_randomInputFixed(RepetitionInfo info) { - SplittableRandom random = new SplittableRandom( - (long) "testFixAscii_randomInputFixed".hashCode() * info.getCurrentRepetition()); + SplittableRandom random = + new SplittableRandom( + (long) "testFixAscii_randomInputFixed".hashCode() * info.getCurrentRepetition()); for (int length = 0; length < 1000; length++) { byte[] randomBytes = generateRandomBytes(random, length); @@ -63,8 +64,9 @@ void testFixAscii_randomInputFixed(RepetitionInfo info) { @RepeatedTest(10) void testFixAscii_validInputNotChanged(RepetitionInfo info) { - SplittableRandom random = new SplittableRandom( - (long) "testFixAscii_validInputNotChanged".hashCode() * info.getCurrentRepetition()); + SplittableRandom random = + new SplittableRandom( + (long) "testFixAscii_validInputNotChanged".hashCode() * info.getCurrentRepetition()); for (int codePoints = 0; codePoints < 1000; codePoints++) { byte[] validAscii = generateValidAsciiBytes(random, codePoints); @@ -76,8 +78,9 @@ void testFixAscii_validInputNotChanged(RepetitionInfo info) { @RepeatedTest(20) void testFixUtf8_randomInputFixed(RepetitionInfo info) { - SplittableRandom random = new SplittableRandom( - (long) "testFixUtf8_randomInputFixed".hashCode() * info.getCurrentRepetition()); + SplittableRandom random = + new SplittableRandom( + (long) "testFixUtf8_randomInputFixed".hashCode() * info.getCurrentRepetition()); for (int length = 0; length < 1000; length++) { byte[] randomBytes = generateRandomBytes(random, length); @@ -93,8 +96,9 @@ void testFixUtf8_randomInputFixed(RepetitionInfo info) { @RepeatedTest(20) void testFixUtf8_validInputNotChanged(RepetitionInfo info) { - SplittableRandom random = new SplittableRandom( - (long) "testFixUtf8_validInputNotChanged".hashCode() * info.getCurrentRepetition()); + SplittableRandom random = + new SplittableRandom( + (long) "testFixUtf8_validInputNotChanged".hashCode() * info.getCurrentRepetition()); for (int codePoints = 0; codePoints < 1000; codePoints++) { byte[] validUtf8 = generateValidUtf8Bytes(random, codePoints); @@ -107,34 +111,48 @@ void testFixUtf8_validInputNotChanged(RepetitionInfo info) { @Test void testMinLengthInit() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @WithUtf8Length(min = 10) String>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder<@NotNull @WithUtf8Length(min = 10) String>() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("String"); try (MockPseudoRandom prng = mockPseudoRandom(5)) { // mock prng should throw an assert error when given a lower value than min - Assertions.assertThrows(AssertionError.class, () -> { String s = mutator.init(prng); }); + Assertions.assertThrows( + AssertionError.class, + () -> { + String s = mutator.init(prng); + }); } } @Test void testMaxLengthInit() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @WithUtf8Length(max = 50) String>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder<@NotNull @WithUtf8Length(max = 50) String>() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("String"); try (MockPseudoRandom prng = mockPseudoRandom(60)) { // mock prng should throw an assert error when given a value higher than max - Assertions.assertThrows(AssertionError.class, () -> { String s = mutator.init(prng); }); + Assertions.assertThrows( + AssertionError.class, + () -> { + String s = mutator.init(prng); + }); } } @Test void testMinLengthMutate() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @WithUtf8Length(min = 10) String>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder<@NotNull @WithUtf8Length(min = 10) String>() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("String"); String s; @@ -153,8 +171,10 @@ void testMinLengthMutate() { @Test void testMaxLengthMutate() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @WithUtf8Length(max = 15) String>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder<@NotNull @WithUtf8Length(max = 15) String>() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("String"); String s; @@ -166,20 +186,25 @@ void testMaxLengthMutate() { System.setProperty(LibFuzzerMutate.MOCK_SIZE_KEY, "20"); try (MockPseudoRandom prng = mockPseudoRandom()) { Assertions.assertThrows( - ArrayIndexOutOfBoundsException.class, () -> { String s2 = mutator.mutate(s, prng); }); + ArrayIndexOutOfBoundsException.class, + () -> { + String s2 = mutator.mutate(s, prng); + }); } } @Test void testMultibyteCharacters() { SerializingMutator mutator = - (SerializingMutator) LangMutators.newFactory().createOrThrow( - new TypeHolder<@NotNull @WithUtf8Length(min = 10) String>() {}.annotatedType()); + (SerializingMutator) + LangMutators.newFactory() + .createOrThrow( + new TypeHolder<@NotNull @WithUtf8Length(min = 10) String>() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("String"); String s; - try ( - MockPseudoRandom prng = mockPseudoRandom(10, "foobarÖÖ".getBytes(StandardCharsets.UTF_8))) { + try (MockPseudoRandom prng = + mockPseudoRandom(10, "foobarÖÖ".getBytes(StandardCharsets.UTF_8))) { s = mutator.init(prng); } assertThat(s).hasLength(8); @@ -206,7 +231,8 @@ private static byte[] generateRandomBytes(SplittableRandom random, int length) { } private static byte[] generateValidAsciiBytes(SplittableRandom random, int length) { - return random.ints(0, 0x7F) + return random + .ints(0, 0x7F) .limit(length) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) .toString() @@ -214,7 +240,8 @@ private static byte[] generateValidAsciiBytes(SplittableRandom random, int lengt } private static byte[] generateValidUtf8Bytes(SplittableRandom random, long codePoints) { - return random.ints(0, Character.MAX_CODE_POINT + 1) + return random + .ints(0, Character.MAX_CODE_POINT + 1) .filter(code -> code < Character.MIN_SURROGATE || code > Character.MAX_SURROGATE) .limit(codePoints) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorProto2Test.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorProto2Test.java index 9492bcece..b41744907 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorProto2Test.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorProto2Test.java @@ -39,51 +39,57 @@ import org.junit.jupiter.api.Test; class BuilderMutatorProto2Test { - private static final MutatorFactory FACTORY = new ChainedMutatorFactory( - LangMutators.newFactory(), CollectionMutators.newFactory(), ProtoMutators.newFactory()); + private static final MutatorFactory FACTORY = + new ChainedMutatorFactory( + LangMutators.newFactory(), CollectionMutators.newFactory(), ProtoMutators.newFactory()); @Test void testPrimitiveField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.Nullable}"); PrimitiveField2.Builder builder = PrimitiveField2.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // present - false, - // boolean - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // present + false, + // boolean + false)) { mutator.initInPlace(builder, prng); } assertThat(builder.hasSomeField()).isTrue(); assertThat(builder.getSomeField()).isFalse(); - try (MockPseudoRandom prng = mockPseudoRandom( - // present - false, - // boolean - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // present + false, + // boolean + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.hasSomeField()).isTrue(); assertThat(builder.getSomeField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate as non-null Boolean - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate as non-null Boolean + false)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.hasSomeField()).isTrue(); assertThat(builder.getSomeField()).isFalse(); - try (MockPseudoRandom prng = mockPseudoRandom( - // not present - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // not present + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.hasSomeField()).isFalse(); @@ -93,8 +99,9 @@ void testPrimitiveField() { @Test void testRequiredPrimitiveField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.Boolean}"); RequiredPrimitiveField2.Builder builder = RequiredPrimitiveField2.newBuilder(); @@ -113,45 +120,49 @@ void testRequiredPrimitiveField() { @Test void testRepeatedPrimitiveField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder via List}"); RepeatedPrimitiveField2.Builder builder = RepeatedPrimitiveField2.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // list size 1 - 1, - // boolean, - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // list size 1 + 1, + // boolean, + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.getSomeFieldList()).containsExactly(true).inOrder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate the list itself by adding an entry - 1, - // add a single element - 1, - // add the element at the end - 1, - // value to add - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate the list itself by adding an entry + 1, + // add a single element + 1, + // add the element at the end + 1, + // value to add + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getSomeFieldList()).containsExactly(true, true).inOrder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate the list itself by changing an entry - 2, - // mutate a single element - 1, - // mutate the second element - 1)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate the list itself by changing an entry + 2, + // mutate a single element + 1, + // mutate the second element + 1)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getSomeFieldList()).containsExactly(true, false).inOrder(); @@ -160,17 +171,19 @@ void testRepeatedPrimitiveField() { @Test void testMessageField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.Nullable<{Builder.Boolean} -> Message>}"); MessageField2.Builder builder = MessageField2.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // init submessage - false, - // boolean submessage field - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // init submessage + false, + // boolean submessage field + true)) { mutator.initInPlace(builder, prng); } @@ -178,24 +191,26 @@ void testMessageField() { .isEqualTo(RequiredPrimitiveField2.newBuilder().setSomeField(true).build()); assertThat(builder.hasMessageField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate submessage as non-null - false, - // mutate first field - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate submessage as non-null + false, + // mutate first field + 0)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getMessageField()) .isEqualTo(RequiredPrimitiveField2.newBuilder().setSomeField(false).build()); assertThat(builder.hasMessageField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate submessage to null - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate submessage to null + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.hasMessageField()).isFalse(); @@ -204,35 +219,39 @@ void testMessageField() { @Test void testRepeatedOptionalMessageField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder< + RepeatedOptionalMessageField2.@NotNull Builder>() {}.annotatedType()); assertThat(mutator.toString()) .isEqualTo("{Builder via List<{Builder.Nullable} -> Message>}"); RepeatedOptionalMessageField2.Builder builder = RepeatedOptionalMessageField2.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // list size 1 - 1, - // boolean - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // list size 1 + 1, + // boolean + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.getMessageFieldList().toString()).isEqualTo("[]"); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate the list itself by adding an entry - 1, - // add a single element - 1, - // add the element at the end - 1, - // Nullable mutator init - false, - // duplicate entry - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate the list itself by adding an entry + 1, + // add a single element + 1, + // add the element at the end + 1, + // Nullable mutator init + false, + // duplicate entry + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getMessageFieldList().size()).isEqualTo(2); @@ -241,56 +260,62 @@ void testRepeatedOptionalMessageField() { @Test void testRepeatedRequiredMessageField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder via List<{Builder.Boolean} -> Message>}"); RepeatedMessageField2.Builder builder = RepeatedMessageField2.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // list size 1 - 1, - // boolean - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // list size 1 + 1, + // boolean + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.getMessageFieldList()) .containsExactly(RequiredPrimitiveField2.newBuilder().setSomeField(true).build()) .inOrder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate the list itself by adding an entry - 1, - // add a single element - 1, - // add the element at the end - 1, - // value to add - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate the list itself by adding an entry + 1, + // add a single element + 1, + // add the element at the end + 1, + // value to add + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getMessageFieldList()) - .containsExactly(RequiredPrimitiveField2.newBuilder().setSomeField(true).build(), + .containsExactly( + RequiredPrimitiveField2.newBuilder().setSomeField(true).build(), RequiredPrimitiveField2.newBuilder().setSomeField(true).build()) .inOrder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // change an entry - 2, - // mutate a single element - 1, - // mutate the second element, - 1, - // mutate the first element - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // change an entry + 2, + // mutate a single element + 1, + // mutate the second element, + 1, + // mutate the first element + 0)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getMessageFieldList()) - .containsExactly(RequiredPrimitiveField2.newBuilder().setSomeField(true).build(), + .containsExactly( + RequiredPrimitiveField2.newBuilder().setSomeField(true).build(), RequiredPrimitiveField2.newBuilder().setSomeField(false).build()) .inOrder(); } @@ -298,15 +323,17 @@ void testRepeatedRequiredMessageField() { @Test void testRecursiveMessageField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()) .isEqualTo("{Builder.Boolean, WithoutInit(Builder.Nullable<(cycle) -> Message>)}"); RecursiveMessageField2.Builder builder = RecursiveMessageField2.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // boolean - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // boolean + true)) { mutator.initInPlace(builder, prng); } @@ -314,41 +341,46 @@ void testRecursiveMessageField() { .isEqualTo(RecursiveMessageField2.newBuilder().setSomeField(true).build()); assertThat(builder.hasMessageField()).isFalse(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate message field (causes init to non-null) - 1, - // bool field in message field - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate message field (causes init to non-null) + 1, + // bool field in message field + false)) { mutator.mutateInPlace(builder, prng); } // Nested message field *is* set explicitly and implicitly equal to the default // instance. assertThat(builder.build()) - .isEqualTo(RecursiveMessageField2.newBuilder() - .setSomeField(true) - .setMessageField(RecursiveMessageField2.newBuilder().setSomeField(false)) - .build()); + .isEqualTo( + RecursiveMessageField2.newBuilder() + .setSomeField(true) + .setMessageField(RecursiveMessageField2.newBuilder().setSomeField(false)) + .build()); assertThat(builder.hasMessageField()).isTrue(); assertThat(builder.getMessageField().hasMessageField()).isFalse(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate message field - 1, - // message field as not null - false, - // mutate message field - 1, - // nested boolean, - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate message field + 1, + // message field as not null + false, + // mutate message field + 1, + // nested boolean, + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) - .isEqualTo(RecursiveMessageField2.newBuilder() - .setSomeField(true) - .setMessageField( - RecursiveMessageField2.newBuilder().setSomeField(false).setMessageField( - RecursiveMessageField2.newBuilder().setSomeField(true))) - .build()); + .isEqualTo( + RecursiveMessageField2.newBuilder() + .setSomeField(true) + .setMessageField( + RecursiveMessageField2.newBuilder() + .setSomeField(false) + .setMessageField(RecursiveMessageField2.newBuilder().setSomeField(true))) + .build()); assertThat(builder.hasMessageField()).isTrue(); assertThat(builder.getMessageField().hasMessageField()).isTrue(); assertThat(builder.getMessageField().getMessageField().hasMessageField()).isFalse(); @@ -357,88 +389,97 @@ void testRecursiveMessageField() { @Test void testOneOfField2() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()) .isEqualTo( - "{Builder.Boolean, Builder.Nullable, Builder.Nullable | Builder.Nullable<{Builder.Boolean} -> Message>}"); + "{Builder.Boolean, Builder.Nullable, Builder.Nullable |" + + " Builder.Nullable<{Builder.Boolean} -> Message>}"); OneOfField2.Builder builder = OneOfField2.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // other_field - true, - // yet_another_field - true, - // oneof: first field - 0, - // bool_field present - false, - // bool_field - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // other_field + true, + // yet_another_field + true, + // oneof: first field + 0, + // bool_field present + false, + // bool_field + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.build()) .isEqualTo(OneOfField2.newBuilder().setOtherField(true).setBoolField(true).build()); assertThat(builder.build().hasBoolField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate oneof - 2, - // preserve oneof state - false, - // mutate bool_field as non-null - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate oneof + 2, + // preserve oneof state + false, + // mutate bool_field as non-null + false)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) .isEqualTo(OneOfField2.newBuilder().setOtherField(true).setBoolField(false).build()); assertThat(builder.build().hasBoolField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate oneof - 2, - // switch oneof state - true, - // new oneof state - 1, - // init message_field as non-null - false, - // init some_field as true - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate oneof + 2, + // switch oneof state + true, + // new oneof state + 1, + // init message_field as non-null + false, + // init some_field as true + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) - .isEqualTo(OneOfField2.newBuilder() - .setOtherField(true) - .setMessageField(RequiredPrimitiveField2.newBuilder().setSomeField(true)) - .build()); + .isEqualTo( + OneOfField2.newBuilder() + .setOtherField(true) + .setMessageField(RequiredPrimitiveField2.newBuilder().setSomeField(true)) + .build()); assertThat(builder.build().hasMessageField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate oneof - 2, - // preserve oneof state - false, - // mutate message_field as non-null - false, - // mutate some_field - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate oneof + 2, + // preserve oneof state + false, + // mutate message_field as non-null + false, + // mutate some_field + 0)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) - .isEqualTo(OneOfField2.newBuilder() - .setOtherField(true) - .setMessageField(RequiredPrimitiveField2.newBuilder().setSomeField(false)) - .build()); + .isEqualTo( + OneOfField2.newBuilder() + .setOtherField(true) + .setMessageField(RequiredPrimitiveField2.newBuilder().setSomeField(false)) + .build()); assertThat(builder.build().hasMessageField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate oneof - 2, - // preserve oneof state - false, - // mutate message_field to null (and thus oneof state to indeterminate) - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate oneof + 2, + // preserve oneof state + false, + // mutate message_field to null (and thus oneof state to indeterminate) + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()).isEqualTo(OneOfField2.newBuilder().setOtherField(true).build()); diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorProto3Test.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorProto3Test.java index ff298540c..67f940bea 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorProto3Test.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorProto3Test.java @@ -52,14 +52,16 @@ import org.junit.jupiter.api.Test; class BuilderMutatorProto3Test { - private static final MutatorFactory FACTORY = new ChainedMutatorFactory( - LangMutators.newFactory(), CollectionMutators.newFactory(), ProtoMutators.newFactory()); + private static final MutatorFactory FACTORY = + new ChainedMutatorFactory( + LangMutators.newFactory(), CollectionMutators.newFactory(), ProtoMutators.newFactory()); @Test void testPrimitiveField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.Boolean}"); PrimitiveField3.Builder builder = PrimitiveField3.newBuilder(); @@ -78,8 +80,9 @@ void testPrimitiveField() { @Test void testEnumField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.Enum}"); EnumField3.Builder builder = EnumField3.newBuilder(); try (MockPseudoRandom prng = mockPseudoRandom(0)) { @@ -95,8 +98,9 @@ void testEnumField() { @Test void testEnumFieldOutside() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.Enum}"); EnumFieldOutside3.Builder builder = EnumFieldOutside3.newBuilder(); try (MockPseudoRandom prng = mockPseudoRandom(0)) { @@ -112,8 +116,9 @@ void testEnumFieldOutside() { @Test void testEnumFieldWithOneValue() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.FixedValue(ONE)}"); EnumFieldOne3.Builder builder = EnumFieldOne3.newBuilder(); try (MockPseudoRandom prng = mockPseudoRandom()) { @@ -129,30 +134,33 @@ void testEnumFieldWithOneValue() { @Test void testRepeatedEnumField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder via List>}"); EnumFieldRepeated3.Builder builder = EnumFieldRepeated3.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // list size - 1, // Only possible start value - // enum values - 2)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // list size + 1, // Only possible start value + // enum values + 2)) { mutator.initInPlace(builder, prng); } assertThat(builder.getSomeFieldList()).isEqualTo(Arrays.asList(TestEnumRepeated.VAL2)); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // change an entry - 2, - // mutate a single element - 1, - // mutate to first enum field - 0, - // mutate to first enum value - 1)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // change an entry + 2, + // mutate a single element + 1, + // mutate to first enum field + 0, + // mutate to first enum value + 1)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getSomeFieldList()).isEqualTo(Arrays.asList(TestEnumRepeated.VAL1)); @@ -161,45 +169,50 @@ void testRepeatedEnumField() { @Test void testOptionalPrimitiveField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.Nullable}"); OptionalPrimitiveField3.Builder builder = OptionalPrimitiveField3.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // present - false, - // boolean - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // present + false, + // boolean + false)) { mutator.initInPlace(builder, prng); } assertThat(builder.hasSomeField()).isTrue(); assertThat(builder.getSomeField()).isFalse(); - try (MockPseudoRandom prng = mockPseudoRandom( - // present - false, - // boolean - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // present + false, + // boolean + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.hasSomeField()).isTrue(); assertThat(builder.getSomeField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate as non-null Boolean - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate as non-null Boolean + false)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.hasSomeField()).isTrue(); assertThat(builder.getSomeField()).isFalse(); - try (MockPseudoRandom prng = mockPseudoRandom( - // not present - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // not present + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.hasSomeField()).isFalse(); @@ -209,45 +222,49 @@ void testOptionalPrimitiveField() { @Test void testRepeatedPrimitiveField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder via List}"); RepeatedPrimitiveField3.Builder builder = RepeatedPrimitiveField3.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // list size 1 - 1, - // boolean, - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // list size 1 + 1, + // boolean, + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.getSomeFieldList()).containsExactly(true).inOrder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate the list itself by adding an entry - 1, - // add a single element - 1, - // add the element at the end - 1, - // value to add - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate the list itself by adding an entry + 1, + // add a single element + 1, + // add the element at the end + 1, + // value to add + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getSomeFieldList()).containsExactly(true, true).inOrder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate the list itself by changing an entry - 2, - // mutate a single element - 1, - // mutate the second element - 1)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate the list itself by changing an entry + 2, + // mutate a single element + 1, + // mutate the second element + 1)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getSomeFieldList()).containsExactly(true, false).inOrder(); @@ -256,41 +273,45 @@ void testRepeatedPrimitiveField() { @Test void testMessageField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder.Nullable<{Builder.Boolean} -> Message>}"); MessageField3.Builder builder = MessageField3.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // init submessage - false, - // boolean submessage field - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // init submessage + false, + // boolean submessage field + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.getMessageField()) .isEqualTo(PrimitiveField3.newBuilder().setSomeField(true).build()); assertThat(builder.hasMessageField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate submessage as non-null - false, - // mutate first field - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate submessage as non-null + false, + // mutate first field + 0)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getMessageField()) .isEqualTo(PrimitiveField3.newBuilder().setSomeField(false).build()); assertThat(builder.hasMessageField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate submessage to null - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate submessage to null + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.hasMessageField()).isFalse(); @@ -299,56 +320,62 @@ void testMessageField() { @Test void testRepeatedMessageField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{Builder via List<{Builder.Boolean} -> Message>}"); RepeatedMessageField3.Builder builder = RepeatedMessageField3.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // list size 1 - 1, - // boolean - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // list size 1 + 1, + // boolean + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.getMessageFieldList()) .containsExactly(PrimitiveField3.newBuilder().setSomeField(true).build()) .inOrder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // mutate the list itself by adding an entry - 1, - // add a single element - 1, - // add the element at the end - 1, - // value to add - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // mutate the list itself by adding an entry + 1, + // add a single element + 1, + // add the element at the end + 1, + // value to add + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getMessageFieldList()) - .containsExactly(PrimitiveField3.newBuilder().setSomeField(true).build(), + .containsExactly( + PrimitiveField3.newBuilder().setSomeField(true).build(), PrimitiveField3.newBuilder().setSomeField(true).build()) .inOrder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate first field - 0, - // change an entry - 2, - // mutate a single element - 1, - // mutate the second element, - 1, - // mutate the first element - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate first field + 0, + // change an entry + 2, + // mutate a single element + 1, + // mutate the second element, + 1, + // mutate the first element + 0)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.getMessageFieldList()) - .containsExactly(PrimitiveField3.newBuilder().setSomeField(true).build(), + .containsExactly( + PrimitiveField3.newBuilder().setSomeField(true).build(), PrimitiveField3.newBuilder().setSomeField(false).build()) .inOrder(); } @@ -356,15 +383,17 @@ void testRepeatedMessageField() { @Test void testRecursiveMessageField() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()) .isEqualTo("{Builder.Boolean, WithoutInit(Builder.Nullable<(cycle) -> Message>)}"); RecursiveMessageField3.Builder builder = RecursiveMessageField3.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // boolean - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // boolean + true)) { mutator.initInPlace(builder, prng); } @@ -372,41 +401,46 @@ void testRecursiveMessageField() { .isEqualTo(RecursiveMessageField3.newBuilder().setSomeField(true).build()); assertThat(builder.hasMessageField()).isFalse(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate message field (causes init to non-null) - 1, - // bool field in message field - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate message field (causes init to non-null) + 1, + // bool field in message field + false)) { mutator.mutateInPlace(builder, prng); } // Nested message field *is* set explicitly and implicitly equal to the default // instance. assertThat(builder.build()) - .isEqualTo(RecursiveMessageField3.newBuilder() - .setSomeField(true) - .setMessageField(RecursiveMessageField3.newBuilder().setSomeField(false)) - .build()); + .isEqualTo( + RecursiveMessageField3.newBuilder() + .setSomeField(true) + .setMessageField(RecursiveMessageField3.newBuilder().setSomeField(false)) + .build()); assertThat(builder.hasMessageField()).isTrue(); assertThat(builder.getMessageField().hasMessageField()).isFalse(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate message field - 1, - // message field as not null - false, - // mutate message field - 1, - // nested boolean, - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate message field + 1, + // message field as not null + false, + // mutate message field + 1, + // nested boolean, + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) - .isEqualTo(RecursiveMessageField3.newBuilder() - .setSomeField(true) - .setMessageField( - RecursiveMessageField3.newBuilder().setSomeField(false).setMessageField( - RecursiveMessageField3.newBuilder().setSomeField(true))) - .build()); + .isEqualTo( + RecursiveMessageField3.newBuilder() + .setSomeField(true) + .setMessageField( + RecursiveMessageField3.newBuilder() + .setSomeField(false) + .setMessageField(RecursiveMessageField3.newBuilder().setSomeField(true))) + .build()); assertThat(builder.hasMessageField()).isTrue(); assertThat(builder.getMessageField().hasMessageField()).isTrue(); assertThat(builder.getMessageField().getMessageField().hasMessageField()).isFalse(); @@ -415,98 +449,109 @@ void testRecursiveMessageField() { @Test void testOneOfField3() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()) .isEqualTo( - "{Builder.Boolean, Builder.Boolean, Builder.Nullable | Builder.Nullable<{Builder.Boolean} -> Message>}"); + "{Builder.Boolean, Builder.Boolean, Builder.Nullable |" + + " Builder.Nullable<{Builder.Boolean} -> Message>}"); OneOfField3.Builder builder = OneOfField3.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // other_field - true, - // yet_another_field - true, - // oneof: first field - 0, - // bool_field present - false, - // bool_field - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // other_field + true, + // yet_another_field + true, + // oneof: first field + 0, + // bool_field present + false, + // bool_field + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.build()) - .isEqualTo(OneOfField3.newBuilder() - .setOtherField(true) - .setBoolField(true) - .setYetAnotherField(true) - .build()); + .isEqualTo( + OneOfField3.newBuilder() + .setOtherField(true) + .setBoolField(true) + .setYetAnotherField(true) + .build()); assertThat(builder.build().hasBoolField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate oneof - 2, - // preserve oneof state - false, - // mutate bool_field as non-null - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate oneof + 2, + // preserve oneof state + false, + // mutate bool_field as non-null + false)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) - .isEqualTo(OneOfField3.newBuilder() - .setOtherField(true) - .setBoolField(false) - .setYetAnotherField(true) - .build()); + .isEqualTo( + OneOfField3.newBuilder() + .setOtherField(true) + .setBoolField(false) + .setYetAnotherField(true) + .build()); assertThat(builder.build().hasBoolField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate oneof - 2, - // switch oneof state - true, - // new oneof state - 1, - // init message_field as non-null - false, - // init some_field as true - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate oneof + 2, + // switch oneof state + true, + // new oneof state + 1, + // init message_field as non-null + false, + // init some_field as true + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) - .isEqualTo(OneOfField3.newBuilder() - .setOtherField(true) - .setMessageField(PrimitiveField3.newBuilder().setSomeField(true)) - .setYetAnotherField(true) - .build()); + .isEqualTo( + OneOfField3.newBuilder() + .setOtherField(true) + .setMessageField(PrimitiveField3.newBuilder().setSomeField(true)) + .setYetAnotherField(true) + .build()); assertThat(builder.build().hasMessageField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate oneof - 2, - // preserve oneof state - false, - // mutate message_field as non-null - false, - // mutate some_field - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate oneof + 2, + // preserve oneof state + false, + // mutate message_field as non-null + false, + // mutate some_field + 0)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) - .isEqualTo(OneOfField3.newBuilder() - .setOtherField(true) - .setMessageField(PrimitiveField3.newBuilder().setSomeField(false)) - .setYetAnotherField(true) - .build()); + .isEqualTo( + OneOfField3.newBuilder() + .setOtherField(true) + .setMessageField(PrimitiveField3.newBuilder().setSomeField(false)) + .setYetAnotherField(true) + .build()); assertThat(builder.build().hasMessageField()).isTrue(); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate oneof - 2, - // preserve oneof state - false, - // mutate message_field to null (and thus oneof state to indeterminate) - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate oneof + 2, + // preserve oneof state + false, + // mutate message_field to null (and thus oneof state to indeterminate) + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build()) @@ -517,8 +562,9 @@ void testOneOfField3() { @Test void testEmptyMessage3() { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder() {}.annotatedType()); assertThat(mutator.toString()).isEqualTo("{}"); EmptyMessage3.Builder builder = EmptyMessage3.newBuilder(); @@ -536,67 +582,73 @@ void testEmptyMessage3() { @Test void testAnyField3() throws InvalidProtocolBufferException { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder<@NotNull @AnySource( - {PrimitiveField3.class, MessageField3.class}) Builder>() { - }.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow( + new TypeHolder< + @NotNull @AnySource({PrimitiveField3.class, MessageField3.class}) + Builder>() {}.annotatedType()); assertThat(mutator.toString()) .isEqualTo( - "{Builder.Nullable Message | Builder.{Builder.Nullable<(cycle) -> Message>} -> Message -> Message>}"); + "{Builder.Nullable Message |" + + " Builder.{Builder.Nullable<(cycle) -> Message>} -> Message -> Message>}"); AnyField3.Builder builder = AnyField3.newBuilder(); - try (MockPseudoRandom prng = mockPseudoRandom( - // initialize message field - false, - // PrimitiveField3 - 0, - // boolean field - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // initialize message field + false, + // PrimitiveField3 + 0, + // boolean field + true)) { mutator.initInPlace(builder, prng); } assertThat(builder.build().getSomeField().unpack(PrimitiveField3.class)) .isEqualTo(PrimitiveField3.newBuilder().setSomeField(true).build()); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate Any field - 0, - // keep non-null message field - false, - // keep Any state, - false, - // mutate boolean field - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate Any field + 0, + // keep non-null message field + false, + // keep Any state, + false, + // mutate boolean field + 0)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build().getSomeField().unpack(PrimitiveField3.class)) .isEqualTo(PrimitiveField3.newBuilder().setSomeField(false).build()); - try (MockPseudoRandom prng = mockPseudoRandom( - // mutate Any field - 0, - // keep non-null message field - false, - // switch Any state - true, - // new Any state - 1, - // non-null message - false, - // boolean field, - true)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // mutate Any field + 0, + // keep non-null message field + false, + // switch Any state + true, + // new Any state + 1, + // non-null message + false, + // boolean field, + true)) { mutator.mutateInPlace(builder, prng); } assertThat(builder.build().getSomeField().unpack(MessageField3.class)) - .isEqualTo(MessageField3.newBuilder() - .setMessageField(PrimitiveField3.newBuilder().setSomeField(true)) - .build()); + .isEqualTo( + MessageField3.newBuilder() + .setMessageField(PrimitiveField3.newBuilder().setSomeField(true)) + .build()); } @Test void testAnyField3WithoutAnySourceDoesNotCrash() throws InvalidProtocolBufferException { InPlaceMutator mutator = - (InPlaceMutator) FACTORY.createInPlaceOrThrow( - new TypeHolder<@NotNull Builder>() {}.annotatedType()); + (InPlaceMutator) + FACTORY.createInPlaceOrThrow(new TypeHolder<@NotNull Builder>() {}.annotatedType()); assertThat(mutator.toString()) .isEqualTo("{Builder.Nullable<{Builder.String, Builder.byte[] -> ByteString} -> Message>}"); } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java index b804c7fb5..a8111cb4e 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/MessageMutatorTest.java @@ -38,8 +38,9 @@ import org.junit.jupiter.api.Test; class MessageMutatorTest { - private static final MutatorFactory FACTORY = new ChainedMutatorFactory( - LangMutators.newFactory(), CollectionMutators.newFactory(), ProtoMutators.newFactory()); + private static final MutatorFactory FACTORY = + new ChainedMutatorFactory( + LangMutators.newFactory(), CollectionMutators.newFactory(), ProtoMutators.newFactory()); @Test void testSimpleMessage() { @@ -47,20 +48,22 @@ void testSimpleMessage() { PrimitiveField3 msg; - try (MockPseudoRandom prng = mockPseudoRandom( - // not null - false, - // boolean - false)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // not null + false, + // boolean + false)) { msg = mutator.init(prng); assertThat(msg).isEqualTo(PrimitiveField3.getDefaultInstance()); } - try (MockPseudoRandom prng = mockPseudoRandom( - // not null, - false, - // mutate first field - 0)) { + try (MockPseudoRandom prng = + mockPseudoRandom( + // not null, + false, + // mutate first field + 0)) { msg = mutator.mutate(msg, prng); assertThat(msg).isNotEqualTo(PrimitiveField3.getDefaultInstance()); } @@ -77,16 +80,19 @@ void testIncompleteMessageWithRequiredFields() throws IOException { byte[] bytes = out.toByteArray(); SerializingMutator mutator = - (SerializingMutator) FACTORY.createOrThrow( - new TypeHolder<@NotNull ExtendedMessage2>() {}.annotatedType()); + (SerializingMutator) + FACTORY.createOrThrow(new TypeHolder<@NotNull ExtendedMessage2>() {}.annotatedType()); ExtendedMessage2 extendedMessage = mutator.readExclusive(new ByteArrayInputStream(bytes)); assertThat(extendedMessage) - .isEqualTo(ExtendedMessage2.newBuilder() - .setMessageField( - ExtendedSubmessage2.newBuilder().setNumericField(42).setMessageField( - OriginalSubmessage2.newBuilder().setNumericField(0).build())) - .setBoolField(true) - .setFloatField(0) - .build()); + .isEqualTo( + ExtendedMessage2.newBuilder() + .setMessageField( + ExtendedSubmessage2.newBuilder() + .setNumericField(42) + .setMessageField( + OriginalSubmessage2.newBuilder().setNumericField(0).build())) + .setBoolField(true) + .setFloatField(0) + .build()); } } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/support/ExceptionSupportTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/support/ExceptionSupportTest.java index 630b7cdf2..2a77cc68f 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/support/ExceptionSupportTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/support/ExceptionSupportTest.java @@ -25,19 +25,32 @@ class ExceptionSupportTest { @Test void testAsUnchecked_withUncheckedException() { - assertThrows(IllegalStateException.class, () -> { - // noinspection TrivialFunctionalExpressionUsage - ((Runnable) () -> { throw asUnchecked(new IllegalStateException()); }).run(); - }); + assertThrows( + IllegalStateException.class, + () -> { + // noinspection TrivialFunctionalExpressionUsage + ((Runnable) + () -> { + throw asUnchecked(new IllegalStateException()); + }) + .run(); + }); } @Test void testAsUnchecked_withCheckedException() { - assertThrows(IOException.class, () -> { - // Verify that asUnchecked can be used to throw a checked exception in a function that doesn't - // declare it as being thrown. - // noinspection TrivialFunctionalExpressionUsage - ((Runnable) () -> { throw asUnchecked(new IOException()); }).run(); - }); + assertThrows( + IOException.class, + () -> { + // Verify that asUnchecked can be used to throw a checked exception in a function that + // doesn't + // declare it as being thrown. + // noinspection TrivialFunctionalExpressionUsage + ((Runnable) + () -> { + throw asUnchecked(new IOException()); + }) + .run(); + }); } } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/support/HolderTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/support/HolderTest.java index 97450e57b..50f087a73 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/support/HolderTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/support/HolderTest.java @@ -61,9 +61,10 @@ void testTypeHolder_annotatedType() { @Test void testParameterHolder_rawType() { - Type type = new ParameterHolder() { - void foo(List parameter) {} - }.type(); + Type type = + new ParameterHolder() { + void foo(List parameter) {} + }.type(); assertThat(type).isInstanceOf(ParameterizedType.class); ParameterizedType parameterizedType = (ParameterizedType) type; @@ -73,9 +74,10 @@ void foo(List parameter) {} @Test void testParameterHolder_annotatedType() { - AnnotatedType type = new ParameterHolder() { - void foo(@ParameterAnnotation @Foo List<@Bar String> parameter) {} - }.annotatedType(); + AnnotatedType type = + new ParameterHolder() { + void foo(@ParameterAnnotation @Foo List<@Bar String> parameter) {} + }.annotatedType(); assertThat(type).isInstanceOf(AnnotatedParameterizedType.class); AnnotatedParameterizedType listType = (AnnotatedParameterizedType) type; @@ -93,9 +95,10 @@ void foo(@ParameterAnnotation @Foo List<@Bar String> parameter) {} @Test void testParameterHolder_parameterAnnotations() { - Annotation[] annotations = new ParameterHolder() { - void foo(@ParameterAnnotation @Foo List<@Bar String> parameter) {} - }.parameterAnnotations(); + Annotation[] annotations = + new ParameterHolder() { + void foo(@ParameterAnnotation @Foo List<@Bar String> parameter) {} + }.parameterAnnotations(); assertThat(annotations).hasLength(1); assertThat(annotations[0]).isInstanceOf(ParameterAnnotation.class); } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/support/TestSupport.java b/src/test/java/com/code_intelligence/jazzer/mutation/support/TestSupport.java index 8035ef866..6151fa8fe 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/support/TestSupport.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/support/TestSupport.java @@ -45,10 +45,11 @@ public final class TestSupport { private static final DataOutputStream nullDataOutputStream = - new DataOutputStream(new OutputStream() { - @Override - public void write(int i) {} - }); + new DataOutputStream( + new OutputStream() { + @Override + public void write(int i) {} + }); private TestSupport() {} @@ -66,9 +67,7 @@ public static PseudoRandom anyPseudoRandom() { return new SeededPseudoRandom(8853461259049838337L); } - /** - * Creates a {@link PseudoRandom} whose methods return the given values in order. - */ + /** Creates a {@link PseudoRandom} whose methods return the given values in order. */ @MustBeClosed public static MockPseudoRandom mockPseudoRandom(Object... returnValues) { return new MockPseudoRandom(returnValues); @@ -176,7 +175,7 @@ public String toDebugString(Predicate isInCycle) { }; } - private static abstract class AbstractMockInPlaceMutator implements InPlaceMutator { + private abstract static class AbstractMockInPlaceMutator implements InPlaceMutator { @Override public void initInPlace(T reference, PseudoRandom prng) { throw new UnsupportedOperationException(); @@ -193,8 +192,8 @@ public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) { } } - private static abstract class AbstractMockMutator extends SerializingMutator { - abstract protected T nextInitialValue(); + private abstract static class AbstractMockMutator extends SerializingMutator { + protected abstract T nextInitialValue(); @Override public T read(DataInputStream in) { diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/support/TypeSupportTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/support/TypeSupportTest.java index bbf4a7e6f..4f17ed4d5 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/support/TypeSupportTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/support/TypeSupportTest.java @@ -47,11 +47,12 @@ class TypeSupportTest { @Test void testFillTypeVariablesRawType_oneVariable() { AnnotatedParameterizedType actual = - withTypeArguments(new TypeHolder<@NotNull List>() {}.annotatedType(), + withTypeArguments( + new TypeHolder<@NotNull List>() {}.annotatedType(), new TypeHolder<@NotNull String>() {}.annotatedType()); AnnotatedParameterizedType expected = - (AnnotatedParameterizedType) new TypeHolder<@NotNull List<@NotNull String>>() { - }.annotatedType(); + (AnnotatedParameterizedType) + new TypeHolder<@NotNull List<@NotNull String>>() {}.annotatedType(); // Test both equals implementations as we implement them ourselves. assertThat(actual.getType()).isEqualTo(expected.getType()); @@ -72,11 +73,12 @@ void testFillTypeVariablesRawType_oneVariable() { @EnabledForJreRange(min = JRE.JAVA_12) void testFillTypeVariablesAnnotatedType_oneVariable() { AnnotatedParameterizedType actual = - withTypeArguments(new TypeHolder<@NotNull List>() {}.annotatedType(), + withTypeArguments( + new TypeHolder<@NotNull List>() {}.annotatedType(), new TypeHolder<@NotNull String>() {}.annotatedType()); AnnotatedParameterizedType expected = - (AnnotatedParameterizedType) new TypeHolder<@NotNull List<@NotNull String>>() { - }.annotatedType(); + (AnnotatedParameterizedType) + new TypeHolder<@NotNull List<@NotNull String>>() {}.annotatedType(); // Test both equals implementations as we implement them ourselves. assertThat(actual).isEqualTo(expected); @@ -97,11 +99,12 @@ void testFillTypeVariablesAnnotatedType_oneVariable() { @Test void testFillTypeVariablesRawType_oneVariable_differentType() { AnnotatedParameterizedType actual = - withTypeArguments(new TypeHolder<@NotNull List>() {}.annotatedType(), + withTypeArguments( + new TypeHolder<@NotNull List>() {}.annotatedType(), new TypeHolder<@NotNull String>() {}.annotatedType()); AnnotatedParameterizedType differentParameterAnnotation = - (AnnotatedParameterizedType) new TypeHolder<@NotNull List<@NotNull Boolean>>() { - }.annotatedType(); + (AnnotatedParameterizedType) + new TypeHolder<@NotNull List<@NotNull Boolean>>() {}.annotatedType(); // Test both equals implementations as we implement them ourselves. assertThat(actual.getType()).isNotEqualTo(differentParameterAnnotation.getType()); @@ -114,7 +117,7 @@ void testFillTypeVariablesRawType_oneVariable_differentType() { .isNotEqualTo( ((ParameterizedType) differentParameterAnnotation.getType()).getActualTypeArguments()); assertThat( - ((ParameterizedType) differentParameterAnnotation.getType()).getActualTypeArguments()) + ((ParameterizedType) differentParameterAnnotation.getType()).getActualTypeArguments()) .isNotEqualTo(((ParameterizedType) actual.getType()).getActualTypeArguments()); } @@ -124,7 +127,8 @@ void testFillTypeVariablesRawType_oneVariable_differentType() { @EnabledForJreRange(min = JRE.JAVA_12) void testFillTypeVariablesAnnotatedType_oneVariable_differentAnnotations() { AnnotatedParameterizedType actual = - withTypeArguments(new TypeHolder<@NotNull List>() {}.annotatedType(), + withTypeArguments( + new TypeHolder<@NotNull List>() {}.annotatedType(), new TypeHolder<@NotNull String>() {}.annotatedType()); AnnotatedParameterizedType differentParameterAnnotation = (AnnotatedParameterizedType) new TypeHolder<@NotNull List>() {}.annotatedType(); @@ -148,12 +152,13 @@ void testFillTypeVariablesAnnotatedType_oneVariable_differentAnnotations() { @Test void testFillTypeVariablesRawType_twoVariables() { AnnotatedParameterizedType actual = - withTypeArguments(new TypeHolder<@NotNull Map>() {}.annotatedType(), + withTypeArguments( + new TypeHolder<@NotNull Map>() {}.annotatedType(), new TypeHolder<@NotNull String>() {}.annotatedType(), new TypeHolder() {}.annotatedType()); AnnotatedParameterizedType expected = - (AnnotatedParameterizedType) new TypeHolder<@NotNull Map<@NotNull String, byte[]>>() { - }.annotatedType(); + (AnnotatedParameterizedType) + new TypeHolder<@NotNull Map<@NotNull String, byte[]>>() {}.annotatedType(); // Test both equals implementations as we implement them ourselves. assertThat(actual.getType()).isEqualTo(expected.getType()); @@ -174,12 +179,13 @@ void testFillTypeVariablesRawType_twoVariables() { @EnabledForJreRange(min = JRE.JAVA_12) void testFillTypeVariablesAnnotatedType_twoVariables() { AnnotatedParameterizedType actual = - withTypeArguments(new TypeHolder<@NotNull Map>() {}.annotatedType(), + withTypeArguments( + new TypeHolder<@NotNull Map>() {}.annotatedType(), new TypeHolder<@NotNull String>() {}.annotatedType(), new TypeHolder() {}.annotatedType()); AnnotatedParameterizedType expected = - (AnnotatedParameterizedType) new TypeHolder<@NotNull Map<@NotNull String, byte[]>>() { - }.annotatedType(); + (AnnotatedParameterizedType) + new TypeHolder<@NotNull Map<@NotNull String, byte[]>>() {}.annotatedType(); // Test both equals implementations as we implement them ourselves. assertThat(actual).isEqualTo(expected); @@ -199,10 +205,14 @@ void testFillTypeVariablesAnnotatedType_twoVariables() { @Test void testFillTypeVariables_failures() { - assertThrows(IllegalArgumentException.class, + assertThrows( + IllegalArgumentException.class, () -> withTypeArguments(new TypeHolder() {}.annotatedType())); - assertThrows(IllegalArgumentException.class, () -> withTypeArguments(new TypeHolder>() { - }.annotatedType(), asAnnotatedType(String.class))); + assertThrows( + IllegalArgumentException.class, + () -> + withTypeArguments( + new TypeHolder>() {}.annotatedType(), asAnnotatedType(String.class))); } @Test @@ -212,8 +222,8 @@ void testAsSubclassOrEmpty() { assertThat(asSubclassOrEmpty(asAnnotatedType(String.class), CharSequence.class)) .hasValue(String.class); assertThat(asSubclassOrEmpty(asAnnotatedType(CharSequence.class), String.class)).isEmpty(); - assertThat(asSubclassOrEmpty(new TypeHolder>() { - }.annotatedType(), List.class)).isEmpty(); + assertThat(asSubclassOrEmpty(new TypeHolder>() {}.annotatedType(), List.class)) + .isEmpty(); } @Target(ElementType.TYPE_USE) @@ -225,40 +235,59 @@ void testAsSubclassOrEmpty() { @Test void testVisitAnnotatedType() { Map> visited = new LinkedHashMap<>(); - AnnotatedType type = new TypeHolder<@A( - 1) List<@A(2) Map<@A(3) byte @A(4)[] @A(5)[], @A(6) Byte> @A(7)[] @A(8)[]>>(){} - .annotatedType(); - - visitAnnotatedType(type, - (clazz, annotations) - -> stream(annotations) - .map(annotation -> ((A) annotation).value()) - .forEach(value -> visited.put(value, clazz))); + AnnotatedType type = + new TypeHolder< + @A(1) List< + @A(2) Map<@A(3) byte @A(4) [] @A(5) [], @A(6) Byte> @A(7) [] @A(8) + []>>() {}.annotatedType(); + + visitAnnotatedType( + type, + (clazz, annotations) -> + stream(annotations) + .map(annotation -> ((A) annotation).value()) + .forEach(value -> visited.put(value, clazz))); assertThat(visited) - .containsExactly(1, List.class, 7, Map[][].class, 8, Map[].class, 2, Map.class, 4, - byte[][].class, 5, byte[].class, 3, byte.class, 6, Byte.class) + .containsExactly( + 1, + List.class, + 7, + Map[][].class, + 8, + Map[].class, + 2, + Map.class, + 4, + byte[][].class, + 5, + byte[].class, + 3, + byte.class, + 6, + Byte.class) .inOrder(); } @Test void testContainedInDirectedCycle() { - Function> successors = integer -> { - switch (integer) { - case 1: - return Stream.of(2); - case 2: - return Stream.of(3); - case 3: - return Stream.of(4, 5); - case 4: - return Stream.of(2); - case 5: - return Stream.empty(); - default: - throw new IllegalStateException(); - } - }; + Function> successors = + integer -> { + switch (integer) { + case 1: + return Stream.of(2); + case 2: + return Stream.of(3); + case 3: + return Stream.of(4, 5); + case 4: + return Stream.of(2); + case 5: + return Stream.empty(); + default: + throw new IllegalStateException(); + } + }; assertThat(containedInDirectedCycle(1, successors)).isFalse(); assertThat(containedInDirectedCycle(2, successors)).isTrue(); diff --git a/src/test/java/com/code_intelligence/jazzer/runtime/TraceCmpHooksTest.java b/src/test/java/com/code_intelligence/jazzer/runtime/TraceCmpHooksTest.java index a1ef86ffd..ed63453d7 100644 --- a/src/test/java/com/code_intelligence/jazzer/runtime/TraceCmpHooksTest.java +++ b/src/test/java/com/code_intelligence/jazzer/runtime/TraceCmpHooksTest.java @@ -34,9 +34,11 @@ public void cmpHookShouldHandleConcurrentModifications() throws InterruptedExcep map.put(arg, arg); // Add elements to map asynchronously - Function put = (final Integer num) -> () -> { - map.put(String.valueOf(num), num); - }; + Function put = + (final Integer num) -> + () -> { + map.put(String.valueOf(num), num); + }; for (int i = 0; i < 1_000_000; i++) { ES.submit(put.apply(i)); } diff --git a/tests/benchmarks/src/test/java/com/example/StructuredMutatorMazeFuzzer.java b/tests/benchmarks/src/test/java/com/example/StructuredMutatorMazeFuzzer.java index 709dcd15f..09ac4fd9c 100644 --- a/tests/benchmarks/src/test/java/com/example/StructuredMutatorMazeFuzzer.java +++ b/tests/benchmarks/src/test/java/com/example/StructuredMutatorMazeFuzzer.java @@ -24,51 +24,60 @@ // A variant of //examples:MazeFuzzer that uses the structured mutator. public final class StructuredMutatorMazeFuzzer { - private static final String[] MAZE_STRING = new String[] { - " ███████████████████", - " █ █ █ █ █ █", - "█ █ █ █ ███ █ █ █ ███", - "█ █ █ █ █ █", - "█ █████ ███ ███ █ ███", - "█ █ █ █ █ █", - "█ ███ ███████ █ ███ █", - "█ █ █ █ █ █", - "███████ █ █ █████ ███", - "█ █ █ █ █", - "█ ███████ █ ███ ███ █", - "█ █ █ █ █ █ █", - "███ ███ █ ███ █ ███ █", - "█ █ █ █ █ █", - "█ ███████ █ █ █ █ █ █", - "█ █ █ █ █ █ █", - "█ █ █████████ ███ ███", - "█ █ █ █ █ █ █", - "█ █ █ ███ █████ ███ █", - "█ █ █ ", - "███████████████████ #", - }; + private static final String[] MAZE_STRING = + new String[] { + " ███████████████████", + " █ █ █ █ █ █", + "█ █ █ █ ███ █ █ █ ███", + "█ █ █ █ █ █", + "█ █████ ███ ███ █ ███", + "█ █ █ █ █ █", + "█ ███ ███████ █ ███ █", + "█ █ █ █ █ █", + "███████ █ █ █████ ███", + "█ █ █ █ █", + "█ ███████ █ ███ ███ █", + "█ █ █ █ █ █ █", + "███ ███ █ ███ █ ███ █", + "█ █ █ █ █ █", + "█ ███████ █ █ █ █ █ █", + "█ █ █ █ █ █ █", + "█ █ █████████ ███ ███", + "█ █ █ █ █ █ █", + "█ █ █ ███ █████ ███ █", + "█ █ █ ", + "███████████████████ #", + }; private static final char[][] MAZE = parseMaze(); private static final char[][] REACHED_FIELDS = parseMaze(); - enum Command { LEFT, RIGHT, UP, DOWN } + enum Command { + LEFT, + RIGHT, + UP, + DOWN + } public static void fuzzerTestOneInput(@NotNull List<@NotNull Command> commands) { - executeCommands(commands, (x, y, won) -> { - if (won) { - throw new TreasureFoundException(commands); - } - // This is the key line that makes this fuzz target work: It instructs the fuzzer to track - // every new combination of x and y as a new feature. Without it, the fuzzer would be - // completely lost in the maze as guessing an escaping path by chance is close to impossible. - Jazzer.exploreState((byte) Objects.hash(x, y), 0); - if (REACHED_FIELDS[y][x] == ' ') { - // Fuzzer reached a new field in the maze, print its progress. - REACHED_FIELDS[y][x] = '.'; - // The following line is commented out to reduce test log sizes. - // System.out.println(renderMaze(REACHED_FIELDS)); - } - }); + executeCommands( + commands, + (x, y, won) -> { + if (won) { + throw new TreasureFoundException(commands); + } + // This is the key line that makes this fuzz target work: It instructs the fuzzer to track + // every new combination of x and y as a new feature. Without it, the fuzzer would be + // completely lost in the maze as guessing an escaping path by chance is close to + // impossible. + Jazzer.exploreState((byte) Objects.hash(x, y), 0); + if (REACHED_FIELDS[y][x] == ' ') { + // Fuzzer reached a new field in the maze, print its progress. + REACHED_FIELDS[y][x] = '.'; + // The following line is commented out to reduce test log sizes. + // System.out.println(renderMaze(REACHED_FIELDS)); + } + }); } private static class TreasureFoundException extends RuntimeException { @@ -121,7 +130,7 @@ private static void executeCommands( } private static char[][] parseMaze() { - return Arrays.stream(MAZE_STRING).map(String::toCharArray).toArray(char[][] ::new); + return Arrays.stream(MAZE_STRING).map(String::toCharArray).toArray(char[][]::new); } private static String renderMaze(char[][] maze) { @@ -130,11 +139,13 @@ private static String renderMaze(char[][] maze) { private static String renderPath(List commands) { char[][] mutableMaze = parseMaze(); - executeCommands(commands, (x, y, won) -> { - if (!won) { - mutableMaze[y][x] = '.'; - } - }); + executeCommands( + commands, + (x, y, won) -> { + if (!won) { + mutableMaze[y][x] = '.'; + } + }); return renderMaze(mutableMaze); } } diff --git a/tests/benchmarks/src/test/java/com/example/UnstructuredPackedMazeFuzzer.java b/tests/benchmarks/src/test/java/com/example/UnstructuredPackedMazeFuzzer.java index fe85eaa71..3446bd6aa 100644 --- a/tests/benchmarks/src/test/java/com/example/UnstructuredPackedMazeFuzzer.java +++ b/tests/benchmarks/src/test/java/com/example/UnstructuredPackedMazeFuzzer.java @@ -26,49 +26,53 @@ // in the maze, which is more useful than going left. This makes the comparison with the structured // mutator version more fair. public final class UnstructuredPackedMazeFuzzer { - private static final String[] MAZE_STRING = new String[] { - " ███████████████████", - " █ █ █ █ █ █", - "█ █ █ █ ███ █ █ █ ███", - "█ █ █ █ █ █", - "█ █████ ███ ███ █ ███", - "█ █ █ █ █ █", - "█ ███ ███████ █ ███ █", - "█ █ █ █ █ █", - "███████ █ █ █████ ███", - "█ █ █ █ █", - "█ ███████ █ ███ ███ █", - "█ █ █ █ █ █ █", - "███ ███ █ ███ █ ███ █", - "█ █ █ █ █ █", - "█ ███████ █ █ █ █ █ █", - "█ █ █ █ █ █ █", - "█ █ █████████ ███ ███", - "█ █ █ █ █ █ █", - "█ █ █ ███ █████ ███ █", - "█ █ █ ", - "███████████████████ #", - }; + private static final String[] MAZE_STRING = + new String[] { + " ███████████████████", + " █ █ █ █ █ █", + "█ █ █ █ ███ █ █ █ ███", + "█ █ █ █ █ █", + "█ █████ ███ ███ █ ███", + "█ █ █ █ █ █", + "█ ███ ███████ █ ███ █", + "█ █ █ █ █ █", + "███████ █ █ █████ ███", + "█ █ █ █ █", + "█ ███████ █ ███ ███ █", + "█ █ █ █ █ █ █", + "███ ███ █ ███ █ ███ █", + "█ █ █ █ █ █", + "█ ███████ █ █ █ █ █ █", + "█ █ █ █ █ █ █", + "█ █ █████████ ███ ███", + "█ █ █ █ █ █ █", + "█ █ █ ███ █████ ███ █", + "█ █ █ ", + "███████████████████ #", + }; private static final char[][] MAZE = parseMaze(); private static final char[][] REACHED_FIELDS = parseMaze(); public static void fuzzerTestOneInput(byte[] commands) { - executeCommands(commands, (x, y, won) -> { - if (won) { - throw new TreasureFoundException(commands); - } - // This is the key line that makes this fuzz target work: It instructs the fuzzer to track - // every new combination of x and y as a new feature. Without it, the fuzzer would be - // completely lost in the maze as guessing an escaping path by chance is close to impossible. - Jazzer.exploreState((byte) Objects.hash(x, y), 0); - if (REACHED_FIELDS[y][x] == ' ') { - // Fuzzer reached a new field in the maze, print its progress. - REACHED_FIELDS[y][x] = '.'; - // The following line is commented out to reduce test log sizes. - // System.out.println(renderMaze(REACHED_FIELDS)); - } - }); + executeCommands( + commands, + (x, y, won) -> { + if (won) { + throw new TreasureFoundException(commands); + } + // This is the key line that makes this fuzz target work: It instructs the fuzzer to track + // every new combination of x and y as a new feature. Without it, the fuzzer would be + // completely lost in the maze as guessing an escaping path by chance is close to + // impossible. + Jazzer.exploreState((byte) Objects.hash(x, y), 0); + if (REACHED_FIELDS[y][x] == ' ') { + // Fuzzer reached a new field in the maze, print its progress. + REACHED_FIELDS[y][x] = '.'; + // The following line is commented out to reduce test log sizes. + // System.out.println(renderMaze(REACHED_FIELDS)); + } + }); } private static class TreasureFoundException extends RuntimeException { @@ -125,7 +129,7 @@ private static void executeCommands(byte[] commands, Consumer3 { - if (!won) { - mutableMaze[y][x] = '.'; - } - }); + executeCommands( + commands, + (x, y, won) -> { + if (!won) { + mutableMaze[y][x] = '.'; + } + }); return renderMaze(mutableMaze); } } diff --git a/tests/src/test/java/com/example/CoverageFuzzer.java b/tests/src/test/java/com/example/CoverageFuzzer.java index 1d65d3b7d..c35e710bf 100644 --- a/tests/src/test/java/com/example/CoverageFuzzer.java +++ b/tests/src/test/java/com/example/CoverageFuzzer.java @@ -34,13 +34,13 @@ /** * Test of coverage report and dump. * - * Internally, JaCoCo is used to gather coverage information to guide the fuzzer to cover new + *

            Internally, JaCoCo is used to gather coverage information to guide the fuzzer to cover new * branches. This information can be dumped in the JaCoCo format and used to generate reports later * on. The dump only contains classes with at least one coverage data point. A JaCoCo report will - * also include completely uncovered files based on the available classes in the stated jar files - * in the report command. + * also include completely uncovered files based on the available classes in the stated jar files in + * the report command. * - * A human-readable coverage report can be generated directly by Jazzer. It contains information + *

            A human-readable coverage report can be generated directly by Jazzer. It contains information * on file level about all classes that should have been instrumented according to the * instrumentation_includes and instrumentation_exclude filters. */ @@ -49,9 +49,11 @@ public final class CoverageFuzzer { // Not used during fuzz run, so not included in the dump public static class ClassNotToCover { private final int i; + public ClassNotToCover(int i) { this.i = i; } + public int getI() { return i; } @@ -96,13 +98,14 @@ private static void assertCoverageReport() throws IOException { List coverage = Files.readAllLines(Paths.get(System.getenv("COVERAGE_REPORT_FILE"))); List> sections = new ArrayList<>(4); sections.add(new ArrayList<>()); - coverage.forEach(l -> { - if (l.isEmpty()) { - sections.add(new ArrayList<>()); - } else { - sections.get(sections.size() - 1).add(l); - } - }); + coverage.forEach( + l -> { + if (l.isEmpty()) { + sections.add(new ArrayList<>()); + } else { + sections.get(sections.size() - 1).add(l); + } + }); List branchCoverage = sections.get(0); assertEquals(2, branchCoverage.size()); @@ -136,13 +139,15 @@ private static void assertCoverageReport() throws IOException { .filter(l -> l.startsWith(CoverageFuzzer.class.getSimpleName())) .findFirst() .orElseThrow(() -> new IllegalStateException("Could not find missed coverage")); - List missingLines = IntStream.rangeClosed(63, 79) - .mapToObj(i -> " " + i) - .filter(missed::contains) - .collect(Collectors.toList()); + List missingLines = + IntStream.rangeClosed(63, 79) + .mapToObj(i -> " " + i) + .filter(missed::contains) + .collect(Collectors.toList()); if (!missingLines.isEmpty()) { - throw new IllegalStateException(String.format( - "Missing coverage for ClassToCover on lines %s", String.join(", ", missingLines))); + throw new IllegalStateException( + String.format( + "Missing coverage for ClassToCover on lines %s", String.join(", ", missingLines))); } } @@ -177,8 +182,7 @@ private static void assertCoverageDump() throws IOException { private static int countHits(boolean[] probes) { int count = 0; for (boolean probe : probes) { - if (probe) - count++; + if (probe) count++; } return count; } diff --git a/tests/src/test/java/com/example/DisabledHooksFuzzer.java b/tests/src/test/java/com/example/DisabledHooksFuzzer.java index f9dbdcba5..34c1e4913 100644 --- a/tests/src/test/java/com/example/DisabledHooksFuzzer.java +++ b/tests/src/test/java/com/example/DisabledHooksFuzzer.java @@ -43,10 +43,13 @@ private static void triggerBuiltinHook() { } class DisabledHook { - @MethodHook(type = HookType.BEFORE, targetClassName = "com.example.DisabledHooksFuzzer", - targetMethod = "triggerCustomHook", targetMethodDescriptor = "()V") - public static void - triggerCustomHookHook(MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + @MethodHook( + type = HookType.BEFORE, + targetClassName = "com.example.DisabledHooksFuzzer", + targetMethod = "triggerCustomHook", + targetMethodDescriptor = "()V") + public static void triggerCustomHookHook( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { Jazzer.reportFindingFromHook( new IllegalStateException("hook on triggerCustomHook should have been disabled")); } diff --git a/tests/src/test/java/com/example/ExperimentalMutatorComplexProtoFuzzer.java b/tests/src/test/java/com/example/ExperimentalMutatorComplexProtoFuzzer.java index 4c3ed31b3..749e0dfd7 100644 --- a/tests/src/test/java/com/example/ExperimentalMutatorComplexProtoFuzzer.java +++ b/tests/src/test/java/com/example/ExperimentalMutatorComplexProtoFuzzer.java @@ -17,7 +17,6 @@ package com.example; import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; -import com.code_intelligence.jazzer.mutation.annotation.InRange; import com.code_intelligence.jazzer.mutation.annotation.NotNull; import com.code_intelligence.jazzer.protobuf.Proto2.TestProtobuf; diff --git a/tests/src/test/java/com/example/ExperimentalMutatorDynamicProtoFuzzer.java b/tests/src/test/java/com/example/ExperimentalMutatorDynamicProtoFuzzer.java index bbca1ddca..9da6e5623 100644 --- a/tests/src/test/java/com/example/ExperimentalMutatorDynamicProtoFuzzer.java +++ b/tests/src/test/java/com/example/ExperimentalMutatorDynamicProtoFuzzer.java @@ -23,7 +23,6 @@ import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Type; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; -import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.DescriptorValidationException; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FileDescriptor; @@ -31,8 +30,11 @@ import com.google.protobuf.Message; public class ExperimentalMutatorDynamicProtoFuzzer { - public static void fuzzerTestOneInput(@NotNull @WithDefaultInstance( - "com.example.ExperimentalMutatorDynamicProtoFuzzer#getDefaultInstance") Message proto) { + public static void fuzzerTestOneInput( + @NotNull + @WithDefaultInstance( + "com.example.ExperimentalMutatorDynamicProtoFuzzer#getDefaultInstance") + Message proto) { FieldDescriptor I32 = proto.getDescriptorForType().findFieldByName("i32"); FieldDescriptor STR = proto.getDescriptorForType().findFieldByName("str"); if (proto.getField(I32).equals(1234) && proto.getField(STR).equals("abcd")) { @@ -45,18 +47,26 @@ private static DynamicMessage getDefaultInstance() { DescriptorProto myMessage = DescriptorProto.newBuilder() .setName("my_message") - .addField(FieldDescriptorProto.newBuilder().setNumber(1).setName("i32").setType( - Type.TYPE_INT32)) - .addField(FieldDescriptorProto.newBuilder().setNumber(2).setName("str").setType( - Type.TYPE_STRING)) + .addField( + FieldDescriptorProto.newBuilder() + .setNumber(1) + .setName("i32") + .setType(Type.TYPE_INT32)) + .addField( + FieldDescriptorProto.newBuilder() + .setNumber(2) + .setName("str") + .setType(Type.TYPE_STRING)) + .build(); + FileDescriptorProto file = + FileDescriptorProto.newBuilder() + .setName("my_protos.proto") + .addMessageType(myMessage) .build(); - FileDescriptorProto file = FileDescriptorProto.newBuilder() - .setName("my_protos.proto") - .addMessageType(myMessage) - .build(); try { - return DynamicMessage.getDefaultInstance(FileDescriptor.buildFrom(file, new FileDescriptor[0]) - .findMessageTypeByName("my_message")); + return DynamicMessage.getDefaultInstance( + FileDescriptor.buildFrom(file, new FileDescriptor[0]) + .findMessageTypeByName("my_message")); } catch (DescriptorValidationException e) { throw new IllegalStateException(e); } diff --git a/tests/src/test/java/com/example/ForkModeFuzzer.java b/tests/src/test/java/com/example/ForkModeFuzzer.java index 9f0051248..b75d3359a 100644 --- a/tests/src/test/java/com/example/ForkModeFuzzer.java +++ b/tests/src/test/java/com/example/ForkModeFuzzer.java @@ -19,14 +19,17 @@ public final class ForkModeFuzzer { public static void fuzzerInitialize() { // When running through a Java reproducer, do not check the Java opts. - if (System.getProperty("jazzer.is_reproducer") != null) - return; + if (System.getProperty("jazzer.is_reproducer") != null) return; 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")) { + 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); diff --git a/tests/src/test/java/com/example/HookDependenciesFuzzer.java b/tests/src/test/java/com/example/HookDependenciesFuzzer.java index 7150ed6c4..251f6e82b 100644 --- a/tests/src/test/java/com/example/HookDependenciesFuzzer.java +++ b/tests/src/test/java/com/example/HookDependenciesFuzzer.java @@ -15,10 +15,6 @@ package com.example; import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; -import com.code_intelligence.jazzer.api.HookType; -import com.code_intelligence.jazzer.api.MethodHook; -import java.lang.invoke.MethodHandle; -import java.lang.reflect.Field; import java.util.regex.Pattern; // This fuzzer verifies that: diff --git a/tests/src/test/java/com/example/HookDependenciesFuzzerHooks.java b/tests/src/test/java/com/example/HookDependenciesFuzzerHooks.java index d4f50dbf5..b9d81fb86 100644 --- a/tests/src/test/java/com/example/HookDependenciesFuzzerHooks.java +++ b/tests/src/test/java/com/example/HookDependenciesFuzzerHooks.java @@ -34,11 +34,17 @@ public class HookDependenciesFuzzerHooks { PATTERN_ROOT = root; } - @MethodHook(type = HookType.AFTER, targetClassName = "java.util.regex.Matcher", - targetMethod = "matches", targetMethodDescriptor = "()Z", + @MethodHook( + type = HookType.AFTER, + targetClassName = "java.util.regex.Matcher", + targetMethod = "matches", + targetMethodDescriptor = "()Z", additionalClassesToHook = {"java.util.regex.Pattern"}) - public static void - matcherMatchesHook(MethodHandle method, Object alwaysNull, Object[] alwaysEmpty, int hookId, + public static void matcherMatchesHook( + MethodHandle method, + Object alwaysNull, + Object[] alwaysEmpty, + int hookId, Boolean returnValue) { if (PATTERN_ROOT != null) { throw new FuzzerSecurityIssueLow("Hook applied even though it depends on the class to hook"); diff --git a/tests/src/test/java/com/example/JUnitAgentConfigurationFuzzTest.java b/tests/src/test/java/com/example/JUnitAgentConfigurationFuzzTest.java index 4f8c2a197..1f2ffaaf9 100644 --- a/tests/src/test/java/com/example/JUnitAgentConfigurationFuzzTest.java +++ b/tests/src/test/java/com/example/JUnitAgentConfigurationFuzzTest.java @@ -32,9 +32,8 @@ void testConfiguration(byte[] bytes) { private static Object getLazyOptValue(String name) { try { Supplier supplier = - (Supplier) Class.forName("com.code_intelligence.jazzer.driver.Opt") - .getField(name) - .get(null); + (Supplier) + Class.forName("com.code_intelligence.jazzer.driver.Opt").getField(name).get(null); return supplier.get(); } catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) { throw new IllegalStateException(e); diff --git a/tests/src/test/java/com/example/JUnitReproducerTest.java b/tests/src/test/java/com/example/JUnitReproducerTest.java index 0e8bdb57c..419f1c4be 100644 --- a/tests/src/test/java/com/example/JUnitReproducerTest.java +++ b/tests/src/test/java/com/example/JUnitReproducerTest.java @@ -28,9 +28,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -/** - * Verifies that reproducing a single input works for @FuzzTests. - */ +/** Verifies that reproducing a single input works for @FuzzTests. */ class JUnitReproducerTest { // echo "Hello, Jazzer!" | openssl dgst -binary -sha256 | openssl base64 -A private static final byte[] TARGET_DIGEST = diff --git a/tests/src/test/java/com/example/LongStringFuzzer.java b/tests/src/test/java/com/example/LongStringFuzzer.java index 324764d41..f168be81e 100644 --- a/tests/src/test/java/com/example/LongStringFuzzer.java +++ b/tests/src/test/java/com/example/LongStringFuzzer.java @@ -19,8 +19,8 @@ /** * Provoke a finding with huge captured data to verify that the generated crash reproducer is still * compilable. This test uses a huge, predefined corpus to speed up finding the issue. - *

            - * Reproduces issue #269 (Reproduces issue #269 (...) */ public class LongStringFuzzer { diff --git a/tests/src/test/java/com/example/NativeValueProfileFuzzer.java b/tests/src/test/java/com/example/NativeValueProfileFuzzer.java index 1085a9537..b942fc1b7 100644 --- a/tests/src/test/java/com/example/NativeValueProfileFuzzer.java +++ b/tests/src/test/java/com/example/NativeValueProfileFuzzer.java @@ -27,8 +27,7 @@ public static void fuzzerInitialize() { public static void fuzzerTestOneInput(FuzzedDataProvider data) { long[] blocks = data.consumeLongs(2); - if (blocks.length != 2) - return; + if (blocks.length != 2) return; if (checkAccess(blocks[0], blocks[1])) { throw new FuzzerSecurityIssueLow("Security breached"); } diff --git a/tests/src/test/java/com/example/SilencedFuzzer.java b/tests/src/test/java/com/example/SilencedFuzzer.java index d1d8777a8..16d8ff135 100644 --- a/tests/src/test/java/com/example/SilencedFuzzer.java +++ b/tests/src/test/java/com/example/SilencedFuzzer.java @@ -21,10 +21,12 @@ import java.io.PrintStream; public class SilencedFuzzer { - private static final PrintStream noopStream = new PrintStream(new OutputStream() { - @Override - public void write(int b) {} - }); + private static final PrintStream noopStream = + new PrintStream( + new OutputStream() { + @Override + public void write(int b) {} + }); public static void fuzzerInitialize() { System.setErr(noopStream); diff --git a/tests/src/test/java/com/example/TimeoutFuzzer.java b/tests/src/test/java/com/example/TimeoutFuzzer.java index 952113bf8..13bf8c0ab 100644 --- a/tests/src/test/java/com/example/TimeoutFuzzer.java +++ b/tests/src/test/java/com/example/TimeoutFuzzer.java @@ -18,7 +18,6 @@ public class TimeoutFuzzer { public static void fuzzerTestOneInput(byte[] b) { - while (true) { - } + while (true) {} } }