Merge pull request #26 from vanhauser-thc/qemu-compcov

Qemu CompCov
This commit is contained in:
Andrea Fioraldi
2019-07-20 14:23:07 +02:00
committed by GitHub
14 changed files with 1025 additions and 11 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
*.o
*.so
.gitignore .gitignore
afl-analyze afl-analyze
afl-as afl-as

3
TODO
View File

@ -23,6 +23,9 @@ gcc_plugin:
- laf-intel - laf-intel
- neverZero - neverZero
qemu_mode:
- Instrument only cmparison with immediate values by default when using compcov
unit testing / or large testcase campaign unit testing / or large testcase campaign

View File

@ -245,6 +245,10 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
- Setting AFL_INST_LIBS causes the translator to also instrument the code - Setting AFL_INST_LIBS causes the translator to also instrument the code
inside any dynamically linked libraries (notably including glibc). inside any dynamically linked libraries (notably including glibc).
- Setting AFL_QEMU_COMPCOV enables the CompareCoverage tracing of all
cmp and sub in x86 and x86_64. Support for other architectures and
comparison functions (mem/strcmp et al.) is planned.
- The underlying QEMU binary will recognize any standard "user space - The underlying QEMU binary will recognize any standard "user space
emulation" variables (e.g., QEMU_STACK_SIZE), but there should be no emulation" variables (e.g., QEMU_STACK_SIZE), but there should be no
reason to touch them. reason to touch them.

View File

@ -133,6 +133,7 @@ patch -p1 <../patches/cpu-exec.diff || exit 1
patch -p1 <../patches/syscall.diff || exit 1 patch -p1 <../patches/syscall.diff || exit 1
patch -p1 <../patches/translate-all.diff || exit 1 patch -p1 <../patches/translate-all.diff || exit 1
patch -p1 <../patches/tcg.diff || exit 1 patch -p1 <../patches/tcg.diff || exit 1
patch -p1 <../patches/i386-translate.diff || exit 1
echo "[+] Patching done." echo "[+] Patching done."

View File

@ -0,0 +1,42 @@
#
# american fuzzy lop - libcompcov
# --------------------------------
#
# Written by Andrea Fioraldi <andreafioraldi@gmail.com>
#
# Copyright 2019 Andrea Fioraldi. 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
#
PREFIX ?= /usr/local
HELPER_PATH = $(PREFIX)/lib/afl
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
CFLAGS ?= -O3 -funroll-loops
CFLAGS += -Wall -Wno-unused-result -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
LDFLAGS += -ldl
all: libcompcov.so compcovtest
libcompcov.so: libcompcov.so.c ../../config.h
$(CC) $(CFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS)
.NOTPARALLEL: clean
clean:
rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
rm -f libcompcov.so compcovtest
compcovtest: compcovtest.cc
$(CXX) $< -o $@
install: all
install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH)
install -m 644 README.compcov $${DESTDIR}$(HELPER_PATH)

View File

@ -0,0 +1,33 @@
================================================================
strcmp() / memcmp() CompareCoverage library for AFLplusplus-QEMU
================================================================
Written by Andrea Fioraldi <andreafioraldi@gmail.com>
This Linux-only companion library allows you to instrument strcmp(), memcmp(),
and related functions to log the CompareCoverage of these libcalls.
Use this with caution. While this can speedup a lot the bypass of hard
branch conditions it can also waste a lot of time and take up unnecessary space
in the shared memory when logging the coverage related to functions that
doesn't process input-related data.
To use the library, you *need* to make sure that your fuzzing target is linked
dynamically and make use of strcmp(), memcmp(), and related functions.
For optimized binaries this is an issue, those functions are often inlined
and this module is not capable to log the coverage in this case.
If you have the source code of the fuzzing target you should nto use this
library and QEMU but build ot with afl-clang-fast and the laf-intel options.
To use this library make sure to preload it with AFL_PRELOAD.
export AFL_PRELOAD=/path/to/libcompcov.so
export AFL_QEMU_COMPCOV=1
afl-fuzz -Q -i input -o output <your options> -- <target args>
The library make use of https://github.com/ouadev/proc_maps_parser and so it is
Linux specific. However this is not a strict dependency, other UNIX operating
systems can be supported simply replacing the code related to the
/proc/self/maps parsing.

