parent
							
								
									b187ad628d
								
							
						
					
					
						commit
						ebf9685124
					
				| @ -0,0 +1,313 @@ | |||||||
|  | /*
 | ||||||
|  |   Copyright 2015 Google LLC All rights reserved. | ||||||
|  | 
 | ||||||
|  |   Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |   you may not use this file except in compliance with the License. | ||||||
|  |   You may obtain a copy of the License at: | ||||||
|  | 
 | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | 
 | ||||||
|  |   Unless required by applicable law or agreed to in writing, software | ||||||
|  |   distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |   See the License for the specific language governing permissions and | ||||||
|  |   limitations under the License. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |    american fuzzy lop - high-performance binary-only instrumentation | ||||||
|  |    ----------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  |    Written by Andrew Griffiths <agriffiths@google.com> and | ||||||
|  |               Michal Zalewski <lcamtuf@google.com> | ||||||
|  | 
 | ||||||
|  |    Idea & design very much by Andrew Griffiths. | ||||||
|  | 
 | ||||||
|  |    This code is a shim patched into the separately-distributed source | ||||||
|  |    code of QEMU 2.10.0. It leverages the built-in QEMU tracing functionality | ||||||
|  |    to implement AFL-style instrumentation and to take care of the remaining | ||||||
|  |    parts of the AFL fork server logic. | ||||||
|  | 
 | ||||||
|  |    The resulting QEMU binary is essentially a standalone instrumentation | ||||||
|  |    tool; for an example of how to leverage it for other purposes, you can | ||||||
|  |    have a look at afl-showmap.c. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include <sys/shm.h> | ||||||
|  | #include "../../config.h" | ||||||
|  | 
 | ||||||
|  | /***************************
 | ||||||
|  |  * VARIOUS AUXILIARY STUFF * | ||||||
|  |  ***************************/ | ||||||
|  | 
 | ||||||
|  | /* A snippet patched into tb_find_slow to inform the parent process that
 | ||||||
|  |    we have hit a new block that hasn't been translated yet, and to tell | ||||||
|  |    it to translate within its own context, too (this avoids translation | ||||||
|  |    overhead in the next forked-off copy). */ | ||||||
|  | 
 | ||||||
|  | #define AFL_QEMU_CPU_SNIPPET1 do { \ | ||||||
|  |     afl_request_tsl(pc, cs_base, flags); \ | ||||||
|  |   } while (0) | ||||||
|  | 
 | ||||||
|  | /* This snippet kicks in when the instruction pointer is positioned at
 | ||||||
|  |    _start and does the usual forkserver stuff, not very different from | ||||||
|  |    regular instrumentation injected via afl-as.h. */ | ||||||
|  | 
 | ||||||
|  | #define AFL_QEMU_CPU_SNIPPET2 do { \ | ||||||
|  |     if(itb->pc == afl_entry_point) { \ | ||||||
|  |       afl_setup(); \ | ||||||
|  |       afl_forkserver(cpu); \ | ||||||
|  |     } \ | ||||||
|  |     afl_maybe_log(itb->pc); \ | ||||||
|  |   } while (0) | ||||||
|  | 
 | ||||||
|  | /* We use one additional file descriptor to relay "needs translation"
 | ||||||
|  |    messages between the child and the fork server. */ | ||||||
|  | 
 | ||||||
|  | #define TSL_FD (FORKSRV_FD - 1) | ||||||
|  | 
 | ||||||
|  | /* This is equivalent to afl-as.h: */ | ||||||
|  | 
 | ||||||
|  | static unsigned char *afl_area_ptr; | ||||||
|  | 
 | ||||||
|  | /* Exported variables populated by the code patched into elfload.c: */ | ||||||
|  | 
 | ||||||
|  | abi_ulong afl_entry_point, /* ELF entry point (_start) */ | ||||||
|  |           afl_start_code,  /* .text start pointer      */ | ||||||
|  |           afl_end_code;    /* .text end pointer        */ | ||||||
|  | 
 | ||||||
|  | /* Set in the child process in forkserver mode: */ | ||||||
|  | 
 | ||||||
|  | static unsigned char afl_fork_child; | ||||||
|  | unsigned int afl_forksrv_pid; | ||||||
|  | 
 | ||||||
|  | /* Instrumentation ratio: */ | ||||||
|  | 
 | ||||||
|  | static unsigned int afl_inst_rms = MAP_SIZE; | ||||||
|  | 
 | ||||||
|  | /* Function declarations. */ | ||||||
|  | 
 | ||||||
|  | static void afl_setup(void); | ||||||
|  | static void afl_forkserver(CPUState*); | ||||||
|  | static inline void afl_maybe_log(abi_ulong); | ||||||
|  | 
 | ||||||
