1 #include <unistd.h>
2 #include <sys/shm.h>
3 #include <sys/mman.h>
4 #include <sys/syscall.h>
5
6 #include "frida-gumjs.h"
7
8 #include "config.h"
9 #include "debug.h"
10 #include "hash.h"
11
12 #include "asan.h"
13 #include "entry.h"
14 #include "frida_cmplog.h"
15 #include "instrument.h"
16 #include "js.h"
17 #include "persistent.h"
18 #include "prefetch.h"
19 #include "ranges.h"
20 #include "stalker.h"
21 #include "stats.h"
22 #include "util.h"
23
24 gboolean instrument_tracing = false;
25 gboolean instrument_optimize = false;
26 gboolean instrument_unique = false;
27 guint64 instrument_hash_zero = 0;
28 guint64 instrument_hash_seed = 0;
29
30 gboolean instrument_use_fixed_seed = FALSE;
31 guint64 instrument_fixed_seed = 0;
32
33 static GumStalkerTransformer *transformer = NULL;
34
35 __thread guint64 instrument_previous_pc = 0;
36
37 static GumAddress previous_rip = 0;
38 static u8 * edges_notified = NULL;
39
trace_debug(char * format,...)40 static void trace_debug(char *format, ...) {
41
42 va_list ap;
43 char buffer[4096] = {0};
44 int ret;
45 int len;
46
47 va_start(ap, format);
48 ret = vsnprintf(buffer, sizeof(buffer) - 1, format, ap);
49 va_end(ap);
50
51 if (ret < 0) { return; }
52
53 len = strnlen(buffer, sizeof(buffer));
54
55 IGNORED_RETURN(write(STDOUT_FILENO, buffer, len));
56
57 }
58
instrument_get_offset_hash(GumAddress current_rip)59 guint64 instrument_get_offset_hash(GumAddress current_rip) {
60
61 guint64 area_offset = hash64((unsigned char *)¤t_rip,
62 sizeof(GumAddress), instrument_hash_seed);
63 return area_offset &= MAP_SIZE - 1;
64
65 }
66
instrument_increment_map(GumAddress edge)67 __attribute__((hot)) static void instrument_increment_map(GumAddress edge) {
68
69 uint8_t *cursor;
70 uint64_t value;
71
72 cursor = &__afl_area_ptr[edge];
73 value = *cursor;
74
75 if (value == 0xff) {
76
77 value = 1;
78
79 } else {
80
81 value++;
82
83 }
84
85 *cursor = value;
86
87 }
88
on_basic_block(GumCpuContext * context,gpointer user_data)89 __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
90 gpointer user_data) {
91
92 UNUSED_PARAMETER(context);
93
94 GumAddress current_rip = GUM_ADDRESS(user_data);
95 guint64 current_pc = instrument_get_offset_hash(current_rip);
96 guint64 edge;
97
98 edge = current_pc ^ instrument_previous_pc;
99
100 instrument_increment_map(edge);
101
102 if (unlikely(instrument_tracing)) {
103
104 if (!instrument_unique || edges_notified[edge] == 0) {
105
106 trace_debug("TRACE: edge: %10" G_GINT64_MODIFIER
107 "d, current_rip: 0x%016" G_GINT64_MODIFIER
108 "x, previous_rip: 0x%016" G_GINT64_MODIFIER "x\n",
109 edge, current_rip, previous_rip);
110
111 }
112
113 if (instrument_unique) { edges_notified[edge] = 1; }
114
115 previous_rip = current_rip;
116
117 }
118
119 instrument_previous_pc =
120 ((current_pc & (MAP_SIZE - 1) >> 1)) | ((current_pc & 0x1) << 15);
121
122 }
123
instrument_basic_block(GumStalkerIterator * iterator,GumStalkerOutput * output,gpointer user_data)124 static void instrument_basic_block(GumStalkerIterator *iterator,
125 GumStalkerOutput * output,
126 gpointer user_data) {
127
128 UNUSED_PARAMETER(user_data);
129
130 const cs_insn *instr;
131 gboolean begin = TRUE;
132 gboolean excluded;
133
134 while (gum_stalker_iterator_next(iterator, &instr)) {
135
136 if (unlikely(begin)) { instrument_debug_start(instr->address, output); }
137
138 if (instr->address == entry_point) { entry_prologue(iterator, output); }
139 if (instr->address == persistent_start) { persistent_prologue(output); }
140 if (instr->address == persistent_ret) { persistent_epilogue(output); }
141
142 /*
143 * Until we reach AFL_ENTRYPOINT (assumed to be main if not specified) or
144 * AFL_FRIDA_PERSISTENT_ADDR (if specified), we don't mark our ranges
145 * excluded as we wish to remain inside stalker at all times so that we can
146 * instrument our entry point and persistent loop (if present). This allows
147 * the user to exclude ranges which would be traversed between main and the
148 * AFL_ENTRYPOINT, but which they don't want included in their coverage
149 * information when fuzzing.
150 *
151 * Since we have no means to discard the instrumented copies of blocks
152 * (setting the trust threshold simply causes a new copy to be made on each
153 * execution), we instead ensure that we honour the additional
154 * instrumentation requested (e.g. coverage, asan and complog) when a block
155 * is compiled no matter where we are during initialization. We will end up
156 * re-using these blocks if the code under test calls a block which is also
157 * used during initialization.
158 *
159 * Coverage data generated during initialization isn't a problem since the
160 * map is zeroed each time the target is forked or each time the persistent
161 * loop is run.
162 *
163 * Lastly, we don't enable pre-fetching back to the parent until we reach
164 * our AFL_ENTRYPOINT, since it is not until then that we start the
165 * fork-server and thus start executing in the child.
166 */
167 excluded = range_is_excluded(GSIZE_TO_POINTER(instr->address));
168
169 stats_collect(instr, begin);
170
171 if (unlikely(begin)) {
172
173 instrument_debug_start(instr->address, output);
174
175 if (likely(entry_reached)) {
176
177 prefetch_write(GSIZE_TO_POINTER(instr->address));
178
179 }
180
181 if (likely(!excluded)) {
182
183 if (likely(instrument_optimize)) {
184
185 instrument_coverage_optimize(instr, output);
186
187 } else {
188
189 gum_stalker_iterator_put_callout(
190 iterator, on_basic_block, GSIZE_TO_POINTER(instr->address), NULL);
191
192 }
193
194 }
195
196 }
197
198 instrument_debug_instruction(instr->address, instr->size);
199
200 if (likely(!excluded)) {
201
202 asan_instrument(instr, iterator);
203 cmplog_instrument(instr, iterator);
204
205 }
206
207 if (js_stalker_callback(instr, begin, excluded, output)) {
208
209 gum_stalker_iterator_keep(iterator);
210
211 }
212
213 begin = FALSE;
214
215 }
216
217 instrument_flush(output);
218 instrument_debug_end(output);
219
220 }
221
instrument_config(void)222 void instrument_config(void) {
223
224 instrument_optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
225 instrument_tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
226 instrument_unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL);
227 instrument_use_fixed_seed = (getenv("AFL_FRIDA_INST_SEED") != NULL);
228 instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED");
229
230 instrument_debug_config();
231 asan_config();
232 cmplog_config();
233
234 }
235
instrument_init(void)236 void instrument_init(void) {
237
238 if (!instrument_is_coverage_optimize_supported()) instrument_optimize = false;
239
240 OKF("Instrumentation - optimize [%c]", instrument_optimize ? 'X' : ' ');
241 OKF("Instrumentation - tracing [%c]", instrument_tracing ? 'X' : ' ');
242 OKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' ');
243 OKF("Instrumentation - fixed seed [%c] [0x%016" G_GINT64_MODIFIER "x]",
244 instrument_use_fixed_seed ? 'X' : ' ', instrument_fixed_seed);
245
246 if (instrument_tracing && instrument_optimize) {
247
248 WARNF("AFL_FRIDA_INST_TRACE implies AFL_FRIDA_INST_NO_OPTIMIZE");
249 instrument_optimize = FALSE;
250
251 }
252
253 if (instrument_unique && instrument_optimize) {
254
255 WARNF("AFL_FRIDA_INST_TRACE_UNIQUE implies AFL_FRIDA_INST_NO_OPTIMIZE");
256 instrument_optimize = FALSE;
257
258 }
259
260 if (instrument_unique) { instrument_tracing = TRUE; }
261
262 if (__afl_map_size != 0x10000) {
263
264 FATAL("Bad map size: 0x%08x", __afl_map_size);
265
266 }
267
268 transformer = gum_stalker_transformer_make_from_callback(
269 instrument_basic_block, NULL, NULL);
270
271 if (instrument_unique) {
272
273 int shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
274 if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
275
276 edges_notified = shmat(shm_id, NULL, 0);
277 g_assert(edges_notified != MAP_FAILED);
278
279 /*
280 * Configure the shared memory region to be removed once the process
281 * dies.
282 */
283 if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
284
285 FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
286
287 }
288
289 /* Clear it, not sure it's necessary, just seems like good practice */
290 memset(edges_notified, '\0', MAP_SIZE);
291
292 }
293
294 if (instrument_use_fixed_seed) {
295
296 /*
297 * This configuration option may be useful for diagnostics or
298 * debugging.
299 */
300 instrument_hash_seed = instrument_fixed_seed;
301
302 } else {
303
304 /*
305 * By using a different seed value for the hash, we can make different
306 * instances have edge collisions in different places when carrying out
307 * parallel fuzzing. The seed itself, doesn't have to be random, it
308 * just needs to be different for each instance.
309 */
310 instrument_hash_seed = g_get_monotonic_time() ^
311 (((guint64)getpid()) << 32) ^ syscall(SYS_gettid);
312
313 }
314
315 OKF("Instrumentation - seed [0x%016" G_GINT64_MODIFIER "x]",
316 instrument_hash_seed);
317 instrument_hash_zero = instrument_get_offset_hash(0);
318
319 instrument_debug_init();
320 asan_init();
321 cmplog_init();
322
323 }
324
instrument_get_transformer(void)325 GumStalkerTransformer *instrument_get_transformer(void) {
326
327 if (transformer == NULL) { FATAL("Instrumentation not initialized"); }
328 return transformer;
329
330 }
331
instrument_on_fork()332 void instrument_on_fork() {
333
334 instrument_previous_pc = instrument_hash_zero;
335
336 }
337
338