View File

@ -0,0 +1,63 @@
/////////////////////////////////////////////////////////////////////////
//
// Author: Mateusz Jurczyk (mjurczyk@google.com)
//
// Copyright 2019 Google LLC
//
// 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
//
// https://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.
//
// solution: echo -ne 'The quick brown fox jumps over the lazy dog\xbe\xba\xfe\xca\xbe\xba\xfe\xca\xde\xc0\xad\xde\xef\xbe' | ./compcovtest
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
int main() {
char buffer[44] = { /* zero padding */ };
fread(buffer, 1, sizeof(buffer) - 1, stdin);
if (memcmp(&buffer[0], "The quick brown fox ", 20) != 0 ||
strncmp(&buffer[20], "jumps over ", 11) != 0 ||
strcmp(&buffer[31], "the lazy dog") != 0) {
return 1;
}
uint64_t x = 0;
fread(&x, sizeof(x), 1, stdin);
if (x != 0xCAFEBABECAFEBABE) {
return 2;
}
uint32_t y = 0;
fread(&y, sizeof(y), 1, stdin);
if (y != 0xDEADC0DE) {
return 3;
}
uint16_t z = 0;
fread(&z, sizeof(z), 1, stdin);
switch (z) {
case 0xBEEF:
break;
default:
return 4;
}
printf("Puzzle solved, congrats!\n");
abort();
return 0;
}

View File

