QEMU-Nyx-fork/nyx/page_cache.c
Steffen Schulz 5c24050a64 page_cache: use file lock also for read access
Without this there may be a risk of reading partially written
files...doesn't seem to happen in practice though?
2022-02-11 10:45:30 -08:00

554 lines
16 KiB
C

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <capstone/capstone.h>
#include <capstone/x86.h>
#include <sys/file.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <assert.h>
#include "page_cache.h"
#include "debug.h"
#ifndef STANDALONE_DECODER
#include "cpu.h"
#include "memory_access.h"
#include "fast_vm_reload.h"
#include "kvm_nested.h"
#include "nyx/state/state.h"
#endif
#define PAGE_CACHE_ADDR_LINE_SIZE sizeof(uint64_t)
#define UNMAPPED_PAGE 0xFFFFFFFFFFFFFFFFULL
static void page_cache_unlock(page_cache_t* self);
static void page_cache_lock(page_cache_t* self);
#ifndef STANDALONE_DECODER
static bool reload_addresses(page_cache_t* self){
#else
bool reload_addresses(page_cache_t* self){
#endif
khiter_t k;
int ret;
uint64_t addr, offset;
uint64_t value = 0;
size_t self_offset = lseek(self->fd_address_file, 0, SEEK_END);
if(self_offset != self->num_pages*PAGE_CACHE_ADDR_LINE_SIZE){
//fprintf(stderr, "Reloading files ...\n");
page_cache_lock(self); // don't read while someone else is writing?
lseek(self->fd_address_file, self->num_pages*PAGE_CACHE_ADDR_LINE_SIZE, SEEK_SET);
offset = self->num_pages;
while(read(self->fd_address_file, &value, PAGE_CACHE_ADDR_LINE_SIZE)){
addr = value & 0xFFFFFFFFFFFFF000ULL;
offset++;
/* put new addresses and offsets into the hash map */
k = kh_get(PC_CACHE, self->lookup, addr);
if(k == kh_end(self->lookup)){
if(value & 0xFFF){
fprintf(stderr, "Load page: %lx (UMAPPED)\n", addr);
//k = kh_put(PC_CACHE, self->lookup, addr, &ret);
//kh_value(self->lookup, k) = UNMAPPED_PAGE;
}
else{
//fprintf(stderr, "Load page: %lx\n", addr);
k = kh_put(PC_CACHE, self->lookup, addr, &ret);
kh_value(self->lookup, k) = (offset-1)*PAGE_SIZE;
}
/*
k = kh_put(PC_CACHE, self->lookup, addr, &ret);
kh_value(self->lookup, k) = (offset-1)*PAGE_SIZE;
*/
}
else{
fprintf(stderr, "----------> Page duplicate found ...skipping! %lx\n", addr);
/* should not be possible ... */
//abort();
}
}
//fprintf(stderr, "Old Value: %d - New Value: %ld\n", self->num_pages, (uint32_t)self_offset/PAGE_CACHE_ADDR_LINE_SIZE);
/* reload page dump file */
munmap(self->page_data, self->num_pages*PAGE_SIZE);
self->num_pages = self_offset/PAGE_CACHE_ADDR_LINE_SIZE;
self->page_data = mmap(NULL, (self->num_pages)*PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd_page_file, 0);
page_cache_unlock(self);
return true;
}
return false;
}
#ifndef STANDALONE_DECODER
static bool append_page(page_cache_t* self, uint64_t page, uint64_t cr3){
bool success = true;
if(!self->num_pages){
assert(!ftruncate(self->fd_page_file, (self->num_pages+1)*PAGE_SIZE));
self->page_data = mmap(NULL, (self->num_pages+1)*PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd_page_file, 0);
}
else{
munmap(self->page_data, self->num_pages*PAGE_SIZE);
assert(!ftruncate(self->fd_page_file, (self->num_pages+1)*PAGE_SIZE));
self->page_data = mmap(NULL, (self->num_pages+1)*PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd_page_file, 0);
}
//if(!dump_page_cr3_snapshot(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu, GET_GLOBAL_STATE()->pt_c3_filter)){
// if(!dump_page_cr3_snapshot(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu, GET_GLOBAL_STATE()->parent_cr3)){
if(!dump_page_cr3_ht(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu, GET_GLOBAL_STATE()->pt_c3_filter)){
if(!dump_page_cr3_ht(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu, GET_GLOBAL_STATE()->parent_cr3)){
if(!dump_page_cr3_snapshot(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu, GET_GLOBAL_STATE()->parent_cr3)){
//fprintf(stderr, "FAILED DUMP PROCESS of PAGE %lx\n", page);
//memset(self->page_data+(PAGE_SIZE*self->num_pages), 0xff, PAGE_SIZE);
munmap(self->page_data, (self->num_pages+1)*PAGE_SIZE);
assert(!ftruncate(self->fd_page_file, (self->num_pages)*PAGE_SIZE));
self->page_data = mmap(NULL, (self->num_pages)*PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd_page_file, 0);
//qemu_backtrace();
success = false;
return success;
//assert(false);
}
}
}
//}
/*
if(!dump_page_cr3_ht(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu, GET_GLOBAL_STATE()->parent_cr3)){
fprintf(stderr, "FAILED DUMP PROCESS of PAGE %lx\n", page);
assert(false);
}
*/
/*
//fast_loadvm();
if(cr3){
dump_page_cr3_ht(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu, GET_GLOBAL_STATE()->parent_cr3); //self->cpu->parent_cr3);
//assert(dump_page_cr3_snapshot(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu, GET_GLOBAL_STATE()->parent_cr3)); //self->cpu->parent_cr3);
//read_virtual_memory_cr3(page, self->page_data+(PAGE_SIZE*self->num_pages), PAGE_SIZE, self->cpu, self->cpu->parent_cr3);
}
else{
dump_page_ht(page, self->page_data+(PAGE_SIZE*self->num_pages), self->cpu);
//read_virtual_memory(page, self->page_data+(PAGE_SIZE*self->num_pages), PAGE_SIZE, self->cpu);
}
*/
fsync(self->fd_page_file);
self->num_pages++;
return success;
}
#else
bool append_page(page_cache_t* self, uint64_t page, uint8_t* ptr){
self->last_page = 0xFFFFFFFFFFFFFFFF;
self->last_addr = 0xFFFFFFFFFFFFFFFF;
page &= 0xFFFFFFFFFFFFF000ULL;
bool success = true;
if(!self->num_pages){
assert(!ftruncate(self->fd_page_file, (self->num_pages+1)*PAGE_SIZE));
self->page_data = mmap(NULL, (self->num_pages+1)*PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd_page_file, 0);
}
else{
munmap(self->page_data, self->num_pages*PAGE_SIZE);
assert(!ftruncate(self->fd_page_file, (self->num_pages+1)*PAGE_SIZE));
self->page_data = mmap(NULL, (self->num_pages+1)*PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd_page_file, 0);
}
memcpy(self->page_data+(PAGE_SIZE*self->num_pages), ptr, PAGE_SIZE);
fsync(self->fd_page_file);
int ret;
khiter_t k;
k = kh_put(PC_CACHE, self->lookup, page, &ret);
kh_value(self->lookup, k) = self->num_pages*PAGE_SIZE;
assert(write(self->fd_address_file, &page, PAGE_CACHE_ADDR_LINE_SIZE) == PAGE_CACHE_ADDR_LINE_SIZE);
self->num_pages++;
return success;
}
#endif
static void page_cache_lock(page_cache_t* self){
#ifndef STANDALONE_DECODER
int ret = 0;
while (true){
ret = flock(self->fd_lock, LOCK_EX);
if (ret == 0){
return;
}
else if (ret == EINTR){
/* try again if acquiring this lock has failed */
fprintf(stderr, "%s: interrupted by signal...\n", __func__);
}
else{
assert(false);
}
}
#endif
}
static void page_cache_unlock(page_cache_t* self){
#ifndef STANDALONE_DECODER
int ret = 0;
while (true){
ret = flock(self->fd_lock, LOCK_UN);
if (ret == 0){
return;
}
else if (ret == EINTR){
/* try again if releasing this lock has failed */
fprintf(stderr, "%s: interrupted by signal...\n", __func__);
}
else{
assert(false);
}
}
#endif
}
static bool update_page_cache(page_cache_t* self, uint64_t page, khiter_t* k){
//#define DEBUG_PAGE_CACHE_LOCK
page_cache_lock(self);
#ifdef DEBUG_PAGE_CACHE_LOCK
fprintf(stderr, "%d: LOCKING PAGE CACHE\n", getpid());
#endif
if(reload_addresses(self)){
*k = kh_get(PC_CACHE, self->lookup, page);
}
if(*k == kh_end(self->lookup)){
#ifndef STANDALONE_DECODER
int ret;
uint64_t cr3 = GET_GLOBAL_STATE()->parent_cr3; //self->cpu->parent_cr3;
if(!is_addr_mapped_cr3_snapshot(page, self->cpu, GET_GLOBAL_STATE()->parent_cr3) && !is_addr_mapped_cr3_snapshot(page, self->cpu, GET_GLOBAL_STATE()->pt_c3_filter)){ //self->cpu->parent_cr3)){
//fprintf(stderr, "PAGE NOT FOUND in SNAPSHOT %lx\n", page);
//assert(false);
}
*k = kh_get(PC_CACHE, self->lookup, page);
if(*k == kh_end(self->lookup) && reload_addresses(self)){
/* reload sucessful */
*k = kh_get(PC_CACHE, self->lookup, page);
}
else{
if(append_page(self, page, cr3)){
*k = kh_put(PC_CACHE, self->lookup, page, &ret);
assert(write(self->fd_address_file, &page, PAGE_CACHE_ADDR_LINE_SIZE) == PAGE_CACHE_ADDR_LINE_SIZE);
kh_value(self->lookup, *k) = (self->num_pages-1)*PAGE_SIZE;
}
else{
//fprintf(stderr, "Fail!!!!\n");
page_cache_unlock(self);
return false;
/*
uint64_t new_page = page | 0xFFF;
assert(write(self->fd_address_file, &new_page, PAGE_CACHE_ADDR_LINE_SIZE) == PAGE_CACHE_ADDR_LINE_SIZE);
kh_value(self->lookup, *k) = UNMAPPED_PAGE;
fprintf(stderr, "APPEND UNMAPPED PAGE %lx!\n", page);
*/
}
*k = kh_get(PC_CACHE, self->lookup, page);
}
#else
//printf("PAGE NOT FOUND: %lx! ABORTING\n", page);
page_cache_unlock(self);
return false;
abort();
#endif
}
#ifdef DEBUG_PAGE_CACHE_LOCK
fprintf(stderr, "%d: UNLOCKING PAGE CACHE\n", getpid());
#endif
page_cache_unlock(self);
return true;
}
uint64_t page_cache_fetch(page_cache_t* self, uint64_t page, bool* success, bool test_mode){
page &= 0xFFFFFFFFFFFFF000ULL;
/*
if(test_mode){
*success = false;
return 0;
}
*/
//if(page == 0x7ffca45b5000)
// return UNMAPPED_PAGE;
//printf("%s %lx\n", __func__, page);
//if (page == 0x0434000)
// return 0;
if (self->last_page == page){
*success = true;
return self->last_addr;
}
//QEMU_PT_PRINTF(PAGE_CACHE_PREFIX, "page_cache_fetch %lx", page);
khiter_t k;
k = kh_get(PC_CACHE, self->lookup, page);
if(k == kh_end(self->lookup)){
if(test_mode || update_page_cache(self, page, &k) == false){
//fprintf(stderr, "%s: fail!\n", __func__);
*success = false;
//abort();
return 0;
}
}
self->last_page = page;
//fprintf(stderr, "[%d]\tkh_n_buckets: %d %d\n", getpid(), kh_n_buckets(self->lookup), k);
if(kh_value(self->lookup, k) == UNMAPPED_PAGE){
self->last_addr = UNMAPPED_PAGE;
}
else{
self->last_addr = (uint64_t)self->page_data+kh_value(self->lookup, k);
}
//fprintf(stderr, "try to unlock flock!\n");
//fprintf(stderr, "flock unlocked!\n");
*success = true;
return self->last_addr;
}
/* fix this */
uint64_t page_cache_fetch2(page_cache_t* self, uint64_t page, bool* success){
return page_cache_fetch(self, page, success, false);
}
#ifndef STANDALONE_DECODER
page_cache_t* page_cache_new(CPUState *cpu, const char* cache_file){
#else
page_cache_t* page_cache_new(const char* cache_file, uint8_t disassembler_word_width){
#endif
page_cache_t* self = malloc(sizeof(page_cache_t));
char* tmp1;
char* tmp2;
char* tmp3;
assert(asprintf(&tmp1, "%s.dump", cache_file) != -1);
assert(asprintf(&tmp2, "%s.addr", cache_file) != -1);
assert(asprintf(&tmp3, "%s.lock", cache_file) != -1);
self->lookup = kh_init(PC_CACHE);
self->fd_page_file = open(tmp1, O_CLOEXEC | O_CREAT | O_RDWR, 0644);
self->fd_address_file = open(tmp2, O_CLOEXEC | O_CREAT | O_RDWR, 0644);
#ifndef STANDALONE_DECODER
self->cpu = cpu;
self->fd_lock = open(tmp3, O_CLOEXEC | O_CREAT, 0644);
assert(self->fd_lock > 0);
#else
if(self->fd_page_file == -1 || self->fd_address_file == -1){
printf("[ ] Page cache files not found...\n");
exit(1);
}
#endif
memset(self->disassemble_cache, 0x0, 16);
self->page_data = NULL;
self->num_pages = 0;
self->last_page = 0xFFFFFFFFFFFFFFFF;
self->last_addr = 0xFFFFFFFFFFFFFFFF;
#ifndef STANDALONE_DECODER
QEMU_PT_PRINTF(PAGE_CACHE_PREFIX, "%s (%s - %s)", __func__, tmp1, tmp2);
#else
QEMU_PT_PRINTF(PAGE_CACHE_PREFIX, "%s (%s - %s) WORD_WIDTH: %d", __func__, tmp1, tmp2, disassembler_word_width);
#endif
free(tmp3);
free(tmp2);
free(tmp1);
if (cs_open(CS_ARCH_X86, CS_MODE_16, &self->handle_16) != CS_ERR_OK)
assert(false);
if (cs_open(CS_ARCH_X86, CS_MODE_32, &self->handle_32) != CS_ERR_OK)
assert(false);
if (cs_open(CS_ARCH_X86, CS_MODE_64, &self->handle_64) != CS_ERR_OK)
assert(false);
cs_option(self->handle_16, CS_OPT_DETAIL, CS_OPT_ON);
cs_option(self->handle_32, CS_OPT_DETAIL, CS_OPT_ON);
cs_option(self->handle_64, CS_OPT_DETAIL, CS_OPT_ON);
return self;
}
#ifdef STANDALONE_DECODER
void page_cache_destroy(page_cache_t* self){
munmap(self->page_data, self->num_pages * 0x1000);
kh_destroy(PC_CACHE, self->lookup);
cs_close(&self->handle_16);
cs_close(&self->handle_32);
cs_close(&self->handle_64);
free(self);
}
#endif
/*
static bool page_cache_load(uint64_t virtual_addr){
return true;
}
*/
/*
static bool page_cache_load_cr3(uint64_t virtual_addr, uint64_t cr3){
return true;
}
*/
bool page_cache_disassemble(page_cache_t* self, uint64_t address, cs_insn **insn){
return true;
}
cs_insn* page_cache_cs_malloc(page_cache_t* self, disassembler_mode_t mode){
switch(mode){
case mode_16:
return cs_malloc(self->handle_16);
case mode_32:
return cs_malloc(self->handle_32);
case mode_64:
return cs_malloc(self->handle_64);
default:
assert(false);
}
return NULL;
}
//#define EXPERIMENTAL_PAGE_FETCH
bool page_cache_disassemble_iter(page_cache_t* self, uint64_t* address, cs_insn *insn, uint64_t* failed_page, disassembler_mode_t mode){
//printf("%s %lx\n", __func__, *address);
*failed_page = 0xFFFFFFFFFFFFFFFFULL;
bool success = true;
size_t code_size = 16;
#if defined(STANDALONE_DECODER) || !defined(EXPERIMENTAL_PAGE_FETCH)
uint8_t* code = (uint8_t*)page_cache_fetch(self, *address, &success, false);
#else
uint8_t* code = (uint8_t*)page_cache_fetch(self, *address, &success, true);
#endif
uint8_t* code_ptr = 0;
//disassembler_mode_t mode = mode_16;
csh* current_handle = NULL;
switch(mode){
case mode_16:
current_handle = &self->handle_16;
break;
case mode_32:
current_handle = &self->handle_32;
break;
case mode_64:
current_handle = &self->handle_64;
break;
default:
assert(false);
}
if (code == (void*)UNMAPPED_PAGE || success == false){
*failed_page = *address;// & 0xFFFFFFFFFFFFF000ULL;
//printf("FAIL???? (0x%lx) %lx %d\n", *address, code, success);
return false;
}
if ((*address & 0xFFF) >= (0x1000-16)){
//printf("-------------> Disassemble between pages...%lx (%lx %lx %lx)\n", *address, (*address&0xFFF), (0x1000-16), 0xf-(0xfff-(*address&0xfff)));
memcpy((void*)self->disassemble_cache, (void*)((uint64_t)code+(0x1000-16)), 16);
code_ptr = self->disassemble_cache + 0xf-(0xfff-(*address&0xfff));
#if defined(STANDALONE_DECODER) || !defined(EXPERIMENTAL_PAGE_FETCH)
code = (uint8_t*)page_cache_fetch(self, *address+0x1000, &success, false);
#else
code = (uint8_t*)page_cache_fetch(self, *address+0x1000, &success, true);
#endif
/* broken AF */
if(success == true){
//printf("=> A\n");
//*failed_page = (*address+0x1000) & 0xFFFFFFFFFFFFF000ULL;
//return false;
//printf("=> %lx %lx\n", (0xfff-(*address&0xfff)), *address);
memcpy((void*)(self->disassemble_cache+16), (void*)code, 16);
//code_size = 16;
return cs_disasm_iter(*current_handle, (const uint8_t**) &code_ptr, &code_size, address, insn);
}
else{
//printf("=> B\n");
code_size = (0xfff-(*address&0xfff));
//printf("%lx\n", code_size);
//abort();
//*failed_page = *address;
if(!cs_disasm_iter(*current_handle, (const uint8_t**) &code_ptr, &code_size, address, insn)){
*failed_page = (*address+0x1000) & 0xFFFFFFFFFFFFF000ULL;
//fprintf(stderr, "%s FAIL: %lx %lx\n", __func__, *address, *failed_page);
//if(*address != 0x555555554ffe && *address != 0x7ffff7478ffc && *address != 0x7ffff7820ff6 && *address != 0x7ffff7822ffa)
// abort();
return false;
}
return true;
//return cs_disasm_iter(self->handle, (const uint8_t**) &code_ptr, &code_size, address, insn);
}
}
else {
//printf("=> C\n");
code_ptr = code + (*address&0xFFF);
//printf("Disassemble...(%lx %x)\n", code_ptr, *code_ptr);
return cs_disasm_iter(*current_handle, (const uint8_t**) &code_ptr, &code_size, address, insn);
}
}