From 2f34a93ae27d367a1475b256de93e7a0247f0e24 Mon Sep 17 00:00:00 2001 From: Khaled Yakdan Date: Mon, 19 Jan 2026 13:54:00 +0100 Subject: [PATCH] feat: add exploreState(byte) overload with auto-generated call-site id Add a convenience overload of Jazzer.exploreState that doesn't require manually providing an id parameter. During instrumentation, calls to this method are replaced with calls to exploreState(byte, int) using automatically generated unique identifiers for each call site. This simplifies the API for users who don't need to manually control the id parameter, while still ensuring each call site is tracked separately. Changes: - Add exploreState(byte) method to Jazzer API - Add JazzerApiHooks with REPLACE hook for the new method - Register JazzerApiHooks in Agent.kt - Update MazeFuzzer example to use the new overload --- .../src/main/java/com/example/MazeFuzzer.java | 2 +- .../code_intelligence/jazzer/agent/Agent.kt | 2 +- .../code_intelligence/jazzer/api/Jazzer.java | 13 ++++++ .../jazzer/runtime/BUILD.bazel | 1 + .../jazzer/runtime/JazzerApiHooks.java | 46 +++++++++++++++++++ 5 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/code_intelligence/jazzer/runtime/JazzerApiHooks.java diff --git a/examples/src/main/java/com/example/MazeFuzzer.java b/examples/src/main/java/com/example/MazeFuzzer.java index c92185aac..9d42a259b 100644 --- a/examples/src/main/java/com/example/MazeFuzzer.java +++ b/examples/src/main/java/com/example/MazeFuzzer.java @@ -67,7 +67,7 @@ public static void fuzzerTestOneInput(byte[] commands) { // 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); + Jazzer.exploreState((byte) Objects.hash(x, y)); if (REACHED_FIELDS[y][x] == ' ') { // Fuzzer reached a new field in the maze, print its progress. REACHED_FIELDS[y][x] = '.'; diff --git a/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt b/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt index 2463ff1db..4ff0fb1ad 100644 --- a/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt +++ b/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt @@ -111,7 +111,7 @@ fun installInternal( InstrumentationType.NATIVE -> "com.code_intelligence.jazzer.runtime.NativeLibHooks" else -> null } - } + } + listOf("com.code_intelligence.jazzer.runtime.JazzerApiHooks") val coverageIdSynchronizer = if (idSyncFilePath != null) { FileSyncCoverageIdStrategy(idSyncFilePath) 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 3809479ec..2b882a269 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java +++ b/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java @@ -217,6 +217,19 @@ public static void exploreState(byte state, int id) { } } + /** + * Convenience overload of {@link #exploreState(byte, int)} that allows using automatically + * generated call-site identifiers. During instrumentation, calls to this method are replaced with + * calls to {@link #exploreState(byte, int)} using a unique id for each call site. + * + * @param state a numeric encoding of a state that should be varied by the fuzzer + * @see #exploreState(byte, int) + */ + public static void exploreState(byte state) { + // Instrumentation replaces calls to this method with calls to exploreState(byte, int) using + // an automatically generated call-site id. Without instrumentation, this is a no-op. + } + /** * Make Jazzer report the provided {@link Throwable} as a finding. * diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel index 9532a0e95..2b5f2d0ef 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel +++ b/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel @@ -149,6 +149,7 @@ java_library( name = "runtime", srcs = [ "HardToCatchError.java", + "JazzerApiHooks.java", "JazzerInternal.java", "NativeLibHooks.java", "TraceCmpHooks.java", diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/JazzerApiHooks.java b/src/main/java/com/code_intelligence/jazzer/runtime/JazzerApiHooks.java new file mode 100644 index 000000000..d1d9c1c01 --- /dev/null +++ b/src/main/java/com/code_intelligence/jazzer/runtime/JazzerApiHooks.java @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.code_intelligence.jazzer.runtime; + +import com.code_intelligence.jazzer.api.HookType; +import com.code_intelligence.jazzer.api.Jazzer; +import com.code_intelligence.jazzer.api.MethodHook; +import java.lang.invoke.MethodHandle; + +/** + * Hooks for the Jazzer API that add call-site specific identifiers to methods that don't require an + * explicit id parameter. + */ +@SuppressWarnings("unused") +public final class JazzerApiHooks { + /** + * Replaces calls to {@link Jazzer#exploreState(byte)} with calls to {@link + * Jazzer#exploreState(byte, int)} using the hook id as the id parameter. + * + *

This allows each call site to be tracked separately without requiring the user to manually + * provide a unique id. + */ + @MethodHook( + type = HookType.REPLACE, + targetClassName = "com.code_intelligence.jazzer.api.Jazzer", + targetMethod = "exploreState", + targetMethodDescriptor = "(B)V") + public static void exploreStateWithId( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + Jazzer.exploreState((byte) arguments[0], hookId); + } +}