@ -0,0 +1,279 @@
/*
american fuzzy lop++ - strcmp() / memcmp() CompareCoverage library
------------------------------------------------------------------
Written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com>
Copyright 2019 Andrea Fioraldi. 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
This Linux-only companion library allows you to instrument strcmp(),
memcmp(), and related functions to get compare coverage.
See README.compcov for more info.
*/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/shm.h>
#include "../../types.h"
#include "../../config.h"
#include "pmparser.h"
#ifndef __linux__
# error "Sorry, this library is Linux-specific for now!"
#endif /* !__linux__ */
/* Change this value to tune the compare coverage */
#define MAX_CMP_LENGTH 32
static void *__compcov_code_start,
*__compcov_code_end;
static u8 *__compcov_afl_map;
static int (*__libc_strcmp)(const char*, const char*);
static int (*__libc_strncmp)(const char*, const char*, size_t);
static int (*__libc_strcasecmp)(const char*, const char*);
static int (*__libc_strncasecmp)(const char*, const char*, size_t);
static int (*__libc_memcmp)(const void*, const void*, size_t);
static int debug_fd = -1;
static size_t __strlen2(const char *s1, const char *s2, size_t max_length) {
// from https://github.com/googleprojectzero/CompareCoverage
size_t len = 0;
for (; len < max_length && s1[len] != '\0' && s2[len] != '\0'; len++) { }
return len;
}
/* Identify the binary boundaries in the memory mapping */
static void __compcov_load(void) {
__libc_strcmp = dlsym(RTLD_NEXT, "strcmp");
__libc_strncmp = dlsym(RTLD_NEXT, "strncmp");
__libc_strcasecmp = dlsym(RTLD_NEXT, "strcasecmp");
__libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp");
__libc_memcmp = dlsym(RTLD_NEXT, "memcmp");
char *id_str = getenv(SHM_ENV_VAR);
int shm_id;
if (id_str) {
shm_id = atoi(id_str);
__compcov_afl_map = shmat(shm_id, NULL, 0);
if (__compcov_afl_map == (void*)-1) exit(1);
} else {
__compcov_afl_map = calloc(1, MAP_SIZE);
}
if (getenv("AFL_INST_LIBS")) {
__compcov_code_start = (void*)0;
__compcov_code_end = (void*)-1;
return;
}
char* bin_name = getenv("AFL_COMPCOV_BINNAME");
procmaps_iterator* maps = pmparser_parse(-1);
procmaps_struct* maps_tmp = NULL;
while ((maps_tmp = pmparser_next(maps)) != NULL) {
/* If AFL_COMPCOV_BINNAME is not set pick the first executable segment */
if (!bin_name || strstr(maps_tmp->pathname, bin_name) != NULL) {
if (maps_tmp->is_x) {
if (!__compcov_code_start)
__compcov_code_start = maps_tmp->addr_start;
if (!__compcov_code_end)
__compcov_code_end = maps_tmp->addr_end;
}
}
}
pmparser_free(maps);
}
static void __compcov_trace(u64 cur_loc, const u8* v0, const u8* v1, size_t n) {
size_t i;
if (debug_fd != 1) {
char debugbuf[4096];
snprintf(debugbuf, sizeof(debugbuf), "0x%llx %s %s %lu\n", cur_loc, v0 == NULL ? "(null)" : (char*)v0, v1 == NULL ? "(null)" : (char*)v1, n);
write(debug_fd, debugbuf, strlen(debugbuf));
}
for (i = 0; i < n && v0[i] == v1[i]; ++i) {
__compcov_afl_map[cur_loc +i]++;
}
}
/* Check an address against the list of read-only mappings. */
static u8 __compcov_is_in_bound(const void* ptr) {
return ptr >= __compcov_code_start && ptr < __compcov_code_end;
}
/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used
only if the target is compiled with -fno-builtins and linked dynamically. */
#undef strcmp
int strcmp(const char* str1, const char* str2) {
void* retaddr = __builtin_return_address(0);
if (__compcov_is_in_bound(retaddr)) {
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
if (n <= MAX_CMP_LENGTH) {
u64 cur_loc = (u64)retaddr;
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
cur_loc &= MAP_SIZE - 1;
__compcov_trace(cur_loc, str1, str2, n);
}
}
return __libc_strcmp(str1, str2);
}
#undef strncmp
int strncmp(const char* str1, const char* str2, size_t len) {
void* retaddr = __builtin_return_address(0);
if (__compcov_is_in_bound(retaddr)) {
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
n = MIN(n, len);
if (n <= MAX_CMP_LENGTH) {
u64 cur_loc = (u64)retaddr;
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
cur_loc &= MAP_SIZE - 1;
__compcov_trace(cur_loc, str1, str2, n);
}
}
return __libc_strncmp(str1, str2, len);
}
#undef strcasecmp
int strcasecmp(const char* str1, const char* str2) {
void* retaddr = __builtin_return_address(0);
if (__compcov_is_in_bound(retaddr)) {
/* Fallback to strcmp, maybe improve in future */
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
if (n <= MAX_CMP_LENGTH) {
u64 cur_loc = (u64)retaddr;
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
cur_loc &= MAP_SIZE - 1;
__compcov_trace(cur_loc, str1, str2, n);
}
}
return __libc_strcasecmp(str1, str2);
}
#undef strncasecmp
int strncasecmp(const char* str1, const char* str2, size_t len) {
void* retaddr = __builtin_return_address(0);
if (__compcov_is_in_bound(retaddr)) {
/* Fallback to strncmp, maybe improve in future */
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
n = MIN(n, len);
if (n <= MAX_CMP_LENGTH) {
u64 cur_loc = (u64)retaddr;
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
cur_loc &= MAP_SIZE - 1;
__compcov_trace(cur_loc, str1, str2, n);
}
}
return __libc_strncasecmp(str1, str2, len);
}
#undef memcmp
int memcmp(const void* mem1, const void* mem2, size_t len) {
void* retaddr = __builtin_return_address(0);
if (__compcov_is_in_bound(retaddr)) {
size_t n = len;
if (n <= MAX_CMP_LENGTH) {
u64 cur_loc = (u64)retaddr;
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
cur_loc &= MAP_SIZE - 1;
__compcov_trace(cur_loc, mem1, mem2, n);
}
}
return __libc_memcmp(mem1, mem2, len);
}
/* Init code to open init the library. */
__attribute__((constructor)) void __compcov_init(void) {
if (getenv("AFL_QEMU_COMPCOV_DEBUG") != NULL)
debug_fd = open("compcov.debug", O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644);
__compcov_load();
}

View File

