Skip to content

Commit dd9416b

Browse files
kyakdanoetr
authored andcommitted
refactor: use CountersTracker instead of value profiling in exploreState
Replace the tracePcIndir-based value profiling approach with dedicated coverage counters via CountersTracker. This removes the dependency on -use_value_profile=1 and tracks all 256 byte values (up from 128) by mapping each to a unique (counter, bucket) pair across 32 counters.
1 parent 204e400 commit dd9416b

File tree

2 files changed

+22
-36
lines changed

2 files changed

+22
-36
lines changed

examples/BUILD.bazel

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ java_fuzz_target_test(
129129
"src/main/java/com/example/MazeFuzzer.java",
130130
],
131131
allowed_findings = ["com.example.MazeFuzzer$$TreasureFoundException"],
132-
fuzzer_args = ["-use_value_profile=1"],
133132
target_class = "com.example.MazeFuzzer",
134133
)
135134

src/main/java/com/code_intelligence/jazzer/api/Jazzer.java

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ public final class Jazzer {
3232
private static final MethodHandle TRACE_STRCMP;
3333
private static final MethodHandle TRACE_STRSTR;
3434
private static final MethodHandle TRACE_MEMCMP;
35-
private static final MethodHandle TRACE_PC_INDIR;
3635

3736
private static final MethodHandle COUNTERS_TRACKER_ALLOCATE;
3837
private static final MethodHandle COUNTERS_TRACKER_SET_RANGE;
38+
private static final MethodHandle COUNTERS_TRACKER_SET_COUNTER;
39+
40+
private static final byte[] EXPLORE_BUCKET_VALUES = {1, 2, 3, 4, 8, 16, 32, (byte) 128};
3941

4042
/**
4143
* Default number of counters allocated for each call site of a method that requires registering a
@@ -53,9 +55,9 @@ public final class Jazzer {
5355
MethodHandle traceStrcmp = null;
5456
MethodHandle traceStrstr = null;
5557
MethodHandle traceMemcmp = null;
56-
MethodHandle tracePcIndir = null;
5758
MethodHandle countersTrackerAllocate = null;
5859
MethodHandle countersTrackerSetRange = null;
60+
MethodHandle countersTrackerSetCounter = null;
5961
try {
6062
jazzerInternal = Class.forName("com.code_intelligence.jazzer.runtime.JazzerInternal");
6163
MethodType onFuzzTargetReadyType = MethodType.methodType(void.class, Runnable.class);
@@ -82,10 +84,6 @@ public final class Jazzer {
8284
traceMemcmp =
8385
MethodHandles.publicLookup()
8486
.findStatic(traceDataFlowNativeCallbacks, "traceMemcmp", traceMemcmpType);
85-
MethodType tracePcIndirType = MethodType.methodType(void.class, int.class, int.class);
86-
tracePcIndir =
87-
MethodHandles.publicLookup()
88-
.findStatic(traceDataFlowNativeCallbacks, "tracePcIndir", tracePcIndirType);
8987

9088
Class<?> countersTracker =
9189
Class.forName("com.code_intelligence.jazzer.runtime.ExtraCountersTracker");
@@ -96,6 +94,10 @@ public final class Jazzer {
9694
MethodType setRangeType = MethodType.methodType(void.class, int.class, int.class);
9795
countersTrackerSetRange =
9896
MethodHandles.publicLookup().findStatic(countersTracker, "setCounterRange", setRangeType);
97+
MethodType setCounterType =
98+
MethodType.methodType(void.class, int.class, int.class, byte.class);
99+
countersTrackerSetCounter =
100+
MethodHandles.publicLookup().findStatic(countersTracker, "setCounter", setCounterType);
99101
} catch (ClassNotFoundException ignore) {
100102
// Not running in the context of the agent. This is fine as long as no methods are called on
101103
// this class.
@@ -111,9 +113,9 @@ public final class Jazzer {
111113
TRACE_STRCMP = traceStrcmp;
112114
TRACE_STRSTR = traceStrstr;
113115
TRACE_MEMCMP = traceMemcmp;
114-
TRACE_PC_INDIR = tracePcIndir;
115116
COUNTERS_TRACKER_ALLOCATE = countersTrackerAllocate;
116117
COUNTERS_TRACKER_SET_RANGE = countersTrackerSetRange;
118+
COUNTERS_TRACKER_SET_COUNTER = countersTrackerSetCounter;
117119
}
118120

119121
private Jazzer() {}
@@ -206,48 +208,31 @@ public static void guideTowardsContainment(String haystack, String needle, int i
206208
}
207209

208210
/**
209-
* Instructs the fuzzer to attain as many possible values for the absolute value of {@code state}
210-
* as possible.
211+
* Instructs the fuzzer to attain as many possible values for {@code state} as possible.
211212
*
212213
* <p>Call this function from a fuzz target or a hook to help the fuzzer track partial progress
213214
* (e.g. by passing the length of a common prefix of two lists that should become equal) or
214215
* explore different values of state that is not directly related to code coverage (see the
215216
* MazeFuzzer example).
216217
*
217-
* <p><b>Note:</b> This hint only takes effect if the fuzzer is run with the argument {@code
218-
* -use_value_profile=1}.
218+
* <p>Each unique state value is tracked via libFuzzer's counter bucketing mechanism, enabling us
219+
* to represent 8 different states for each coverage counter. As a result, all 256 byte values are
220+
* distinguished by mapping each to a unique (counter, bucket) pair across 32 counters. See:
221+
* https://github.com/llvm/llvm-project/blob/972e73b812cb7b6dd349c7c07daae73314f29e8f/compiler-rt/lib/fuzzer/FuzzerTracePC.h#L213-L235
219222
*
220223
* @param state a numeric encoding of a state that should be varied by the fuzzer
221224
* @param id a (probabilistically) unique identifier for this particular state hint
222225
*/
223226
public static void exploreState(byte state, int id) {
224-
if (TRACE_PC_INDIR == null) {
227+
if (COUNTERS_TRACKER_ALLOCATE == null) {
225228
return;
226229
}
227-
// We only use the lower 7 bits of state, which allows for 128 different state values tracked
228-
// per id. The particular amount of 7 bits of state is also used in libFuzzer's
229-
// TracePC::HandleCmp:
230-
// https://github.com/llvm/llvm-project/blob/c12d49c4e286fa108d4d69f1c6d2b8d691993ffd/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L390
231-
// This value should be large enough for most use cases (e.g. tracking the length of a prefix in
232-
// a comparison) while being small enough that the bitmap isn't filled up too quickly
233-
// (65536 bits / 128 bits per id = 512 ids).
234-
235-
// We use tracePcIndir as a way to set a bit in libFuzzer's value profile bitmap. In
236-
// TracePC::HandleCallerCallee, which is what this function ultimately calls through to, the
237-
// lower 12 bits of each argument are combined into a 24-bit index into the bitmap, which is
238-
// then reduced modulo a 16-bit prime. To keep the modulo bias small, we should fill as many
239-
// of the relevant bits as possible.
240-
241-
// We pass state in the lowest bits of the caller address, which is used to form the lowest bits
242-
// of the bitmap index. This should result in the best caching behavior as state is expected to
243-
// change quickly in consecutive runs and in this way all its bitmap entries would be located
244-
// close to each other in memory.
245-
int lowerBits = (state & 0x7f) | (id << 7);
246-
int upperBits = id >>> 5;
247230
try {
248-
TRACE_PC_INDIR.invokeExact(upperBits, lowerBits);
249-
} catch (JazzerApiException e) {
250-
throw e;
231+
COUNTERS_TRACKER_ALLOCATE.invokeExact(id, 32);
232+
int unsignedState = state & 0xff;
233+
int counterIndex = unsignedState >> 3;
234+
byte counterValue = EXPLORE_BUCKET_VALUES[unsignedState & 0x7];
235+
COUNTERS_TRACKER_SET_COUNTER.invokeExact(id, counterIndex, counterValue);
251236
} catch (Throwable e) {
252237
throw new JazzerApiException("exploreState: " + e.getMessage(), e);
253238
}
@@ -258,6 +243,8 @@ public static void exploreState(byte state, int id) {
258243
* generated call-site identifiers. During instrumentation, calls to this method are replaced with
259244
* calls to {@link #exploreState(byte, int)} using a unique id for each call site.
260245
*
246+
* <p>Without instrumentation, this is a no-op.
247+
*
261248
* @param state a numeric encoding of a state that should be varied by the fuzzer
262249
* @see #exploreState(byte, int)
263250
*/

0 commit comments

Comments
 (0)