xref: /aosp_15_r20/external/jazzer-api/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java (revision 33edd6723662ea34453766bfdca85dbfdd5342b8)
1 // Copyright 2021 Code Intelligence GmbH
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.code_intelligence.jazzer.api;
16 
17 import java.lang.invoke.MethodHandle;
18 import java.lang.invoke.MethodHandles;
19 import java.lang.invoke.MethodType;
20 import java.lang.reflect.InvocationTargetException;
21 import java.security.SecureRandom;
22 
23 /**
24  * Static helper methods that hooks can use to provide feedback to the fuzzer.
25  */
26 public final class Jazzer {
27   private static final Class<?> JAZZER_INTERNAL;
28 
29   private static final MethodHandle ON_FUZZ_TARGET_READY;
30 
31   private static final MethodHandle TRACE_STRCMP;
32   private static final MethodHandle TRACE_STRSTR;
33   private static final MethodHandle TRACE_MEMCMP;
34   private static final MethodHandle TRACE_PC_INDIR;
35 
36   static {
37     Class<?> jazzerInternal = null;
38     MethodHandle onFuzzTargetReady = null;
39     MethodHandle traceStrcmp = null;
40     MethodHandle traceStrstr = null;
41     MethodHandle traceMemcmp = null;
42     MethodHandle tracePcIndir = null;
43     try {
44       jazzerInternal = Class.forName("com.code_intelligence.jazzer.runtime.JazzerInternal");
45       MethodType onFuzzTargetReadyType = MethodType.methodType(void.class, Runnable.class);
46       onFuzzTargetReady = MethodHandles.publicLookup().findStatic(
47           jazzerInternal, "registerOnFuzzTargetReadyCallback", onFuzzTargetReadyType);
48       Class<?> traceDataFlowNativeCallbacks =
49           Class.forName("com.code_intelligence.jazzer.runtime.TraceDataFlowNativeCallbacks");
50 
51       // Use method handles for hints as the calls are potentially performance critical.
52       MethodType traceStrcmpType =
53           MethodType.methodType(void.class, String.class, String.class, int.class, int.class);
54       traceStrcmp = MethodHandles.publicLookup().findStatic(
55           traceDataFlowNativeCallbacks, "traceStrcmp", traceStrcmpType);
56       MethodType traceStrstrType =
57           MethodType.methodType(void.class, String.class, String.class, int.class);
58       traceStrstr = MethodHandles.publicLookup().findStatic(
59           traceDataFlowNativeCallbacks, "traceStrstr", traceStrstrType);
60       MethodType traceMemcmpType =
61           MethodType.methodType(void.class, byte[].class, byte[].class, int.class, int.class);
62       traceMemcmp = MethodHandles.publicLookup().findStatic(
63           traceDataFlowNativeCallbacks, "traceMemcmp", traceMemcmpType);
64       MethodType tracePcIndirType = MethodType.methodType(void.class, int.class, int.class);
65       tracePcIndir = MethodHandles.publicLookup().findStatic(
66           traceDataFlowNativeCallbacks, "tracePcIndir", tracePcIndirType);
67     } catch (ClassNotFoundException ignore) {
68       // Not running in the context of the agent. This is fine as long as no methods are called on
69       // this class.
70     } catch (NoSuchMethodException | IllegalAccessException e) {
71       // This should never happen as the Jazzer API is loaded from the agent and thus should always
72       // match the version of the runtime classes.
73       System.err.println("ERROR: Incompatible version of the Jazzer API detected, please update.");
74       e.printStackTrace();
75       System.exit(1);
76     }
77     JAZZER_INTERNAL = jazzerInternal;
78     ON_FUZZ_TARGET_READY = onFuzzTargetReady;
79     TRACE_STRCMP = traceStrcmp;
80     TRACE_STRSTR = traceStrstr;
81     TRACE_MEMCMP = traceMemcmp;
82     TRACE_PC_INDIR = tracePcIndir;
83   }
84 
Jazzer()85   private Jazzer() {}
86 
87   /**
88    * A 32-bit random number that hooks can use to make pseudo-random choices
89    * between multiple possible mutations they could guide the fuzzer towards.
90    * Hooks <b>must not</b> base the decision whether or not to report a finding
91    * on this number as this will make findings non-reproducible.
92    * <p>
93    * This is the same number that libFuzzer uses as a seed internally, which
94    * makes it possible to deterministically reproduce a previous fuzzing run by
95    * supplying the seed value printed by libFuzzer as the value of the
96    * {@code -seed}.
97    */
98   public static final int SEED = getLibFuzzerSeed();
99 
100   /**
101    * Instructs the fuzzer to guide its mutations towards making {@code current} equal to {@code
102    * target}.
103    * <p>
104    * If the relation between the raw fuzzer input and the value of {@code current} is relatively
105    * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to
106    * achieve equality.
107    *
108    * @param current a non-constant string observed during fuzz target execution
109    * @param target a string that {@code current} should become equal to, but currently isn't
110    * @param id a (probabilistically) unique identifier for this particular compare hint
111    */
guideTowardsEquality(String current, String target, int id)112   public static void guideTowardsEquality(String current, String target, int id) {
113     if (TRACE_STRCMP == null) {
114       return;
115     }
116     try {
117       TRACE_STRCMP.invokeExact(current, target, 1, id);
118     } catch (Throwable e) {
119       e.printStackTrace();
120     }
121   }
122 
123   /**
124    * Instructs the fuzzer to guide its mutations towards making {@code current} equal to {@code
125    * target}.
126    * <p>
127    * If the relation between the raw fuzzer input and the value of {@code current} is relatively
128    * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to
129    * achieve equality.
130    *
131    * @param current a non-constant byte array observed during fuzz target execution
132    * @param target a byte array that {@code current} should become equal to, but currently isn't
133    * @param id a (probabilistically) unique identifier for this particular compare hint
134    */
guideTowardsEquality(byte[] current, byte[] target, int id)135   public static void guideTowardsEquality(byte[] current, byte[] target, int id) {
136     if (TRACE_MEMCMP == null) {
137       return;
138     }
139     try {
140       TRACE_MEMCMP.invokeExact(current, target, 1, id);
141     } catch (Throwable e) {
142       e.printStackTrace();
143     }
144   }
145 
146   /**
147    * Instructs the fuzzer to guide its mutations towards making {@code haystack} contain {@code
148    * needle} as a substring.
149    * <p>
150    * If the relation between the raw fuzzer input and the value of {@code haystack} is relatively
151    * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to
152    * satisfy the substring check.
153    *
154    * @param haystack a non-constant string observed during fuzz target execution
155    * @param needle a string that should be contained in {@code haystack} as a substring, but
156    *     currently isn't
157    * @param id a (probabilistically) unique identifier for this particular compare hint
158    */
guideTowardsContainment(String haystack, String needle, int id)159   public static void guideTowardsContainment(String haystack, String needle, int id) {
160     if (TRACE_STRSTR == null) {
161       return;
162     }
163     try {
164       TRACE_STRSTR.invokeExact(haystack, needle, id);
165     } catch (Throwable e) {
166       e.printStackTrace();
167     }
168   }
169 
170   /**
171    * Instructs the fuzzer to attain as many possible values for the absolute value of {@code state}
172    * as possible.
173    * <p>
174    * Call this function from a fuzz target or a hook to help the fuzzer track partial progress
175    * (e.g. by passing the length of a common prefix of two lists that should become equal) or
176    * explore different values of state that is not directly related to code coverage (see the
177    * MazeFuzzer example).
178    * <p>
179    * <b>Note:</b> This hint only takes effect if the fuzzer is run with the argument
180    * {@code -use_value_profile=1}.
181    *
182    * @param state a numeric encoding of a state that should be varied by the fuzzer
183    * @param id a (probabilistically) unique identifier for this particular state hint
184    */
exploreState(byte state, int id)185   public static void exploreState(byte state, int id) {
186     if (TRACE_PC_INDIR == null) {
187       return;
188     }
189     // We only use the lower 7 bits of state, which allows for 128 different state values tracked
190     // per id. The particular amount of 7 bits of state is also used in libFuzzer's
191     // TracePC::HandleCmp:
192     // https://github.com/llvm/llvm-project/blob/c12d49c4e286fa108d4d69f1c6d2b8d691993ffd/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L390
193     // This value should be large enough for most use cases (e.g. tracking the length of a prefix in
194     // a comparison) while being small enough that the bitmap isn't filled up too quickly
195     // (65536 bits / 128 bits per id = 512 ids).
196 
197     // We use tracePcIndir as a way to set a bit in libFuzzer's value profile bitmap. In
198     // TracePC::HandleCallerCallee, which is what this function ultimately calls through to, the
199     // lower 12 bits of each argument are combined into a 24-bit index into the bitmap, which is
200     // then reduced modulo a 16-bit prime. To keep the modulo bias small, we should fill as many
201     // of the relevant bits as possible.
202 
203     // We pass state in the lowest bits of the caller address, which is used to form the lowest bits
204     // of the bitmap index. This should result in the best caching behavior as state is expected to
205     // change quickly in consecutive runs and in this way all its bitmap entries would be located
206     // close to each other in memory.
207     int lowerBits = (state & 0x7f) | (id << 7);
208     int upperBits = id >>> 5;
209     try {
210       TRACE_PC_INDIR.invokeExact(upperBits, lowerBits);
211     } catch (Throwable e) {
212       e.printStackTrace();
213     }
214   }
215 
216   /**
217    * Make Jazzer report the provided {@link Throwable} as a finding.
218    * <p>
219    * <b>Note:</b> This method must only be called from a method hook. In a
220    * fuzz target, simply throw an exception to trigger a finding.
221    * @param finding the finding that Jazzer should report
222    */
reportFindingFromHook(Throwable finding)223   public static void reportFindingFromHook(Throwable finding) {
224     try {
225       JAZZER_INTERNAL.getMethod("reportFindingFromHook", Throwable.class).invoke(null, finding);
226     } catch (NullPointerException | IllegalAccessException | NoSuchMethodException e) {
227       // We can only reach this point if the runtime is not on the classpath, e.g. in case of a
228       // reproducer. Just throw the finding.
229       rethrowUnchecked(finding);
230     } catch (InvocationTargetException e) {
231       rethrowUnchecked(e.getCause());
232     }
233   }
234 
235   /**
236    * Register a callback to be executed right before the fuzz target is executed for the first time.
237    * <p>
238    * This can be used to disable hooks until after Jazzer has been fully initializing, e.g. to
239    * prevent Jazzer internals from triggering hooks on Java standard library classes.
240    *
241    * @param callback the callback to execute
242    */
onFuzzTargetReady(Runnable callback)243   public static void onFuzzTargetReady(Runnable callback) {
244     try {
245       ON_FUZZ_TARGET_READY.invokeExact(callback);
246     } catch (Throwable e) {
247       e.printStackTrace();
248     }
249   }
250 
getLibFuzzerSeed()251   private static int getLibFuzzerSeed() {
252     // The Jazzer driver sets this property based on the value of libFuzzer's -seed command-line
253     // option, which allows for fully reproducible fuzzing runs if set. If not running in the
254     // context of the driver, fall back to a random number instead.
255     String rawSeed = System.getProperty("jazzer.internal.seed");
256     if (rawSeed == null) {
257       return new SecureRandom().nextInt();
258     }
259     // If jazzer.internal.seed is set, we expect it to be a valid integer.
260     return Integer.parseUnsignedInt(rawSeed);
261   }
262 
263   // Rethrows a (possibly checked) exception while avoiding a throws declaration.
264   @SuppressWarnings("unchecked")
rethrowUnchecked(Throwable t)265   private static <T extends Throwable> void rethrowUnchecked(Throwable t) throws T {
266     throw(T) t;
267   }
268 }
269