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); + } +}