add AFL++ support and other improvements

This commit is contained in:
Sergej Schumilo 2021-12-18 14:30:24 +01:00
parent c9dac7704d
commit 18ad4753d3
12 changed files with 58 additions and 51 deletions

View File

@ -1917,19 +1917,19 @@ static int kvm_init(MachineState *ms)
}
#ifdef QEMU_NYX
if (ioctl(s->fd, KVM_CHECK_EXTENSION, KVM_CAP_NYX_PT) != 1 && ioctl(s->fd, KVM_CHECK_EXTENSION, KVM_CAP_NYX_FDL) != 1) {
fprintf(stderr, "[!] Could not access KVM-PT kernel module!\n [*] Trying vanilla KVM...\n");
fprintf(stderr, "[QEMU-Nyx] Could not access KVM-PT kernel module!\n[QEMU-Nyx] Trying vanilla KVM...\n");
/* fallback -> use vanilla KVM module instead (no Intel-PT tracing or nested hypercalls at this point) */
s->fd = qemu_open("/dev/kvm", O_RDWR);
if (s->fd == -1) {
fprintf(stderr, "Error: NYX fallback failed: Could not access vanilla KVM module!\n");
fprintf(stderr, "[QEMU-Nyx] Error: NYX fallback failed: Could not access vanilla KVM module!\n");
ret = -errno;
goto err;
}
int ret_val = ioctl(s->fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING);
if(ret_val == -1 || ret_val == 0){
fprintf(stderr, "Error: NYX requires support for KVM_CAP_DIRTY_LOG_RING in fallback mode!\n");
fprintf(stderr, "[QEMU-Nyx] Error: NYX requires support for KVM_CAP_DIRTY_LOG_RING in fallback mode!\n");
ret = -errno;
goto err;
}
@ -1947,7 +1947,7 @@ static int kvm_init(MachineState *ms)
close(fd);
if(vmware_backdoor_option == 'N'){
fprintf(stderr, "\nERROR: vmware backdoor is not enabled...\n");
fprintf(stderr, "\n[QEMU-Nyx] ERROR: vmware backdoor is not enabled...\n");
fprintf(stderr, "\n\tRun the following commands to fix the issue:\n");
fprintf(stderr, "\t-----------------------------------------\n");
fprintf(stderr, "\tsudo modprobe -r kvm-intel\n");
@ -1960,9 +1960,10 @@ static int kvm_init(MachineState *ms)
goto err;
}
fprintf(stderr, "NYX runs in fallback mode (no Intel-PT tracing or nested hypercall support)!\n");
fprintf(stderr, "[QEMU-Nyx] NYX runs in fallback mode (no Intel-PT tracing or nested hypercall support)!\n");
s->nyx_no_pt_mode = true;
GET_GLOBAL_STATE()->nyx_fdl = false;
GET_GLOBAL_STATE()->pt_trace_mode = false; // Intel PT is not available in this mode
fast_reload_set_mode(get_fast_reload_snapshot(), RELOAD_MEMORY_MODE_DIRTY_RING);
}
else{

View File

@ -231,6 +231,12 @@ void set_crash_reason_auxiliary_buffer(auxilary_buffer_t* auxilary_buffer, char*
VOLATILE_WRITE_8(auxilary_buffer->result.crash_found, 1);
}
void set_abort_reason_auxiliary_buffer(auxilary_buffer_t* auxilary_buffer, char* msg, uint32_t len){
VOLATILE_WRITE_16(auxilary_buffer->misc.len, MIN(len, MISC_SIZE-2));
volatile_memcpy((void*)&auxilary_buffer->misc.data, (void*)msg, (size_t) MIN(len, MISC_SIZE-2));
VOLATILE_WRITE_8(auxilary_buffer->result.abort, 1);
}
void flush_hprintf_auxiliary_buffer(auxilary_buffer_t* auxilary_buffer){
VOLATILE_WRITE_8(auxilary_buffer->result.hprintf, 0);
}

View File

@ -27,7 +27,7 @@ along with QEMU-PT. If not, see <http://www.gnu.org/licenses/>.
#define AUX_MAGIC 0x54502d554d4551
#define QEMU_PT_VERSION 1 /* let's start at 1 for the initial version using the aux buffer */
#define QEMU_PT_VERSION 2 /* let's start at 1 for the initial version using the aux buffer */
#define HEADER_SIZE 128
#define CAP_SIZE 256
@ -123,7 +123,8 @@ typedef struct auxilary_buffer_result_s{
uint32_t dirty_pages;
uint32_t pt_trace_size;
uint8_t payload_buffer_write_attempt_found;
uint8_t payload_buffer_write_attempt_found;
uint8_t abort;
/* more to come */
} __attribute__((packed)) auxilary_buffer_result_t;
@ -170,6 +171,7 @@ void set_hprintf_auxiliary_buffer(auxilary_buffer_t* auxilary_buffer, char* msg,
void set_page_not_found_result_buffer(auxilary_buffer_t* auxilary_buffer, uint64_t page_addr);
void set_success_auxiliary_result_buffer(auxilary_buffer_t* auxilary_buffer, uint8_t success);
void set_crash_reason_auxiliary_buffer(auxilary_buffer_t* auxilary_buffer, char* msg, uint32_t len);
void set_abort_reason_auxiliary_buffer(auxilary_buffer_t* auxilary_buffer, char* msg, uint32_t len);
void set_tmp_snapshot_created(auxilary_buffer_t* auxilary_buffer, uint8_t value);

View File

@ -73,6 +73,10 @@ void apply_capabilities(CPUState *cpu){
debug_printf("GET_GLOBAL_STATE()->cap_cr3: %lx\n", GET_GLOBAL_STATE()->cap_cr3);
debug_printf("--------------------------\n");
if(GET_GLOBAL_STATE()->cap_compile_time_tracing_buffer_vaddr&0xfff){
fprintf(stderr, "[QEMU-Nyx] Warning: guest's trace bitmap v_addr (0x%lx) is not page aligned!\n", GET_GLOBAL_STATE()->cap_compile_time_tracing_buffer_vaddr);
}
for(uint64_t i = 0; i < GET_GLOBAL_STATE()->shared_bitmap_size; i += 0x1000){
assert(remap_slot(GET_GLOBAL_STATE()->cap_compile_time_tracing_buffer_vaddr+ i, i/0x1000, cpu, GET_GLOBAL_STATE()->shared_bitmap_fd, GET_GLOBAL_STATE()->shared_bitmap_size, true, GET_GLOBAL_STATE()->cap_cr3));
}
@ -82,6 +86,10 @@ void apply_capabilities(CPUState *cpu){
if(GET_GLOBAL_STATE()->cap_ijon_tracing){
debug_fprintf(stderr, "%s: agent trace buffer at vaddr: %lx\n", __func__, GET_GLOBAL_STATE()->cap_ijon_tracing_buffer_vaddr);
if(GET_GLOBAL_STATE()->cap_ijon_tracing_buffer_vaddr&0xfff){
fprintf(stderr, "[QEMU-Nyx] Warning: guest's ijon buffer v_addr (0x%lx) is not page aligned!\n", GET_GLOBAL_STATE()->cap_ijon_tracing_buffer_vaddr);
}
kvm_arch_get_registers_fast(cpu);
for(uint64_t i = 0; i < GET_GLOBAL_STATE()->shared_ijon_bitmap_size; i += 0x1000){
assert(remap_slot(GET_GLOBAL_STATE()->cap_ijon_tracing_buffer_vaddr + i, (GET_GLOBAL_STATE()->shared_bitmap_size+i)/0x1000, cpu, GET_GLOBAL_STATE()->shared_bitmap_fd, GET_GLOBAL_STATE()->shared_bitmap_size+GET_GLOBAL_STATE()->shared_ijon_bitmap_size, true, GET_GLOBAL_STATE()->cap_cr3));

View File

@ -121,6 +121,14 @@ bool handle_hypercall_kafl_next_payload(struct kvm_run *run, CPUState *cpu, uint
} else {
if(!setup_snapshot_once){
//pt_reset_bitmap();
if (GET_GLOBAL_STATE()->pt_trace_mode){
fprintf(stderr, "[QEMU-Nyx] coverage mode: Intel-PT (KVM-Nyx and libxdc)\n");
}
else{
fprintf(stderr, "[QEMU-Nyx] coverage mode: compile-time instrumentation\n");
}
fuzz_bitmap_reset();
request_fast_vm_reload(GET_GLOBAL_STATE()->reload_state, REQUEST_SAVE_SNAPSHOT_ROOT_FIX_RIP);
setup_snapshot_once = true;
@ -206,7 +214,7 @@ static void handle_hypercall_get_payload(struct kvm_run *run, CPUState *cpu, uin
//print_48_paging2(GET_GLOBAL_STATE()->parent_cr3);
if(hypercall_arg&0xFFF){
fprintf(stderr, "Error: Payload buffer is not page-aligned! (0x%lx)\n", hypercall_arg);
fprintf(stderr, "[QEMU-Nyx] Error: Payload buffer is not page-aligned! (0x%lx)\n", hypercall_arg);
abort();
}
@ -295,7 +303,7 @@ static void handle_hypercall_kafl_range_submit(struct kvm_run *run, CPUState *cp
}
if(GET_GLOBAL_STATE()->pt_ip_filter_configured[buffer[2]]){
QEMU_PT_PRINTF(CORE_PREFIX, "Ignoring agent-provided address ranges (abort reason: 1)");
QEMU_PT_PRINTF(CORE_PREFIX, "Ignoring agent-provided address ranges (abort reason: 1) - %d", buffer[2]);
return;
}
@ -304,7 +312,7 @@ static void handle_hypercall_kafl_range_submit(struct kvm_run *run, CPUState *cp
GET_GLOBAL_STATE()->pt_ip_filter_b[buffer[2]] = buffer[1];
GET_GLOBAL_STATE()->pt_ip_filter_configured[buffer[2]] = true;
QEMU_PT_PRINTF(CORE_PREFIX, "Configuring agent-provided address ranges:");
QEMU_PT_PRINTF(CORE_PREFIX, "\tIP0: %lx-%lx [ENABLED]", GET_GLOBAL_STATE()->pt_ip_filter_a[buffer[2]], GET_GLOBAL_STATE()->pt_ip_filter_b[buffer[2]]);
QEMU_PT_PRINTF(CORE_PREFIX, "\tIP%d: %lx-%lx [ENABLED]", buffer[2], GET_GLOBAL_STATE()->pt_ip_filter_a[buffer[2]], GET_GLOBAL_STATE()->pt_ip_filter_b[buffer[2]]);
}
else{
QEMU_PT_PRINTF(CORE_PREFIX, "Ignoring agent-provided address ranges (abort reason: 2)");
@ -916,17 +924,8 @@ bool handle_hypercall_kafl_hook(struct kvm_run *run, CPUState *cpu, uint64_t hyp
static void handle_hypercall_kafl_user_abort(struct kvm_run *run, CPUState *cpu, uint64_t hypercall_arg){
read_virtual_memory(hypercall_arg, (uint8_t*)hprintf_buffer, HPRINTF_SIZE, cpu);
fprintf(stderr, "%s: %s\n", __func__, hprintf_buffer);
abort();
if(hypercall_enabled){
//hypercall_snd_char(KAFL_PROTO_PT_ABORT);
QEMU_PT_PRINTF_DEBUG("Protocol - SEND: KAFL_PROTO_PT_ABORT");
}
debug_fprintf(stderr, "USER ABORT!\n");
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
set_abort_reason_auxiliary_buffer(GET_GLOBAL_STATE()->auxilary_buffer, hprintf_buffer, strlen(hprintf_buffer));
synchronization_lock();
}
void pt_enable_rqi(CPUState *cpu){

View File

@ -215,6 +215,12 @@ bool remap_slot(uint64_t addr, uint32_t slot, CPUState *cpu, int fd, uint64_t sh
if(virtual){
phys_addr = get_paging_phys_addr(cpu, cr3, (addr & x86_64_PAGE_MASK));
if(phys_addr == (uint64_t)-1){
fprintf(stderr, "[QEMU-Nyx] Error: failed to translate v_addr (0x%lx) to p_addr!\n", addr);
fprintf(stderr, "[QEMU-Nyx] Check if the buffer is present in the guest's memory...\n");
exit(1);
}
phys_addr = address_to_ram_offset(phys_addr);
}

View File

@ -382,7 +382,7 @@ page_cache_t* page_cache_new(const char* cache_file, uint8_t disassembler_word_w
self->last_page = 0xFFFFFFFFFFFFFFFF;
self->last_addr = 0xFFFFFFFFFFFFFFFF;
QEMU_PT_PRINTF(PAGE_CACHE_PREFIX, "%s (%s - %s) WORD_WIDTH: %d", __func__, tmp1, tmp2, disassembler_word_width);
QEMU_PT_PRINTF(PAGE_CACHE_PREFIX, "%s (%s - %s)", __func__, tmp1, tmp2);
free(tmp3);
free(tmp2);

View File

@ -31,7 +31,7 @@ static int vm_enable_dirty_ring(int vm_fd, uint32_t ring_size){
int ret = ioctl(vm_fd, KVM_ENABLE_CAP, &cap);
if(ret != 0){
printf("[ ] KVM_ENABLE_CAP ioctl failed\n");
printf("[QEMU-Nyx] Error: KVM_ENABLE_CAP ioctl failed\n");
}
return ring_size;
@ -40,11 +40,11 @@ static int vm_enable_dirty_ring(int vm_fd, uint32_t ring_size){
static int check_dirty_ring_size(int kvm_fd, int vm_fd){
int ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING);
if(ret < 0 ){
printf("[ ] KVM_CAP_DIRTY_LOG_RING failed (dirty ring not supported?)\n");
printf("[QEMU-Nyx] Error: KVM_CAP_DIRTY_LOG_RING failed (dirty ring not supported?)\n");
exit(1);
}
printf("[*] Max Dirty Ring Size -> %d (Entries: %d)\n", ret, ret/(int)sizeof(struct kvm_dirty_gfn));
printf("[QEMU-Nyx] Max Dirty Ring Size -> %d (Entries: %d)\n", ret, ret/(int)sizeof(struct kvm_dirty_gfn));
uint64_t dirty_ring_max_size = ret; //kvm_dirty_ring_size * sizeof(struct kvm_dirty_gfn);
@ -52,7 +52,7 @@ static int check_dirty_ring_size(int kvm_fd, int vm_fd){
ret = vm_enable_dirty_ring(vm_fd, dirty_ring_max_size);
if(ret < 0 ){
printf("[ ] Enabling dirty ring (size: %ld) failed\n", dirty_ring_max_size);
printf("[QEMU-Nyx] Error: Enabling dirty ring (size: %ld) failed\n", dirty_ring_max_size);
exit(1);
}
@ -66,11 +66,11 @@ static void allocate_dirty_ring(int kvm_vcpu, int vm_fd){
if (dirty_ring_size) {
kvm_dirty_gfns = mmap(NULL, dirty_ring_size, PROT_READ | PROT_WRITE, MAP_SHARED, kvm_vcpu, PAGE_SIZE * KVM_DIRTY_LOG_PAGE_OFFSET);
if (kvm_dirty_gfns == MAP_FAILED) {
printf("[ ] Dirty ring mmap failed!\n");
printf("[QEMU-Nyx] Error: Dirty ring mmap failed!\n");
exit(1);
}
}
printf("[*] Dirty ring mmap region located at %p\n", kvm_dirty_gfns);
printf("[QEMU-Nyx] Dirty ring mmap region located at %p\n", kvm_dirty_gfns);
int ret = ioctl(vm_fd, KVM_RESET_DIRTY_RINGS, 0);
assert(ret == 0);
@ -107,7 +107,6 @@ static void dirty_ring_flush_and_collect(nyx_dirty_ring_t* self, shadow_memory_t
struct kvm_dirty_gfn *entry = NULL;
int cleared = 0;
//fprintf(stderr, "self->kvm_dirty_gfns_index -> %lx\n", kvm_dirty_gfns_index);
while(true){
entry = &kvm_dirty_gfns[kvm_dirty_gfns_index & kvm_dirty_gfns_index_mask];
@ -122,7 +121,7 @@ static void dirty_ring_flush_and_collect(nyx_dirty_ring_t* self, shadow_memory_t
entry->flags |= 0x2; // reset dirty entry
}
else{
printf("[%p] kvm_dirty_gfn -> flags: %d slot: %d offset: %lx {ERROR}\n", entry, entry->flags, entry->slot, entry->offset);
printf("[QEMU-Nyx] [%p] kvm_dirty_gfn -> flags: %d slot: %d offset: %lx {ERROR}\n", entry, entry->flags, entry->slot, entry->offset);
fflush(stdout);
exit(1);
}
@ -131,7 +130,6 @@ static void dirty_ring_flush_and_collect(nyx_dirty_ring_t* self, shadow_memory_t
}
int ret = ioctl(vm_fd, KVM_RESET_DIRTY_RINGS, 0);
//printf("KVM_RESET_DIRTY_RINGS -> (%d vs %d)\n", ret, cleared);
assert(ret == cleared);
}
@ -139,7 +137,6 @@ static void dirty_ring_flush(int vm_fd){
struct kvm_dirty_gfn *entry = NULL;
int cleared = 0;
//printf("self->kvm_dirty_gfns_index -> %lx\n", self->kvm_dirty_gfns_index);
while(true){
entry = &kvm_dirty_gfns[kvm_dirty_gfns_index & kvm_dirty_gfns_index_mask];
@ -153,7 +150,7 @@ static void dirty_ring_flush(int vm_fd){
entry->flags |= 0x2; // reset dirty entry
}
else{
printf("[%p] kvm_dirty_gfn -> flags: %d slot: %d offset: %lx {ERROR}\n", entry, entry->flags, entry->slot, entry->offset);
printf("[QEMU-Nyx] [%p] kvm_dirty_gfn -> flags: %d slot: %d offset: %lx {ERROR}\n", entry, entry->flags, entry->slot, entry->offset);
fflush(stdout);
exit(1);
}
@ -162,8 +159,7 @@ static void dirty_ring_flush(int vm_fd){
}
int ret = ioctl(vm_fd, KVM_RESET_DIRTY_RINGS, 0);
//printf("KVM_RESET_DIRTY_RINGS -> (%d vs %ld)\n", ret, cleared);
assert(ret == cleared);
assert(ret == cleared);
}
/* init operation */
@ -177,8 +173,6 @@ nyx_dirty_ring_t* nyx_dirty_ring_init(shadow_memory_t* shadow_memory){
KVMMemoryListener *kml = kvm_get_kml(0);
KVMSlot *mem;
//printf("kml -> %p\n", kml);
//printf("MEM-SLOTS -> %d\n", kvm_get_max_memslots());
for (int i = 0; i < kvm_get_max_memslots(); i++) {
mem = &kml->slots[i];
@ -186,17 +180,9 @@ nyx_dirty_ring_t* nyx_dirty_ring_init(shadow_memory_t* shadow_memory){
break;
}
//printf("[%p] SLOT: %d - start: %lx - size: %lx - flags: %x\n", mem, mem->slot, mem->start_addr, mem->memory_size, mem->flags);
self->kvm_region_slots_num++;
}
/*
for(int i = 0; i < shadow_memory->ram_regions_num; i++){
printf("[%d] base: %lx - size: %lx\n", i, shadow_memory->ram_regions[i].base, shadow_memory->ram_regions[i].size);
}
*/
self->kvm_region_slots = malloc(sizeof(slot_t) * self->kvm_region_slots_num);
memset(self->kvm_region_slots, 0, sizeof(slot_t) * self->kvm_region_slots_num);
@ -220,7 +206,6 @@ nyx_dirty_ring_t* nyx_dirty_ring_init(shadow_memory_t* shadow_memory){
if(self->kvm_region_slots[i].enabled){
bool ram_region_found = false;
//printf("SEARCHING %lx %lx\n", mem->start_addr, mem->memory_size);
for(int j = 0; j < shadow_memory->ram_regions_num; j++){
if(FAST_IN_RANGE(mem->start_addr, shadow_memory->ram_regions[j].base, (shadow_memory->ram_regions[j].base+shadow_memory->ram_regions[j].size))){

View File

@ -77,7 +77,7 @@ struct state_qemu_pt{
/* Intel PT Options (not migratable) */
uint64_t pt_c3_filter;
volatile bool pt_ip_filter_enabled[4];
bool pt_trace_mode; // enable by default; disabled if compile-time tracing is implemented by agent
bool pt_trace_mode; // enabled by default; disabled if compile-time tracing is implemented by agent
/* disabled by default; enable to force usage of PT tracing
* (useful for targets that use compile-time tracing and redqueen at the same time (which obviously relies on PT traces))

View File

@ -439,7 +439,7 @@ void synchronization_cow_full_detected(void){
void synchronization_disable_pt(CPUState *cpu){
//fprintf(stderr, "==============> %s\n", __func__);
if(!in_fuzzing_loop){
fprintf(stderr, "<%d-%ld>\t%s [NOT IN FUZZING LOOP]\n", getpid(), run_counter, __func__);
//fprintf(stderr, "<%d-%ld>\t%s [NOT IN FUZZING LOOP]\n", getpid(), run_counter, __func__);
set_success_auxiliary_result_buffer(GET_GLOBAL_STATE()->auxilary_buffer, 0);
/*
qemu_backtrace();

View File

@ -4485,11 +4485,11 @@ static void x86_cpuid_set_model_id(Object *obj, const char *model_id,
}
#ifdef QEMU_NYX
if(strncmp(model_id, NYX_PT_CPU_MODEL, strlen(NYX_PT_CPU_MODEL)) == 0 && GET_GLOBAL_STATE()->nyx_fdl == false){
fprintf(stderr, "Warning: Attempt to use unsupported CPU model (PT) without KVM-PT (Hint: use '-cpu kAFL64-Hypervisor-v2' instead)\n");
fprintf(stderr, "[QEMU-Nyx] Warning: Attempt to use unsupported CPU model (PT) without KVM-PT (Hint: use '-cpu kAFL64-Hypervisor-v2' instead)\n");
model_id = NYX_NO_PT_CPU_MODEL;
}
if(strncmp(model_id, NYX_NO_PT_CPU_MODEL, strlen(NYX_NO_PT_CPU_MODEL)) == 0 && GET_GLOBAL_STATE()->nyx_fdl == true){
fprintf(stderr, "Error: Attempt to use unsupported CPU model (NO-PT) with KVM-PT (Hint: use '-cpu kAFL64-Hypervisor-v1' instead)\n");
fprintf(stderr, "[QEMU-Nyx] Error: Attempt to use unsupported CPU model (NO-PT) with KVM-PT (Hint: use '-cpu kAFL64-Hypervisor-v1' instead)\n");
exit(1);
}
#endif

2
vl.c
View File

@ -4652,7 +4652,7 @@ int main(int argc, char **argv, char **envp)
//GET_GLOBAL_STATE()->pt_trace_mode = false;
}
else{
fprintf(stderr, "[!] qemu-nyx: Booting to start fuzzing...\n");
fprintf(stderr, "[QEMU-Nyx] Booting VM to start fuzzing...\n");
set_fast_reload_mode(false);
}
}