@ -0,0 +1,280 @@
/*
@Author : ouadimjamal@gmail.com
@date : December 2015
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation. No representations are made about the suitability of this
software for any purpose. It is provided "as is" without express or
implied warranty.
*/
#ifndef H_PMPARSER
#define H_PMPARSER
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/limits.h>
//maximum line length in a procmaps file
#define PROCMAPS_LINE_MAX_LENGTH (PATH_MAX + 100)
/**
* procmaps_struct
* @desc hold all the information about an area in the process's VM
*/
typedef struct procmaps_struct{
void* addr_start; //< start address of the area
void* addr_end; //< end address
unsigned long length; //< size of the range
char perm[5]; //< permissions rwxp
short is_r; //< rewrote of perm with short flags
short is_w;
short is_x;
short is_p;
long offset; //< offset
char dev[12]; //< dev major:minor
int inode; //< inode of the file that backs the area
char pathname[600]; //< the path of the file that backs the area
//chained list
struct procmaps_struct* next; //<handler of the chinaed list
} procmaps_struct;
/**
* procmaps_iterator
* @desc holds iterating information
*/
typedef struct procmaps_iterator{
procmaps_struct* head;
procmaps_struct* current;
} procmaps_iterator;
/**
* pmparser_parse
* @param pid the process id whose memory map to be parser. the current process if pid<0
* @return an iterator over all the nodes
*/
procmaps_iterator* pmparser_parse(int pid);
/**
* pmparser_next
* @description move between areas
* @param p_procmaps_it the iterator to move on step in the chained list
* @return a procmaps structure filled with information about this VM area
*/
procmaps_struct* pmparser_next(procmaps_iterator* p_procmaps_it);
/**
* pmparser_free
* @description should be called at the end to free the resources
* @param p_procmaps_it the iterator structure returned by pmparser_parse
*/
void pmparser_free(procmaps_iterator* p_procmaps_it);
/**
* _pmparser_split_line
* @description internal usage
*/
void _pmparser_split_line(char*buf,char*addr1,char*addr2,char*perm, char* offset, char* device,char*inode,char* pathname);
/**
* pmparser_print
* @param map the head of the list
* @order the order of the area to print, -1 to print everything
*/
void pmparser_print(procmaps_struct* map,int order);
/**
* gobal variables
*/
//procmaps_struct* g_last_head=NULL;
//procmaps_struct* g_current=NULL;
procmaps_iterator* pmparser_parse(int pid){
procmaps_iterator* maps_it = malloc(sizeof(procmaps_iterator));
char maps_path[500];
if(pid>=0 ){
sprintf(maps_path,"/proc/%d/maps",pid);
}else{
sprintf(maps_path,"/proc/self/maps");
}
FILE* file=fopen(maps_path,"r");
if(!file){
fprintf(stderr,"pmparser : cannot open the memory maps, %s\n",strerror(errno));
return NULL;
}
int ind=0;char buf[PROCMAPS_LINE_MAX_LENGTH];
//int c;
procmaps_struct* list_maps=NULL;
procmaps_struct* tmp;
procmaps_struct* current_node=list_maps;
char addr1[20],addr2[20], perm[8], offset[20], dev[10],inode[30],pathname[PATH_MAX];
while( !feof(file) ){
fgets(buf,PROCMAPS_LINE_MAX_LENGTH,file);
//allocate a node
tmp=(procmaps_struct*)malloc(sizeof(procmaps_struct));
//fill the node
_pmparser_split_line(buf,addr1,addr2,perm,offset, dev,inode,pathname);
//printf("#%s",buf);
//printf("%s-%s %s %s %s %s\t%s\n",addr1,addr2,perm,offset,dev,inode,pathname);
//addr_start & addr_end
//unsigned long l_addr_start;
sscanf(addr1,"%lx",(long unsigned *)&tmp->addr_start );
sscanf(addr2,"%lx",(long unsigned *)&tmp->addr_end );
//size
tmp->length=(unsigned long)(tmp->addr_end-tmp->addr_start);
//perm
strcpy(tmp->perm,perm);
tmp->is_r=(perm[0]=='r');
tmp->is_w=(perm[1]=='w');
tmp->is_x=(perm[2]=='x');
tmp->is_p=(perm[3]=='p');
//offset
sscanf(offset,"%lx",&tmp->offset );
//device
strcpy(tmp->dev,dev);
//inode
tmp->inode=atoi(inode);
//pathname
strcpy(tmp->pathname,pathname);
tmp->next=NULL;
//attach the node
if(ind==0){
list_maps=tmp;
list_maps->next=NULL;
current_node=list_maps;
}
current_node->next=tmp;
current_node=tmp;
ind++;
//printf("%s",buf);
}
//close file
fclose(file);
//g_last_head=list_maps;
maps_it->head = list_maps;
maps_it->current = list_maps;
return maps_it;
}
procmaps_struct* pmparser_next(procmaps_iterator* p_procmaps_it){
if(p_procmaps_it->current == NULL)
return NULL;
procmaps_struct* p_current = p_procmaps_it->current;
p_procmaps_it->current = p_procmaps_it->current->next;
return p_current;
/*
if(g_current==NULL){
g_current=g_last_head;
}else
g_current=g_current->next;
return g_current;
*/
}
void pmparser_free(procmaps_iterator* p_procmaps_it){
procmaps_struct* maps_list = p_procmaps_it->head;
if(maps_list==NULL) return ;
procmaps_struct* act=maps_list;
procmaps_struct* nxt=act->next;
while(act!=NULL){
free(act);
act=nxt;
if(nxt!=NULL)
nxt=nxt->next;
}
}
void _pmparser_split_line(
char*buf,char*addr1,char*addr2,
char*perm,char* offset,char* device,char*inode,
char* pathname){
//
int orig=0;
int i=0;
//addr1
while(buf[i]!='-'){
addr1[i-orig]=buf[i];
i++;
}
addr1[i]='\0';
i++;
//addr2
orig=i;
while(buf[i]!='\t' && buf[i]!=' '){
addr2[i-orig]=buf[i];
i++;
}
addr2[i-orig]='\0';
//perm
while(buf[i]=='\t' || buf[i]==' ')
i++;
orig=i;
while(buf[i]!='\t' && buf[i]!=' '){
perm[i-orig]=buf[i];
i++;
}
perm[i-orig]='\0';
//offset
while(buf[i]=='\t' || buf[i]==' ')
i++;
orig=i;
while(buf[i]!='\t' && buf[i]!=' '){
offset[i-orig]=buf[i];
i++;
}
offset[i-orig]='\0';
//dev
while(buf[i]=='\t' || buf[i]==' ')
i++;
orig=i;
while(buf[i]!='\t' && buf[i]!=' '){
device[i-orig]=buf[i];
i++;
}
device[i-orig]='\0';
//inode
while(buf[i]=='\t' || buf[i]==' ')
i++;
orig=i;
while(buf[i]!='\t' && buf[i]!=' '){
inode[i-orig]=buf[i];
i++;
}
inode[i-orig]='\0';
//pathname
pathname[0]='\0';
while(buf[i]=='\t' || buf[i]==' ')
i++;
orig=i;
while(buf[i]!='\t' && buf[i]!=' ' && buf[i]!='\n'){
pathname[i-orig]=buf[i];
i++;
}
pathname[i-orig]='\0';
}
#endif

