
- replace several hardcoded fprintf() - use of warn vs error or nyx_abort() - several more missing newlines - fixed up messages
571 lines
17 KiB
C
571 lines
17 KiB
C
/*
|
|
|
|
Copyright (C) 2017 Sergej Schumilo
|
|
|
|
This file is part of QEMU-PT (HyperTrash / kAFL).
|
|
|
|
QEMU-PT is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QEMU-PT is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QEMU-PT. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
|
|
#include <stdint.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <immintrin.h>
|
|
|
|
#include "block/qapi.h"
|
|
#include "exec/ram_addr.h"
|
|
|
|
#include "migration/global_state.h"
|
|
#include "migration/migration.h"
|
|
#include "migration/qemu-file.h"
|
|
#include "migration/register.h"
|
|
#include "migration/savevm.h"
|
|
#include "migration/vmstate.h"
|
|
|
|
#include "qemu/main-loop.h"
|
|
#include "qemu/rcu_queue.h"
|
|
|
|
#include "sysemu/block-backend.h"
|
|
#include "sysemu/cpus.h"
|
|
#include "sysemu/kvm_int.h"
|
|
#include "sysemu/reset.h"
|
|
#include "sysemu/runstate.h"
|
|
#include "sysemu/sysemu.h"
|
|
|
|
#include "nyx/debug.h"
|
|
#include "nyx/fast_vm_reload.h"
|
|
#include "nyx/state/snapshot_state.h"
|
|
#include "nyx/state/state.h"
|
|
|
|
#include "nyx/helpers.h"
|
|
#include "nyx/memory_access.h"
|
|
|
|
#include "nyx/snapshot/block/nyx_block_snapshot.h"
|
|
#include "nyx/snapshot/devices/nyx_device_state.h"
|
|
#include "nyx/snapshot/helper.h"
|
|
#include "nyx/snapshot/memory/backend/nyx_debug.h"
|
|
#include "nyx/snapshot/memory/block_list.h"
|
|
#include "nyx/snapshot/memory/shadow_memory.h"
|
|
|
|
FastReloadMemoryMode mode = RELOAD_MEMORY_MODE_DEBUG;
|
|
|
|
/* basic operations */
|
|
|
|
static void fast_snapshot_init_operation(fast_reload_t *self,
|
|
const char *snapshot_folder,
|
|
bool pre_snapshot)
|
|
{
|
|
assert((snapshot_folder == NULL && pre_snapshot == false) || snapshot_folder);
|
|
|
|
if (snapshot_folder) {
|
|
self->device_state =
|
|
nyx_device_state_init_from_snapshot(snapshot_folder, pre_snapshot);
|
|
self->shadow_memory_state =
|
|
shadow_memory_init_from_snapshot(snapshot_folder, pre_snapshot);
|
|
} else {
|
|
self->device_state = nyx_device_state_init();
|
|
self->shadow_memory_state = shadow_memory_init();
|
|
}
|
|
|
|
if (!pre_snapshot) {
|
|
switch (mode) {
|
|
case RELOAD_MEMORY_MODE_DEBUG:
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DEBUG_QUIET:
|
|
break;
|
|
case RELOAD_MEMORY_MODE_FDL:
|
|
self->fdl_state = nyx_fdl_init(self->shadow_memory_state);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_FDL_DEBUG:
|
|
self->fdl_state = nyx_fdl_init(self->shadow_memory_state);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DIRTY_RING:
|
|
self->dirty_ring_state = nyx_dirty_ring_init(self->shadow_memory_state);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DIRTY_RING_DEBUG:
|
|
self->dirty_ring_state = nyx_dirty_ring_init(self->shadow_memory_state);
|
|
break;
|
|
}
|
|
|
|
self->fdl_user_state = nyx_fdl_user_init(self->shadow_memory_state);
|
|
nyx_fdl_user_enable(self->fdl_user_state);
|
|
}
|
|
|
|
if (snapshot_folder) {
|
|
self->block_state =
|
|
nyx_block_snapshot_init_from_file(snapshot_folder, pre_snapshot);
|
|
} else {
|
|
self->block_state = nyx_block_snapshot_init();
|
|
}
|
|
|
|
memory_global_dirty_log_start();
|
|
if (!pre_snapshot) {
|
|
self->root_snapshot_created = true;
|
|
}
|
|
}
|
|
|
|
static void fast_snapshot_restore_operation(fast_reload_t *self)
|
|
{
|
|
uint32_t num_dirty_pages = 0;
|
|
|
|
switch (mode) {
|
|
case RELOAD_MEMORY_MODE_DEBUG:
|
|
num_dirty_pages += nyx_snapshot_debug_restore(self->shadow_memory_state,
|
|
self->blocklist, true);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DEBUG_QUIET:
|
|
num_dirty_pages += nyx_snapshot_debug_restore(self->shadow_memory_state,
|
|
self->blocklist, false);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_FDL:
|
|
num_dirty_pages += nyx_snapshot_nyx_fdl_restore(self->fdl_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_FDL_DEBUG:
|
|
num_dirty_pages += nyx_snapshot_nyx_fdl_restore(self->fdl_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
num_dirty_pages += nyx_snapshot_debug_restore(self->shadow_memory_state,
|
|
self->blocklist, true);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DIRTY_RING:
|
|
num_dirty_pages +=
|
|
nyx_snapshot_nyx_dirty_ring_restore(self->dirty_ring_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DIRTY_RING_DEBUG:
|
|
num_dirty_pages +=
|
|
nyx_snapshot_nyx_dirty_ring_restore(self->dirty_ring_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
num_dirty_pages += nyx_snapshot_debug_restore(self->shadow_memory_state,
|
|
self->blocklist, true);
|
|
break;
|
|
}
|
|
|
|
num_dirty_pages += nyx_snapshot_user_fdl_restore(self->fdl_user_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
GET_GLOBAL_STATE()->num_dirty_pages = num_dirty_pages;
|
|
}
|
|
|
|
static inline void fast_snapshot_pre_create_incremental_operation(fast_reload_t *self)
|
|
{
|
|
/* flush all pending block writes */
|
|
bdrv_drain_all();
|
|
memory_global_dirty_log_sync();
|
|
|
|
nyx_device_state_switch_incremental(self->device_state);
|
|
nyx_block_snapshot_switch_incremental(self->block_state);
|
|
}
|
|
|
|
static inline void fast_snapshot_create_incremental_operation(fast_reload_t *self)
|
|
{
|
|
shadow_memory_prepare_incremental(self->shadow_memory_state);
|
|
nyx_device_state_save_tsc_incremental(self->device_state);
|
|
|
|
switch (mode) {
|
|
case RELOAD_MEMORY_MODE_DEBUG:
|
|
nyx_snapshot_debug_save_root_pages(self->shadow_memory_state,
|
|
self->blocklist, true);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DEBUG_QUIET:
|
|
nyx_snapshot_debug_save_root_pages(self->shadow_memory_state,
|
|
self->blocklist, false);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_FDL:
|
|
nyx_snapshot_nyx_fdl_save_root_pages(self->fdl_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_FDL_DEBUG:
|
|
nyx_snapshot_nyx_fdl_save_root_pages(self->fdl_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
nyx_snapshot_debug_save_root_pages(self->shadow_memory_state,
|
|
self->blocklist, true);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DIRTY_RING:
|
|
nyx_snapshot_nyx_dirty_ring_save_root_pages(self->dirty_ring_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
break;
|
|
case RELOAD_MEMORY_MODE_DIRTY_RING_DEBUG:
|
|
nyx_snapshot_nyx_dirty_ring_save_root_pages(self->dirty_ring_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
nyx_snapshot_debug_save_root_pages(self->shadow_memory_state,
|
|
self->blocklist, true);
|
|
break;
|
|
}
|
|
|
|
nyx_snapshot_nyx_fdl_user_save_root_pages(self->fdl_user_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
shadow_memory_switch_snapshot(self->shadow_memory_state, true);
|
|
|
|
kvm_arch_put_registers(qemu_get_cpu(0), KVM_PUT_FULL_STATE_FAST);
|
|
qemu_get_cpu(0)->vcpu_dirty = false;
|
|
}
|
|
|
|
|
|
fast_reload_t *fast_reload_new(void)
|
|
{
|
|
fast_reload_t *self = malloc(sizeof(fast_reload_t));
|
|
memset(self, 0x0, sizeof(fast_reload_t));
|
|
|
|
self->root_snapshot_created = false;
|
|
self->incremental_snapshot_enabled = false;
|
|
|
|
self->bitmap_copy = NULL;
|
|
|
|
return self;
|
|
}
|
|
|
|
void fast_reload_set_mode(fast_reload_t *self, FastReloadMemoryMode m)
|
|
{
|
|
assert(!self->root_snapshot_created);
|
|
mode = m;
|
|
}
|
|
|
|
FastReloadMemoryMode fast_reload_get_mode(fast_reload_t *self)
|
|
{
|
|
return mode;
|
|
}
|
|
|
|
void fast_reload_init(fast_reload_t *self)
|
|
{
|
|
self->blocklist = snapshot_page_blocklist_init();
|
|
}
|
|
|
|
/* fix this */
|
|
void fast_reload_destroy(fast_reload_t *self)
|
|
{
|
|
/* TODO: complete me */
|
|
|
|
// close(self->vmx_fdl_fd);
|
|
// munmap(self->fdl_data, (self->guest_ram_size/0x1000)*8);
|
|
|
|
/*
|
|
munmap(self->ptr, self->guest_ram_size);
|
|
|
|
free(self->black_list_pages);
|
|
|
|
free(self);
|
|
*/
|
|
}
|
|
|
|
inline static void unlock_snapshot(const char *folder)
|
|
{
|
|
char *info_file;
|
|
char *lock_file;
|
|
|
|
/* info file */
|
|
assert(asprintf(&info_file, "%s/INFO.txt", folder) != -1);
|
|
FILE *f_info = fopen(info_file, "w+b");
|
|
if (GET_GLOBAL_STATE()->fast_reload_pre_image) {
|
|
const char *msg = "THIS IS A NYX PRE IMAGE SNAPSHOT FOLDER!\n";
|
|
fwrite(msg, strlen(msg), 1, f_info);
|
|
} else {
|
|
const char *msg = "THIS IS A NYX SNAPSHOT FOLDER!\n";
|
|
fwrite(msg, strlen(msg), 1, f_info);
|
|
}
|
|
fclose(f_info);
|
|
|
|
assert(asprintf(&lock_file, "%s/ready.lock", folder) != -1);
|
|
int fd = open(lock_file, O_WRONLY | O_CREAT, S_IRWXU);
|
|
close(fd);
|
|
|
|
free(lock_file);
|
|
}
|
|
|
|
inline static void wait_for_snapshot(const char *folder)
|
|
{
|
|
char *lock_file;
|
|
|
|
assert(asprintf(&lock_file, "%s/ready.lock", folder) != -1);
|
|
while (access(lock_file, F_OK) == -1) {
|
|
sleep(1);
|
|
}
|
|
free(lock_file);
|
|
}
|
|
|
|
void fast_reload_serialize_to_file(fast_reload_t *self,
|
|
const char *folder,
|
|
bool is_pre_snapshot)
|
|
{
|
|
nyx_trace();
|
|
|
|
/* sanity check */
|
|
if (!folder_exits(folder)) {
|
|
nyx_error("Folder %s does not exist. Abort.\n", folder);
|
|
assert(0);
|
|
}
|
|
|
|
/* shadow memory state */
|
|
shadow_memory_serialize(self->shadow_memory_state, folder);
|
|
|
|
/* device state */
|
|
nyx_device_state_serialize(self->device_state, folder);
|
|
|
|
/* block device state */
|
|
nyx_block_snapshot_serialize(self->block_state, folder);
|
|
|
|
/* NYX's state */
|
|
serialize_state(folder, is_pre_snapshot);
|
|
|
|
/* finalize snapshot */
|
|
unlock_snapshot(folder);
|
|
}
|
|
|
|
|
|
static void fast_reload_create_from_snapshot(fast_reload_t *self,
|
|
const char *folder,
|
|
bool lock_iothread,
|
|
bool pre_snapshot)
|
|
{
|
|
nyx_trace();
|
|
|
|
assert(self != NULL);
|
|
wait_for_snapshot(folder);
|
|
|
|
nyx_debug_p(RELOAD_PREFIX,
|
|
"=> CREATING FAST RELOAD SNAPSHOT FROM DUMP (location: %s)\n", folder);
|
|
|
|
rcu_read_lock();
|
|
|
|
bdrv_drain_all();
|
|
bdrv_flush_all();
|
|
|
|
cpu_synchronize_all_pre_loadvm();
|
|
|
|
if (!pre_snapshot) {
|
|
memory_global_dirty_log_stop();
|
|
memory_global_dirty_log_sync();
|
|
}
|
|
|
|
fast_snapshot_init_operation(self, folder, pre_snapshot);
|
|
|
|
rcu_read_unlock();
|
|
|
|
if (!pre_snapshot) {
|
|
deserialize_state(folder);
|
|
}
|
|
|
|
cpu_synchronize_all_post_init();
|
|
qemu_get_cpu(0)->vcpu_dirty = true;
|
|
kvm_arch_put_registers(qemu_get_cpu(0), KVM_PUT_FULL_STATE);
|
|
if (!pre_snapshot) {
|
|
nyx_device_state_save_tsc(self->device_state);
|
|
}
|
|
}
|
|
|
|
void fast_reload_create_from_file(fast_reload_t *self,
|
|
const char *folder,
|
|
bool lock_iothread)
|
|
{
|
|
nyx_trace();
|
|
fast_reload_create_from_snapshot(self, folder, lock_iothread, false);
|
|
}
|
|
|
|
void fast_reload_create_from_file_pre_image(fast_reload_t *self,
|
|
const char *folder,
|
|
bool lock_iothread)
|
|
{
|
|
nyx_trace();
|
|
fast_reload_create_from_snapshot(self, folder, lock_iothread, true);
|
|
}
|
|
|
|
void fast_reload_create_in_memory(fast_reload_t *self)
|
|
{
|
|
nyx_trace();
|
|
assert(self != NULL);
|
|
|
|
rcu_read_lock();
|
|
|
|
bdrv_drain_all();
|
|
bdrv_flush_all();
|
|
|
|
cpu_synchronize_all_pre_loadvm();
|
|
|
|
memory_global_dirty_log_stop();
|
|
memory_global_dirty_log_sync();
|
|
|
|
fast_snapshot_init_operation(self, NULL, false);
|
|
|
|
rcu_read_unlock();
|
|
cpu_synchronize_all_post_init();
|
|
}
|
|
|
|
void fast_reload_restore(fast_reload_t *self)
|
|
{
|
|
assert(self != NULL);
|
|
self->dirty_pages = 0;
|
|
|
|
/* flush all pending block writes */
|
|
bdrv_drain_all();
|
|
memory_global_dirty_log_sync();
|
|
|
|
nyx_block_snapshot_reset(self->block_state);
|
|
|
|
nyx_device_state_restore(self->device_state);
|
|
|
|
nyx_block_snapshot_flush(self->block_state);
|
|
|
|
fast_snapshot_restore_operation(self);
|
|
|
|
nyx_device_state_post_restore(self->device_state);
|
|
kvm_arch_put_registers(qemu_get_cpu(0), KVM_PUT_FULL_STATE_FAST);
|
|
qemu_get_cpu(0)->vcpu_dirty = false;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
bool read_snapshot_memory(fast_reload_t *self, uint64_t address, void *ptr, size_t size)
|
|
{
|
|
return shadow_memory_read_physical_memory(self->shadow_memory_state, address,
|
|
ptr, size);
|
|
}
|
|
|
|
/* fix this */
|
|
void *fast_reload_get_physmem_shadow_ptr(fast_reload_t *self, uint64_t physaddr)
|
|
{
|
|
abort(); /* TODO: fix this function first -> pc_piix memory split issue */
|
|
|
|
/*
|
|
assert(self != NULL);
|
|
assert(!(physaddr&0xFFF)); // physaddr must be 4kb align !
|
|
if (self->shadow_memory_regions){
|
|
for(uint64_t j = 0; j < self->shadow_memory_regions; j++){
|
|
if(physaddr >= self->ram_block_array[j]->offset && physaddr <
|
|
(self->ram_block_array[j]->offset+self->ram_block_array[j]->used_length)){ return
|
|
self->shadow_memory[j]+(physaddr-self->ram_block_array[j]->offset);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
return NULL; // not found ... sorry :(
|
|
}
|
|
|
|
void fast_reload_blacklist_page(fast_reload_t *self, uint64_t physaddr)
|
|
{
|
|
assert(self->blocklist);
|
|
snapshot_page_blocklist_add(self->blocklist, physaddr);
|
|
}
|
|
|
|
bool fast_reload_snapshot_exists(fast_reload_t *self)
|
|
{
|
|
if (!self) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void fast_reload_create_tmp_snapshot(fast_reload_t *self)
|
|
{
|
|
assert(self);
|
|
|
|
self->dirty_pages = 0;
|
|
fast_snapshot_pre_create_incremental_operation(self);
|
|
|
|
if (!self->bitmap_copy) {
|
|
self->bitmap_copy = new_coverage_bitmaps();
|
|
}
|
|
coverage_bitmap_copy_to_buffer(self->bitmap_copy);
|
|
|
|
fast_snapshot_create_incremental_operation(self);
|
|
self->incremental_snapshot_enabled = true;
|
|
}
|
|
|
|
void fast_reload_discard_tmp_snapshot(fast_reload_t *self)
|
|
{
|
|
assert(self && self->incremental_snapshot_enabled);
|
|
|
|
self->dirty_pages = 0;
|
|
|
|
/* flush all pending block writes */
|
|
bdrv_drain_all();
|
|
memory_global_dirty_log_sync();
|
|
|
|
fast_snapshot_restore_operation(self);
|
|
|
|
shadow_memory_restore_memory(self->shadow_memory_state);
|
|
shadow_memory_switch_snapshot(self->shadow_memory_state, false);
|
|
|
|
nyx_device_state_disable_incremental(self->device_state);
|
|
|
|
nyx_block_snapshot_disable_incremental(self->block_state);
|
|
|
|
self->incremental_snapshot_enabled = false;
|
|
}
|
|
|
|
bool fast_reload_root_created(fast_reload_t *self)
|
|
{
|
|
return self->root_snapshot_created;
|
|
}
|
|
|
|
bool fast_reload_tmp_created(fast_reload_t *self)
|
|
{
|
|
return self->incremental_snapshot_enabled;
|
|
}
|
|
|
|
uint32_t get_dirty_page_num(fast_reload_t *self)
|
|
{
|
|
if (self) {
|
|
return self->dirty_pages;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool fast_reload_set_bitmap(fast_reload_t *self)
|
|
{
|
|
if (self->incremental_snapshot_enabled) {
|
|
coverage_bitmap_copy_from_buffer(self->bitmap_copy);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void fast_reload_qemu_user_fdl_set_dirty(fast_reload_t *self,
|
|
MemoryRegion *mr,
|
|
uint64_t addr,
|
|
uint64_t length)
|
|
{
|
|
/* works only with PC.RAM's memory region */
|
|
assert(mr->alias_offset == 0);
|
|
|
|
nyx_fdl_user_set(self->fdl_user_state, self->shadow_memory_state,
|
|
self->fdl_state, addr, length);
|
|
}
|
|
|
|
void fast_reload_handle_dirty_ring_full(fast_reload_t *self)
|
|
{
|
|
if (self->dirty_ring_state) {
|
|
nyx_snapshot_nyx_dirty_ring_flush_and_collect(self->dirty_ring_state,
|
|
self->shadow_memory_state,
|
|
self->blocklist);
|
|
} else {
|
|
nyx_snapshot_nyx_dirty_ring_flush();
|
|
}
|
|
}
|