@@ -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