View File

@ -9,7 +9,8 @@
TCG instrumentation and block chaining support by Andrea Biondo TCG instrumentation and block chaining support by Andrea Biondo
<andrea.biondo965@gmail.com> <andrea.biondo965@gmail.com>
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
<andreafioraldi@gmail.com> <andreafioraldi@gmail.com>
Copyright 2015, 2016, 2017 Google Inc. All rights reserved. Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
@ -65,6 +66,8 @@ abi_ulong afl_entry_point, /* ELF entry point (_start) */
afl_start_code, /* .text start pointer */ afl_start_code, /* .text start pointer */
afl_end_code; /* .text end pointer */ afl_end_code; /* .text end pointer */
u8 afl_enable_compcov;
/* Set in the child process in forkserver mode: */ /* Set in the child process in forkserver mode: */
static int forkserver_installed = 0; static int forkserver_installed = 0;
@ -147,7 +150,6 @@ static void afl_setup(void) {
if (inst_r) afl_area_ptr[0] = 1; if (inst_r) afl_area_ptr[0] = 1;
} }
if (getenv("AFL_INST_LIBS")) { if (getenv("AFL_INST_LIBS")) {
@ -157,6 +159,11 @@ static void afl_setup(void) {
} }
if (getenv("AFL_QEMU_COMPCOV")) {
afl_enable_compcov = 1;
}
/* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm /* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
not entirely sure what is the cause. This disables that not entirely sure what is the cause. This disables that
behaviour, and seems to work alright? */ behaviour, and seems to work alright? */

View File

@ -0,0 +1,125 @@
/*
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.
TCG instrumentation and block chaining support by Andrea Biondo
<andrea.biondo965@gmail.com>
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
<andreafioraldi@gmail.com>
Copyright 2015, 2016, 2017 Google Inc. 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
This code is a shim patched into the separately-distributed source
code of QEMU 3.1.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 "../../config.h"
#include "tcg.h"
#include "tcg-op.h"
/* Declared in afl-qemu-cpu-inl.h */
extern unsigned char *afl_area_ptr;
extern unsigned int afl_inst_rms;
extern abi_ulong afl_start_code, afl_end_code;
extern u8 afl_enable_compcov;
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
TCGv_i64 arg1, TCGv_i64 arg2);
static void afl_compcov_log_16(target_ulong cur_loc, target_ulong arg1,
target_ulong arg2) {
if ((arg1 & 0xff) == (arg2 & 0xff)) {
afl_area_ptr[cur_loc]++;
}
}
static void afl_compcov_log_32(target_ulong cur_loc, target_ulong arg1,
target_ulong arg2) {
if ((arg1 & 0xff) == (arg2 & 0xff)) {
afl_area_ptr[cur_loc]++;
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
afl_area_ptr[cur_loc +1]++;
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
afl_area_ptr[cur_loc +2]++;
}
}
}
}
static void afl_compcov_log_64(target_ulong cur_loc, target_ulong arg1,
target_ulong arg2) {
if ((arg1 & 0xff) == (arg2 & 0xff)) {
afl_area_ptr[cur_loc]++;
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
afl_area_ptr[cur_loc +1]++;
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
afl_area_ptr[cur_loc +2]++;
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffff)) {
afl_area_ptr[cur_loc +3]++;
if ((arg1 & 0xffffffffff) == (arg2 & 0xffffffffff)) {
afl_area_ptr[cur_loc +4]++;
if ((arg1 & 0xffffffffffff) == (arg2 & 0xffffffffffff)) {
afl_area_ptr[cur_loc +5]++;
if ((arg1 & 0xffffffffffffff) == (arg2 & 0xffffffffffffff)) {
afl_area_ptr[cur_loc +6]++;
}
}
}
}
}
}
}
}
static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
TCGMemOp ot) {
void *func;
if (!afl_enable_compcov || cur_loc > afl_end_code || cur_loc < afl_start_code)
return;
switch (ot) {
case MO_64:
func = &afl_compcov_log_64;
break;
case MO_32:
func = &afl_compcov_log_32;
break;
case MO_16:
func = &afl_compcov_log_16;
break;
default:
return;
}
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
cur_loc &= MAP_SIZE - 1;
if (cur_loc >= afl_inst_rms) return;
tcg_gen_afl_compcov_log_call(func, cur_loc, arg1, arg2);
}

