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 0660928e4..db25a9956 100644 --- a/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetRunner.java +++ b/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetRunner.java @@ -50,7 +50,7 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; -import java.util.function.BiConsumer; +import java.util.function.BiPredicate; import java.util.stream.Stream; import sun.misc.Unsafe; @@ -119,7 +119,7 @@ public final class FuzzTargetRunner { private static final boolean useFuzzedDataProvider; private static final ArgumentsMutator mutator; private static final ReproducerTemplate reproducerTemplate; - private static BiConsumer fatalFindingHandlerForJUnit; + private static BiPredicate fatalFindingDeterminatorForJUnit; static { FuzzTargetHolder.FuzzTarget fuzzTarget = FuzzTargetHolder.fuzzTarget; @@ -293,15 +293,16 @@ private static int runOne(long dataPtr, int dataLength) { emitDedupToken && (keepGoing == 0 || Long.compareUnsigned(ignoredTokens.size(), keepGoing) < 0); boolean isFuzzingFromCommandLine = - fatalFindingHandlerForJUnit == null || Opt.isJUnitAndCommandLine.get(); + fatalFindingDeterminatorForJUnit == null || Opt.isJUnitAndCommandLine.get(); // In case of --keep_going, only the last finding is reported to JUnit as a Java object, all // previous ones are merely printed. When fuzzing from the command line, we always print all // findings. if (isFuzzingFromCommandLine || continueFuzzing) { Log.finding(finding); } - if (fatalFindingHandlerForJUnit != null && !continueFuzzing) { - fatalFindingHandlerForJUnit.accept(data, finding); + if (fatalFindingDeterminatorForJUnit != null) { + boolean isFatal = fatalFindingDeterminatorForJUnit.test(data, finding); + continueFuzzing = continueFuzzing && !isFatal; } if (emitDedupToken) { // Has to be printed to stdout as it is parsed by libFuzzer when minimizing a crash. It does @@ -322,7 +323,7 @@ private static int runOne(long dataPtr, int dataLength) { // that satisfies the same purpose. // It also doesn't support the mutator framework yet as that requires implementing Java code // generation for mutators. - if (fatalFindingHandlerForJUnit == null && !useMutatorFramework) { + if (fatalFindingDeterminatorForJUnit == null && !useMutatorFramework) { dumpReproducer(data); } @@ -342,7 +343,7 @@ private static int runOne(long dataPtr, int dataLength) { Opt.autofuzzIgnore.get().stream(), Stream.of(finding.getClass().getName())) .collect(joining(",")))); } - if (fatalFindingHandlerForJUnit == null) { + if (fatalFindingDeterminatorForJUnit == null) { // When running a legacy fuzzerTestOneInput test, exit now with the correct exit code. // This will trigger the shutdown hook that runs fuzzerTearDown. System.exit(JAZZER_FINDING_EXIT_CODE); @@ -442,9 +443,9 @@ public static int startLibFuzzer(List args) { args.stream().map(str -> str.getBytes(StandardCharsets.UTF_8)).toArray(byte[][]::new)); } - public static void registerFatalFindingHandlerForJUnit( - BiConsumer findingHandler) { - FuzzTargetRunner.fatalFindingHandlerForJUnit = Objects.requireNonNull(findingHandler); + public static void registerFatalFindingDeterminatorForJUnit( + BiPredicate findingHandler) { + FuzzTargetRunner.fatalFindingDeterminatorForJUnit = Objects.requireNonNull(findingHandler); } private static void shutdown() { 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 422b31079..1491f2907 100644 --- a/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExecutor.java +++ b/src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExecutor.java @@ -42,8 +42,9 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; +import java.util.function.BiPredicate; import java.util.function.Supplier; import java.util.stream.Stream; import org.junit.jupiter.api.Timeout; @@ -318,11 +319,13 @@ public Optional execute( AtomicReference atomicFinding = new AtomicReference<>(); try { // Non-fatal findings (with --keep_going) are logged by FuzzTargetRunner. - BiConsumer consumer = + AtomicInteger counter = new AtomicInteger(0); + BiPredicate predicate = (a, b) -> { - atomicFinding.set(b); + if (counter.incrementAndGet() == Opt.keepGoing.get()) atomicFinding.set(b); + return Opt.keepGoing.get() != 0 && counter.get() >= Opt.keepGoing.get(); }; - FuzzTargetRunner.registerFatalFindingHandlerForJUnit(consumer); + FuzzTargetRunner.registerFatalFindingDeterminatorForJUnit(predicate); } catch (Throwable throwable) { // Exception during initialization of FuzzTargetRunner, e.g. unsupported // parameter type in fuzz target method. Rethrow as FuzzTestConfigurationError.