diff options
Diffstat (limited to 'agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java')
-rw-r--r-- | agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java | 246 |
1 files changed, 195 insertions, 51 deletions
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java index e45f7600..97adf578 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java +++ b/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java @@ -18,32 +18,69 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; +import java.security.SecureRandom; /** * Helper class with static methods that interact with Jazzer at runtime. */ final public class Jazzer { - private static Class<?> jazzerInternal = null; - - private static MethodHandle traceStrcmp = null; - private static MethodHandle traceStrstr = null; - private static MethodHandle traceMemcmp = null; - - private static MethodHandle consume = null; - private static MethodHandle autofuzzFunction1 = null; - private static MethodHandle autofuzzFunction2 = null; - private static MethodHandle autofuzzFunction3 = null; - private static MethodHandle autofuzzFunction4 = null; - private static MethodHandle autofuzzFunction5 = null; - private static MethodHandle autofuzzConsumer1 = null; - private static MethodHandle autofuzzConsumer2 = null; - private static MethodHandle autofuzzConsumer3 = null; - private static MethodHandle autofuzzConsumer4 = null; - private static MethodHandle autofuzzConsumer5 = null; + /** + * A 32-bit random number that hooks can use to make pseudo-random choices + * between multiple possible mutations they could guide the fuzzer towards. + * Hooks <b>must not</b> base the decision whether or not to report a finding + * on this number as this will make findings non-reproducible. + * <p> + * This is the same number that libFuzzer uses as a seed internally, which + * makes it possible to deterministically reproduce a previous fuzzing run by + * supplying the seed value printed by libFuzzer as the value of the + * {@code -seed}. + */ + public static final int SEED = getLibFuzzerSeed(); + + private static final Class<?> JAZZER_INTERNAL; + + private static final MethodHandle ON_FUZZ_TARGET_READY; + + private static final MethodHandle TRACE_STRCMP; + private static final MethodHandle TRACE_STRSTR; + private static final MethodHandle TRACE_MEMCMP; + private static final MethodHandle TRACE_PC_INDIR; + + private static final MethodHandle CONSUME; + private static final MethodHandle AUTOFUZZ_FUNCTION_1; + private static final MethodHandle AUTOFUZZ_FUNCTION_2; + private static final MethodHandle AUTOFUZZ_FUNCTION_3; + private static final MethodHandle AUTOFUZZ_FUNCTION_4; + private static final MethodHandle AUTOFUZZ_FUNCTION_5; + private static final MethodHandle AUTOFUZZ_CONSUMER_1; + private static final MethodHandle AUTOFUZZ_CONSUMER_2; + private static final MethodHandle AUTOFUZZ_CONSUMER_3; + private static final MethodHandle AUTOFUZZ_CONSUMER_4; + private static final MethodHandle AUTOFUZZ_CONSUMER_5; static { + Class<?> jazzerInternal = null; + MethodHandle onFuzzTargetReady = null; + MethodHandle traceStrcmp = null; + MethodHandle traceStrstr = null; + MethodHandle traceMemcmp = null; + MethodHandle tracePcIndir = null; + MethodHandle consume = null; + MethodHandle autofuzzFunction1 = null; + MethodHandle autofuzzFunction2 = null; + MethodHandle autofuzzFunction3 = null; + MethodHandle autofuzzFunction4 = null; + MethodHandle autofuzzFunction5 = null; + MethodHandle autofuzzConsumer1 = null; + MethodHandle autofuzzConsumer2 = null; + MethodHandle autofuzzConsumer3 = null; + MethodHandle autofuzzConsumer4 = null; + MethodHandle autofuzzConsumer5 = null; try { jazzerInternal = Class.forName("com.code_intelligence.jazzer.runtime.JazzerInternal"); + MethodType onFuzzTargetReadyType = MethodType.methodType(void.class, Runnable.class); + onFuzzTargetReady = MethodHandles.publicLookup().findStatic( + jazzerInternal, "registerOnFuzzTargetReadyCallback", onFuzzTargetReadyType); Class<?> traceDataFlowNativeCallbacks = Class.forName("com.code_intelligence.jazzer.runtime.TraceDataFlowNativeCallbacks"); @@ -60,6 +97,9 @@ final public class Jazzer { MethodType.methodType(void.class, byte[].class, byte[].class, int.class, int.class); traceMemcmp = MethodHandles.publicLookup().findStatic( traceDataFlowNativeCallbacks, "traceMemcmp", traceMemcmpType); + MethodType tracePcIndirType = MethodType.methodType(void.class, int.class, int.class); + tracePcIndir = MethodHandles.publicLookup().findStatic( + traceDataFlowNativeCallbacks, "tracePcIndir", tracePcIndirType); Class<?> metaClass = Class.forName("com.code_intelligence.jazzer.autofuzz.Meta"); MethodType consumeType = @@ -96,6 +136,23 @@ final public class Jazzer { e.printStackTrace(); System.exit(1); } + JAZZER_INTERNAL = jazzerInternal; + ON_FUZZ_TARGET_READY = onFuzzTargetReady; + TRACE_STRCMP = traceStrcmp; + TRACE_STRSTR = traceStrstr; + TRACE_MEMCMP = traceMemcmp; + TRACE_PC_INDIR = tracePcIndir; + CONSUME = consume; + AUTOFUZZ_FUNCTION_1 = autofuzzFunction1; + AUTOFUZZ_FUNCTION_2 = autofuzzFunction2; + AUTOFUZZ_FUNCTION_3 = autofuzzFunction3; + AUTOFUZZ_FUNCTION_4 = autofuzzFunction4; + AUTOFUZZ_FUNCTION_5 = autofuzzFunction5; + AUTOFUZZ_CONSUMER_1 = autofuzzConsumer1; + AUTOFUZZ_CONSUMER_2 = autofuzzConsumer2; + AUTOFUZZ_CONSUMER_3 = autofuzzConsumer3; + AUTOFUZZ_CONSUMER_4 = autofuzzConsumer4; + AUTOFUZZ_CONSUMER_5 = autofuzzConsumer5; } private Jazzer() {} @@ -103,7 +160,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -120,7 +177,7 @@ final public class Jazzer { @SuppressWarnings("unchecked") public static <T1, R> R autofuzz(FuzzedDataProvider data, Function1<T1, R> func) { try { - return (R) autofuzzFunction1.invoke(data, func); + return (R) AUTOFUZZ_FUNCTION_1.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -133,7 +190,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -150,7 +207,7 @@ final public class Jazzer { @SuppressWarnings("unchecked") public static <T1, T2, R> R autofuzz(FuzzedDataProvider data, Function2<T1, T2, R> func) { try { - return (R) autofuzzFunction2.invoke(data, func); + return (R) AUTOFUZZ_FUNCTION_2.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -163,7 +220,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -180,7 +237,7 @@ final public class Jazzer { @SuppressWarnings("unchecked") public static <T1, T2, T3, R> R autofuzz(FuzzedDataProvider data, Function3<T1, T2, T3, R> func) { try { - return (R) autofuzzFunction3.invoke(data, func); + return (R) AUTOFUZZ_FUNCTION_3.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -193,7 +250,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -211,7 +268,7 @@ final public class Jazzer { public static <T1, T2, T3, T4, R> R autofuzz( FuzzedDataProvider data, Function4<T1, T2, T3, T4, R> func) { try { - return (R) autofuzzFunction4.invoke(data, func); + return (R) AUTOFUZZ_FUNCTION_4.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -224,7 +281,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -242,7 +299,7 @@ final public class Jazzer { public static <T1, T2, T3, T4, T5, R> R autofuzz( FuzzedDataProvider data, Function5<T1, T2, T3, T4, T5, R> func) { try { - return (R) autofuzzFunction5.invoke(data, func); + return (R) AUTOFUZZ_FUNCTION_5.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -255,7 +312,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -269,7 +326,7 @@ final public class Jazzer { */ public static <T1> void autofuzz(FuzzedDataProvider data, Consumer1<T1> func) { try { - autofuzzConsumer1.invoke(data, func); + AUTOFUZZ_CONSUMER_1.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -280,7 +337,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -294,7 +351,7 @@ final public class Jazzer { */ public static <T1, T2> void autofuzz(FuzzedDataProvider data, Consumer2<T1, T2> func) { try { - autofuzzConsumer2.invoke(data, func); + AUTOFUZZ_CONSUMER_2.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -305,7 +362,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -319,7 +376,7 @@ final public class Jazzer { */ public static <T1, T2, T3> void autofuzz(FuzzedDataProvider data, Consumer3<T1, T2, T3> func) { try { - autofuzzConsumer3.invoke(data, func); + AUTOFUZZ_CONSUMER_3.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -330,7 +387,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -345,7 +402,7 @@ final public class Jazzer { public static <T1, T2, T3, T4> void autofuzz( FuzzedDataProvider data, Consumer4<T1, T2, T3, T4> func) { try { - autofuzzConsumer4.invoke(data, func); + AUTOFUZZ_CONSUMER_4.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -356,7 +413,7 @@ final public class Jazzer { /** * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input * using only public methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in * meaningful ways for a number of reasons. * @@ -371,7 +428,7 @@ final public class Jazzer { public static <T1, T2, T3, T4, T5> void autofuzz( FuzzedDataProvider data, Consumer5<T1, T2, T3, T4, T5> func) { try { - autofuzzConsumer5.invoke(data, func); + AUTOFUZZ_CONSUMER_5.invoke(data, func); } catch (AutofuzzInvocationException e) { rethrowUnchecked(e.getCause()); } catch (Throwable t) { @@ -382,7 +439,7 @@ final public class Jazzer { /** * Attempts to construct an instance of {@code type} from the fuzzer input using only public * methods available on the classpath. - * + * <p> * <b>Note:</b> This function is inherently heuristic and may fail to return meaningful values for * a variety of reasons. * @@ -394,7 +451,7 @@ final public class Jazzer { @SuppressWarnings("unchecked") public static <T> T consume(FuzzedDataProvider data, Class<T> type) { try { - return (T) consume.invokeExact(data, type); + return (T) CONSUME.invokeExact(data, type); } catch (AutofuzzConstructionException ignored) { return null; } catch (Throwable t) { @@ -407,7 +464,7 @@ final public class Jazzer { /** * Instructs the fuzzer to guide its mutations towards making {@code current} equal to {@code * target}. - * + * <p> * If the relation between the raw fuzzer input and the value of {@code current} is relatively * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to * achieve equality. @@ -417,8 +474,11 @@ final public class Jazzer { * @param id a (probabilistically) unique identifier for this particular compare hint */ public static void guideTowardsEquality(String current, String target, int id) { + if (TRACE_STRCMP == null) { + return; + } try { - traceStrcmp.invokeExact(current, target, 1, id); + TRACE_STRCMP.invokeExact(current, target, 1, id); } catch (Throwable e) { e.printStackTrace(); } @@ -427,7 +487,7 @@ final public class Jazzer { /** * Instructs the fuzzer to guide its mutations towards making {@code current} equal to {@code * target}. - * + * <p> * If the relation between the raw fuzzer input and the value of {@code current} is relatively * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to * achieve equality. @@ -437,8 +497,11 @@ final public class Jazzer { * @param id a (probabilistically) unique identifier for this particular compare hint */ public static void guideTowardsEquality(byte[] current, byte[] target, int id) { + if (TRACE_MEMCMP == null) { + return; + } try { - traceMemcmp.invokeExact(current, target, 1, id); + TRACE_MEMCMP.invokeExact(current, target, 1, id); } catch (Throwable e) { e.printStackTrace(); } @@ -447,7 +510,7 @@ final public class Jazzer { /** * Instructs the fuzzer to guide its mutations towards making {@code haystack} contain {@code * needle} as a substring. - * + * <p> * If the relation between the raw fuzzer input and the value of {@code haystack} is relatively * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to * satisfy the substring check. @@ -458,28 +521,81 @@ final public class Jazzer { * @param id a (probabilistically) unique identifier for this particular compare hint */ public static void guideTowardsContainment(String haystack, String needle, int id) { + if (TRACE_STRSTR == null) { + return; + } try { - traceStrstr.invokeExact(haystack, needle, id); + TRACE_STRSTR.invokeExact(haystack, needle, id); } catch (Throwable e) { e.printStackTrace(); } } /** - * Make Jazzer report the provided {@link Throwable} as a finding. + * Instructs the fuzzer to attain as many possible values for the absolute value of {@code state} + * as possible. + * <p> + * Call this function from a fuzz target or a hook to help the fuzzer track partial progress + * (e.g. by passing the length of a common prefix of two lists that should become equal) or + * explore different values of state that is not directly related to code coverage (see the + * MazeFuzzer example). + * <p> + * <b>Note:</b> This hint only takes effect if the fuzzer is run with the argument + * {@code -use_value_profile=1}. * + * @param state a numeric encoding of a state that should be varied by the fuzzer + * @param id a (probabilistically) unique identifier for this particular state hint + */ + public static void exploreState(byte state, int id) { + if (TRACE_PC_INDIR == null) { + return; + } + // We only use the lower 7 bits of state, which allows for 128 different state values tracked + // per id. The particular amount of 7 bits of state is also used in libFuzzer's + // TracePC::HandleCmp: + // https://github.com/llvm/llvm-project/blob/c12d49c4e286fa108d4d69f1c6d2b8d691993ffd/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L390 + // This value should be large enough for most use cases (e.g. tracking the length of a prefix in + // a comparison) while being small enough that the bitmap isn't filled up too quickly + // (65536 bits/ 128 bits per id = 512 ids). + + // We use tracePcIndir as a way to set a bit in libFuzzer's value profile bitmap. In + // TracePC::HandleCallerCallee, which is what this function ultimately calls through to, the + // lower 12 bits of each argument are combined into a 24-bit index into the bitmap, which is + // then reduced modulo a 16-bit prime. To keep the modulo bias small, we should fill as many + // of the relevant bits as possible. However, there are the following restrictions: + // 1. Since we use the return address trampoline to set the caller address indirectly, its + // upper 3 bits are fixed, which leaves a total of 21 variable bits on x86_64. + // 2. On arm64 macOS, where every instruction is aligned to 4 bytes, the lower 2 bits of the + // caller address will always be zero, further reducing the number of variable bits in the + // caller parameter to 7. + // https://github.com/llvm/llvm-project/blob/c12d49c4e286fa108d4d69f1c6d2b8d691993ffd/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L121 + // Even taking these restrictions into consideration, we pass state in the lowest bits of the + // caller address, which is used to form the lowest bits of the bitmap index. This should result + // in the best caching behavior as state is expected to change quickly in consecutive runs and + // in this way all its bitmap entries would be located close to each other in memory. + int lowerBits = (state & 0x7f) | (id << 7); + int upperBits = id >>> 5; + try { + TRACE_PC_INDIR.invokeExact(upperBits, lowerBits); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + /** + * Make Jazzer report the provided {@link Throwable} as a finding. + * <p> * <b>Note:</b> This method must only be called from a method hook. In a * fuzz target, simply throw an exception to trigger a finding. * @param finding the finding that Jazzer should report */ public static void reportFindingFromHook(Throwable finding) { try { - jazzerInternal.getMethod("reportFindingFromHook", Throwable.class).invoke(null, finding); + JAZZER_INTERNAL.getMethod("reportFindingFromHook", Throwable.class).invoke(null, finding); } catch (NullPointerException | IllegalAccessException | NoSuchMethodException e) { - // We can only reach this point if the runtime is not in the classpath, but it must be if - // hooks work and this function should only be called from them. - System.err.println("ERROR: Jazzer.reportFindingFromHook must be called from a method hook"); - System.exit(1); + // We can only reach this point if the runtime is not on the classpath, e.g. in case of a + // reproducer. Just throw the finding. + rethrowUnchecked(finding); } catch (InvocationTargetException e) { // reportFindingFromHook throws a HardToCatchThrowable, which will bubble up wrapped in an // InvocationTargetException that should not be stopped here. @@ -491,6 +607,34 @@ final public class Jazzer { } } + /** + * Register a callback to be executed right before the fuzz target is executed for the first time. + * <p> + * This can be used to disable hooks until after Jazzer has been fully initializing, e.g. to + * prevent Jazzer internals from triggering hooks on Java standard library classes. + * + * @param callback the callback to execute + */ + public static void onFuzzTargetReady(Runnable callback) { + try { + ON_FUZZ_TARGET_READY.invokeExact(callback); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private static int getLibFuzzerSeed() { + // The Jazzer driver sets this property based on the value of libFuzzer's -seed command-line + // option, which allows for fully reproducible fuzzing runs if set. If not running in the + // context of the driver, fall back to a random number instead. + String rawSeed = System.getProperty("jazzer.seed"); + if (rawSeed == null) { + return new SecureRandom().nextInt(); + } + // If jazzer.seed is set, we expect it to be a valid integer. + return Integer.parseUnsignedInt(rawSeed); + } + // Rethrows a (possibly checked) exception while avoiding a throws declaration. @SuppressWarnings("unchecked") private static <T extends Throwable> void rethrowUnchecked(Throwable t) throws T { |