View File

@ -9,7 +9,8 @@
TCG instrumentation and block chaining support by Andrea Biondo TCG instrumentation and block chaining support by Andrea Biondo
<andrea.biondo965@gmail.com> <andrea.biondo965@gmail.com>
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
<andreafioraldi@gmail.com> <andreafioraldi@gmail.com>
Copyright 2015, 2016, 2017 Google Inc. All rights reserved. Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
@ -42,10 +43,10 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
unsigned sizemask, flags; unsigned sizemask, flags;
TCGOp *op; TCGOp *op;
TCGTemp *arg = tcgv_ptr_temp( tcg_const_tl(cur_loc) ); TCGTemp *arg = tcgv_i64_temp( tcg_const_tl(cur_loc) );
flags = 0; flags = 0;
sizemask = dh_sizemask(void, 0) | dh_sizemask(ptr, 1); sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1);
#if defined(__sparc__) && !defined(__arch64__) \ #if defined(__sparc__) && !defined(__arch64__) \
&& !defined(CONFIG_TCG_INTERPRETER) && !defined(CONFIG_TCG_INTERPRETER)
@ -151,7 +152,7 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them. /* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
Note that describing these as TCGv_i64 eliminates an unnecessary Note that describing these as TCGv_i64 eliminates an unnecessary
zero-extension that tcg_gen_concat_i32_i64 would create. */ zero-extension that tcg_gen_concat_i32_i64 would create. */
tcg_gen_concat32_i64(temp_tcgv_i64(ret), retl, reth); tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
tcg_temp_free_i64(retl); tcg_temp_free_i64(retl);
tcg_temp_free_i64(reth); tcg_temp_free_i64(reth);
} }
@ -163,3 +164,143 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
#endif /* TCG_TARGET_EXTEND_ARGS */ #endif /* TCG_TARGET_EXTEND_ARGS */
} }
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2)
{
int i, real_args, nb_rets, pi;
unsigned sizemask, flags;
TCGOp *op;
const int nargs = 3;
TCGTemp *args[3] = { tcgv_i64_temp( tcg_const_tl(cur_loc) ),
tcgv_i64_temp(arg1),
tcgv_i64_temp(arg2) };
flags = 0;
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1) |
dh_sizemask(i64, 2) | dh_sizemask(i64, 3);
#if defined(__sparc__) && !defined(__arch64__) \
&& !defined(CONFIG_TCG_INTERPRETER)
/* We have 64-bit values in one register, but need to pass as two
separate parameters. Split them. */
int orig_sizemask = sizemask;
int orig_nargs = nargs;
TCGv_i64 retl, reth;
TCGTemp *split_args[MAX_OPC_PARAM];
retl = NULL;
reth = NULL;
if (sizemask != 0) {
for (i = real_args = 0; i < nargs; ++i) {
int is_64bit = sizemask & (1 << (i+1)*2);
if (is_64bit) {
TCGv_i64 orig = temp_tcgv_i64(args[i]);
TCGv_i32 h = tcg_temp_new_i32();
TCGv_i32 l = tcg_temp_new_i32();
tcg_gen_extr_i64_i32(l, h, orig);
split_args[real_args++] = tcgv_i32_temp(h);
split_args[real_args++] = tcgv_i32_temp(l);
} else {
split_args[real_args++] = args[i];
}
}
nargs = real_args;
args = split_args;
sizemask = 0;
}
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
for (i = 0; i < nargs; ++i) {
int is_64bit = sizemask & (1 << (i+1)*2);
int is_signed = sizemask & (2 << (i+1)*2);
if (!is_64bit) {
TCGv_i64 temp = tcg_temp_new_i64();
TCGv_i64 orig = temp_tcgv_i64(args[i]);
if (is_signed) {
tcg_gen_ext32s_i64(temp, orig);
} else {
tcg_gen_ext32u_i64(temp, orig);
}
args[i] = tcgv_i64_temp(temp);
}
}
#endif /* TCG_TARGET_EXTEND_ARGS */
op = tcg_emit_op(INDEX_op_call);
pi = 0;
nb_rets = 0;
TCGOP_CALLO(op) = nb_rets;
real_args = 0;
for (i = 0; i < nargs; i++) {
int is_64bit = sizemask & (1 << (i+1)*2);
if (TCG_TARGET_REG_BITS < 64 && is_64bit) {
#ifdef TCG_TARGET_CALL_ALIGN_ARGS
/* some targets want aligned 64 bit args */
if (real_args & 1) {
op->args[pi++] = TCG_CALL_DUMMY_ARG;
real_args++;
}
#endif
/* If stack grows up, then we will be placing successive
arguments at lower addresses, which means we need to
reverse the order compared to how we would normally
treat either big or little-endian. For those arguments
that will wind up in registers, this still works for
HPPA (the only current STACK_GROWSUP target) since the
argument registers are *also* allocated in decreasing
order. If another such target is added, this logic may
have to get more complicated to differentiate between
stack arguments and register arguments. */
#if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP)
op->args[pi++] = temp_arg(args[i] + 1);
op->args[pi++] = temp_arg(args[i]);
#else
op->args[pi++] = temp_arg(args[i]);
op->args[pi++] = temp_arg(args[i] + 1);
#endif
real_args += 2;
continue;
}
op->args[pi++] = temp_arg(args[i]);
real_args++;
}
op->args[pi++] = (uintptr_t)func;
op->args[pi++] = flags;
TCGOP_CALLI(op) = real_args;
/* Make sure the fields didn't overflow. */
tcg_debug_assert(TCGOP_CALLI(op) == real_args);
tcg_debug_assert(pi <= ARRAY_SIZE(op->args));
#if defined(__sparc__) && !defined(__arch64__) \
&& !defined(CONFIG_TCG_INTERPRETER)
/* Free all of the parts we allocated above. */
for (i = real_args = 0; i < orig_nargs; ++i) {
int is_64bit = orig_sizemask & (1 << (i+1)*2);
if (is_64bit) {
tcg_temp_free_internal(args[real_args++]);
tcg_temp_free_internal(args[real_args++]);
} else {
real_args++;
}
}
if (orig_sizemask & 1) {
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
Note that describing these as TCGv_i64 eliminates an unnecessary
zero-extension that tcg_gen_concat_i32_i64 would create. */
tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
tcg_temp_free_i64(retl);
tcg_temp_free_i64(reth);
}
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
for (i = 0; i < nargs; ++i) {
int is_64bit = sizemask & (1 << (i+1)*2);
if (!is_64bit) {
tcg_temp_free_internal(args[i]);
}
}
#endif /* TCG_TARGET_EXTEND_ARGS */
}