|  | static void afl_wait_tsl(CPUState*, int); | ||||||
|  | static void afl_request_tsl(target_ulong, target_ulong, uint64_t); | ||||||
|  | 
 | ||||||
|  | /* Data structure passed around by the translate handlers: */ | ||||||
|  | 
 | ||||||
|  | struct afl_tsl { | ||||||
|  |   target_ulong pc; | ||||||
|  |   target_ulong cs_base; | ||||||
|  |   uint64_t flags; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Some forward decls: */ | ||||||
|  | 
 | ||||||
|  | TranslationBlock *tb_htable_lookup(CPUState*, target_ulong, target_ulong, uint32_t); | ||||||
|  | static inline TranslationBlock *tb_find(CPUState*, TranslationBlock*, int); | ||||||
|  | 
 | ||||||
|  | /*************************
 | ||||||
|  |  * ACTUAL IMPLEMENTATION * | ||||||
|  |  *************************/ | ||||||
|  | 
 | ||||||
|  | /* Set up SHM region and initialize other stuff. */ | ||||||
|  | 
 | ||||||
|  | static void afl_setup(void) { | ||||||
|  | 
 | ||||||
|  |   char *id_str = getenv(SHM_ENV_VAR), | ||||||
|  |        *inst_r = getenv("AFL_INST_RATIO"); | ||||||
|  | 
 | ||||||
|  |   int shm_id; | ||||||
|  | 
 | ||||||
|  |   if (inst_r) { | ||||||
|  | 
 | ||||||
|  |     unsigned int r; | ||||||
|  | 
 | ||||||
|  |     r = atoi(inst_r); | ||||||
|  | 
 | ||||||
|  |     if (r > 100) r = 100; | ||||||
|  |     if (!r) r = 1; | ||||||
|  | 
 | ||||||
|  |     afl_inst_rms = MAP_SIZE * r / 100; | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (id_str) { | ||||||
|  | 
 | ||||||
|  |     shm_id = atoi(id_str); | ||||||
|  |     afl_area_ptr = shmat(shm_id, NULL, 0); | ||||||
|  | 
 | ||||||
|  |     if (afl_area_ptr == (void*)-1) exit(1); | ||||||
|  | 
 | ||||||
|  |     /* With AFL_INST_RATIO set to a low value, we want to touch the bitmap
 | ||||||
|  |        so that the parent doesn't give up on us. */ | ||||||
|  | 
 | ||||||
|  |     if (inst_r) afl_area_ptr[0] = 1; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (getenv("AFL_INST_LIBS")) { | ||||||
|  | 
 | ||||||
|  |     afl_start_code = 0; | ||||||
|  |     afl_end_code   = (abi_ulong)-1; | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
 | ||||||
|  |      not entirely sure what is the cause. This disables that | ||||||
|  |      behaviour, and seems to work alright? */ | ||||||
|  | 
 | ||||||
|  |   rcu_disable_atfork(); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Fork server logic, invoked once we hit _start. */ | ||||||
|  | 
 | ||||||
|  | static void afl_forkserver(CPUState *cpu) { | ||||||
|  | 
 | ||||||
|  |   static unsigned char tmp[4]; | ||||||
|  | 
 | ||||||
|  |   if (!afl_area_ptr) return; | ||||||
|  | 
 | ||||||
|  |   /* Tell the parent that we're alive. If the parent doesn't want
 | ||||||
|  |      to talk, assume that we're not running in forkserver mode. */ | ||||||
|  | 
 | ||||||
|  |   if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; | ||||||
|  | 
 | ||||||
|  |   afl_forksrv_pid = getpid(); | ||||||
|  | 
 | ||||||
|  |   /* All right, let's await orders... */ | ||||||
|  | 
 | ||||||
|  |   while (1) { | ||||||
|  | 
 | ||||||
|  |     pid_t child_pid; | ||||||
|  |     int status, t_fd[2]; | ||||||
|  | 
 | ||||||
|  |     /* Whoops, parent dead? */ | ||||||
|  | 
 | ||||||
|  |     if (read(FORKSRV_FD, tmp, 4) != 4) exit(2); | ||||||
|  | 
 | ||||||
|  |     /* Establish a channel with child to grab translation commands. We'll
 | ||||||
|  |        read from t_fd[0], child will write to TSL_FD. */ | ||||||
|  | 
 | ||||||
|  |     if (pipe(t_fd) || dup2(t_fd[1], TSL_FD) < 0) exit(3); | ||||||
|  |     close(t_fd[1]); | ||||||
|  | 
 | ||||||
|  |     child_pid = fork(); | ||||||
|  |     if (child_pid < 0) exit(4); | ||||||
|  | 
 | ||||||
|  |     if (!child_pid) { | ||||||
|  | 
 | ||||||
|  |       /* Child process. Close descriptors and run free. */ | ||||||
|  | 
 | ||||||
|  |       afl_fork_child = 1; | ||||||
|  |       close(FORKSRV_FD); | ||||||
|  |       close(FORKSRV_FD + 1); | ||||||
|  |       close(t_fd[0]); | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Parent. */ | ||||||
|  | 
 | ||||||
|  |     close(TSL_FD); | ||||||
|  | 
 | ||||||
|  |     if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(5); | ||||||
|  | 
 | ||||||
|  |     /* Collect translation requests until child dies and closes the pipe. */ | ||||||
|  | 
 | ||||||
|  |     afl_wait_tsl(cpu, t_fd[0]); | ||||||
|  | 
 | ||||||
|  |     /* Get and relay exit status to parent. */ | ||||||
|  | 
 | ||||||
|  |     if (waitpid(child_pid, &status, 0) < 0) exit(6); | ||||||
|  |     if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7); | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* The equivalent of the tuple logging routine from afl-as.h. */ | ||||||
|  | 
 | ||||||
|  | static inline void afl_maybe_log(abi_ulong cur_loc) { | ||||||
|  | 
 | ||||||
|  |   static __thread abi_ulong prev_loc; | ||||||
|  | 
 | ||||||
|  |   /* Optimize for cur_loc > afl_end_code, which is the most likely case on
 | ||||||
|  |      Linux systems. */ | ||||||
|  | 
 | ||||||
|  |   if (cur_loc > afl_end_code || cur_loc < afl_start_code || !afl_area_ptr) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   /* Looks like QEMU always maps to fixed locations, so ASAN is not a
 | ||||||
|  |      concern. Phew. But instruction addresses may be aligned. Let's mangle | ||||||
|  |      the value to get something quasi-uniform. */ | ||||||
|  | 
 | ||||||
|  |   cur_loc  = (cur_loc >> 4) ^ (cur_loc << 8); | ||||||
|  |   cur_loc &= MAP_SIZE - 1; | ||||||
|  | 
 | ||||||
|  |   /* Implement probabilistic instrumentation by looking at scrambled block
 | ||||||
|  |      address. This keeps the instrumented locations stable across runs. */ | ||||||
|  | 
 | ||||||
|  |   if (cur_loc >= afl_inst_rms) return; | ||||||
|  | 
 | ||||||
|  |   afl_area_ptr[cur_loc ^ prev_loc]++; | ||||||
|  |   prev_loc = cur_loc >> 1; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* This code is invoked whenever QEMU decides that it doesn't have a
 | ||||||
|  |    translation of a particular block and needs to compute it. When this happens, | ||||||
|  |    we tell the parent to mirror the operation, so that the next fork() has a | ||||||
|  |    cached copy. */ | ||||||
|  | 
 | ||||||
|  | static void afl_request_tsl(target_ulong pc, target_ulong cb, uint64_t flags) { | ||||||
|  | 
 | ||||||
|  |   struct afl_tsl t; | ||||||
|  | 
 | ||||||
|  |   if (!afl_fork_child) return; | ||||||
|  | 
 | ||||||
|  |   t.pc      = pc; | ||||||
|  |   t.cs_base = cb; | ||||||
|  |   t.flags   = flags; | ||||||
|  | 
 | ||||||
|  |   if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* This is the other side of the same channel. Since timeouts are handled by
 | ||||||
|  |    afl-fuzz simply killing the child, we can just wait until the pipe breaks. */ | ||||||
|  | 
 | ||||||
|  | static void afl_wait_tsl(CPUState *cpu, int fd) { | ||||||
|  | 
 | ||||||
|  |   struct afl_tsl t; | ||||||
|  |   TranslationBlock *tb; | ||||||
|  | 
 | ||||||
|  |   while (1) { | ||||||
|  | 
 | ||||||
|  |     /* Broken pipe means it's time to return to the fork server routine. */ | ||||||
|  | 
 | ||||||
|  |     if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     tb = tb_htable_lookup(cpu, t.pc, t.cs_base, t.flags); | ||||||
|  | 
 | ||||||
|  |     if(!tb) { | ||||||
|  |       mmap_lock(); | ||||||
|  |       tb_lock(); | ||||||
|  |       tb_gen_code(cpu, t.pc, t.cs_base, t.flags, 0); | ||||||
|  |       mmap_unlock(); | ||||||
|  |       tb_unlock(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   close(fd); | ||||||
|  | 
 | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue