Skip to content

Commit 2b9e788

Browse files
committed
feat: add JazzerApiException for API error handling
Introduce JazzerApiException to distinguish API misuse errors (invalid arguments, incompatible runtime) from findings in the code under test. The fuzzing engine treats this exception as a fatal error rather than a reportable bug.
1 parent f932854 commit 2b9e788

File tree

5 files changed

+63
-0
lines changed

5 files changed

+63
-0
lines changed

src/main/java/com/code_intelligence/jazzer/api/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ java_library(
4646
"FuzzerSecurityIssueMedium.java",
4747
"HookType.java",
4848
"Jazzer.java",
49+
"JazzerApiException.java",
4950
"MethodHook.java",
5051
"MethodHooks.java",
5152
"//src/main/java/jaz",
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2026 Code Intelligence GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.code_intelligence.jazzer.api;
18+
19+
/**
20+
* Signals error from the Jazzer API (e.g. invalid arguments to {@link Jazzer#maximize}).
21+
*
22+
* <p>This exception is treated as a fatal error by the fuzzing engine rather than as a finding in
23+
* the code under test. When thrown during fuzzing, it stops the current fuzz test with an error
24+
* instead of reporting a bug in the fuzz target.
25+
*/
26+
public final class JazzerApiException extends RuntimeException {
27+
public JazzerApiException(String message) {
28+
super(message);
29+
}
30+
31+
public JazzerApiException(String message, Throwable cause) {
32+
super(message, cause);
33+
}
34+
35+
public JazzerApiException(Throwable cause) {
36+
super(cause);
37+
}
38+
}

src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetRunner.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.code_intelligence.jazzer.driver;
1818

19+
import static com.code_intelligence.jazzer.driver.Constants.JAZZER_ERROR_EXIT_CODE;
1920
import static com.code_intelligence.jazzer.driver.Constants.JAZZER_FINDING_EXIT_CODE;
2021
import static com.code_intelligence.jazzer.runtime.Constants.IS_ANDROID;
2122
import static java.lang.System.exit;
@@ -91,6 +92,8 @@ public final class FuzzTargetRunner {
9192

9293
private static final String OPENTEST4J_TEST_ABORTED_EXCEPTION =
9394
"org.opentest4j.TestAbortedException";
95+
private static final String JAZZER_API_EXCEPTION =
96+
"com.code_intelligence.jazzer.api.JazzerApiException";
9497

9598
private static final Unsafe UNSAFE = UnsafeProvider.getUnsafe();
9699

@@ -271,6 +274,16 @@ private static int runOne(long dataPtr, int dataLength) {
271274
finding = JazzerInternal.lastFinding;
272275
JazzerInternal.lastFinding = null;
273276
}
277+
// JazzerApiException signals API error, not a finding in the code under test.
278+
if (finding != null && finding.getClass().getName().equals(JAZZER_API_EXCEPTION)) {
279+
Log.error("Jazzer API error", finding);
280+
temporarilyDisableLibfuzzerExitHook();
281+
if (fatalFindingHandlerForJUnit != null) {
282+
fatalFindingHandlerForJUnit.accept(finding);
283+
return LIBFUZZER_RETURN_FROM_DRIVER;
284+
}
285+
exit(JAZZER_ERROR_EXIT_CODE);
286+
}
274287
// Allow skipping invalid inputs in fuzz tests by using e.g. JUnit's assumeTrue.
275288
if (finding == null || finding.getClass().getName().equals(OPENTEST4J_TEST_ABORTED_EXCEPTION)) {
276289
return LIBFUZZER_CONTINUE;

src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExecutor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import org.junit.platform.commons.support.AnnotationSupport;
5353

5454
class FuzzTestExecutor {
55+
private static final String JAZZER_API_EXCEPTION =
56+
"com.code_intelligence.jazzer.api.JazzerApiException";
5557
private static final AtomicBoolean hasBeenPrepared = new AtomicBoolean();
5658
private static final AtomicBoolean agentInstalled = new AtomicBoolean(false);
5759

@@ -334,6 +336,9 @@ public Optional<Throwable> execute(
334336
Throwable finding = atomicFinding.get();
335337

336338
if (finding != null) {
339+
if (finding.getClass().getName().equals(JAZZER_API_EXCEPTION)) {
340+
return Optional.of(finding);
341+
}
337342
return Optional.of(new FuzzTestFindingException(finding));
338343
} else if (exitCode != 0) {
339344
return Optional.of(

src/main/java/com/code_intelligence/jazzer/junit/FuzzTestExtensions.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class FuzzTestExtensions
4242
implements ExecutionCondition, InvocationInterceptor, TestExecutionExceptionHandler {
4343
private static final String JAZZER_INTERNAL =
4444
"com.code_intelligence.jazzer.runtime.JazzerInternal";
45+
private static final String JAZZER_API_EXCEPTION =
46+
"com.code_intelligence.jazzer.api.JazzerApiException";
4547
private static final AtomicReference<Method> fuzzTestMethod = new AtomicReference<>();
4648
private static Field lastFindingField;
4749
private static Field hooksEnabledField;
@@ -114,6 +116,10 @@ private static void runWithHooks(Invocation<Void> invocation) throws Throwable {
114116
} catch (Throwable t) {
115117
thrown = t;
116118
}
119+
// JazzerApiException signals API error, so propagate as is and not as a finding.
120+
if (thrown != null && thrown.getClass().getName().equals(JAZZER_API_EXCEPTION)) {
121+
throw thrown;
122+
}
117123
Throwable stored = (Throwable) getLastFindingField().get(null);
118124
if (stored != null) {
119125
throw new FuzzTestFindingException(stored);

0 commit comments

Comments
 (0)