View File

@ -9,7 +9,8 @@
TCG instrumentation and block chaining support by Andrea Biondo TCG instrumentation and block chaining support by Andrea Biondo
<andrea.biondo965@gmail.com> <andrea.biondo965@gmail.com>
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
<andreafioraldi@gmail.com> <andreafioraldi@gmail.com>
Copyright 2015, 2016, 2017 Google Inc. All rights reserved. Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
@ -41,12 +42,12 @@ extern abi_ulong afl_start_code, afl_end_code;
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc); void tcg_gen_afl_maybe_log_call(target_ulong cur_loc);
void afl_maybe_log(void* cur_loc) { void afl_maybe_log(target_ulong cur_loc) {
static __thread abi_ulong prev_loc; static __thread abi_ulong prev_loc;
afl_area_ptr[(abi_ulong)cur_loc ^ prev_loc]++; afl_area_ptr[cur_loc ^ prev_loc]++;
prev_loc = (abi_ulong)cur_loc >> 1; prev_loc = cur_loc >> 1;
} }
@ -59,7 +60,7 @@ static void afl_gen_trace(target_ulong cur_loc) {
if (cur_loc > afl_end_code || cur_loc < afl_start_code /*|| !afl_area_ptr*/) // not needed because of static dummy buffer if (cur_loc > afl_end_code || cur_loc < afl_start_code /*|| !afl_area_ptr*/) // not needed because of static dummy buffer
return; return;
/* Looks like QEMU always maps to fixed locations, so ASAN is not a /* Looks like QEMU always maps to fixed locations, so ASLR is not a
concern. Phew. But instruction addresses may be aligned. Let's mangle concern. Phew. But instruction addresses may be aligned. Let's mangle
the value to get something quasi-uniform. */ the value to get something quasi-uniform. */

View File

@ -0,0 +1,33 @@
diff --git a/target/i386/translate.c b/target/i386/translate.c
index 0dd5fbe4..b95d341e 100644
--- a/target/i386/translate.c
+++ b/target/i386/translate.c
@@ -32,6 +32,8 @@
#include "trace-tcg.h"
#include "exec/log.h"
+#include "../patches/afl-qemu-cpu-translate-inl.h"
+
#define PREFIX_REPZ 0x01
#define PREFIX_REPNZ 0x02
#define PREFIX_LOCK 0x04
@@ -1343,9 +1345,11 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d)
tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0,
s1->mem_index, ot | MO_LE);
tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1);
+ afl_gen_compcov(s1->pc, s1->cc_srcT, s1->T1, ot);
} else {
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1);
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot);
gen_op_st_rm_T0_A0(s1, ot, d);
}
gen_op_update2_cc(s1);
@@ -1389,6 +1393,7 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d)
tcg_gen_mov_tl(cpu_cc_src, s1->T1);
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1);
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot);
set_cc_op(s1, CC_OP_SUBB + ot);
break;
}