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 *)&current_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