aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2025-09-30 16:12:17 +0200
committerAlexander Potapenko <glider@google.com>2025-10-17 06:51:20 +0000
commit2606b77513573de02745daefe9de925c9494cfff (patch)
tree4e4d4856049ef76553068e598ad46d053fdb87d5 /executor
parent6f255ae2c60c113dc8eeae689561e439791cc502 (diff)
executor: refactor x86 SYZOS setup
Pass around struct kvm_syzos_vm instead of one-off pointers to various guest memory ranges.
Diffstat (limited to 'executor')
-rw-r--r--executor/common_kvm_amd64.h129
-rw-r--r--executor/kvm.h3
2 files changed, 67 insertions, 65 deletions
diff --git a/executor/common_kvm_amd64.h b/executor/common_kvm_amd64.h
index cdf36b714..44e209650 100644
--- a/executor/common_kvm_amd64.h
+++ b/executor/common_kvm_amd64.h
@@ -202,12 +202,14 @@ static void setup_64bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t g
}
#endif
-#if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu || __NR_syz_kvm_setup_syzos_vm
+#if SYZ_EXECUTOR || __NR_syz_kvm_setup_syzos_vm || __NR_syz_kvm_add_vcpu
// Flags for mem_region
#define MEM_REGION_FLAG_USER_CODE (1 << 0)
#define MEM_REGION_FLAG_DIRTY_LOG (1 << 1)
#define MEM_REGION_FLAG_READONLY (1 << 2)
#define MEM_REGION_FLAG_EXECUTOR_CODE (1 << 3)
+#define MEM_REGION_FLAG_GPA0 (1 << 5)
+#define MEM_REGION_FLAG_NO_HOST_MEM (1 << 6)
struct mem_region {
uint64 gpa;
@@ -215,18 +217,40 @@ struct mem_region {
uint32 flags;
};
+// SYZOS guest virtual memory layout (must be in sync with executor/kvm.h):
static const struct mem_region syzos_mem_regions[] = {
- {X86_SYZOS_ADDR_ZERO, 20, 0},
+ // AMD64 data structures (20 pages starting at GPA 0x0, see kvm.h).
+ {X86_SYZOS_ADDR_ZERO, 20, MEM_REGION_FLAG_GPA0},
+ // SMRAM memory.
{X86_SYZOS_ADDR_SMRAM, 10, 0},
+ // Unmapped region to trigger a page faults for uexits etc.
+ {X86_SYZOS_ADDR_EXIT, 1, MEM_REGION_FLAG_NO_HOST_MEM},
+ // Writable region with KVM_MEM_LOG_DIRTY_PAGES to fuzz dirty ring.
{X86_SYZOS_ADDR_DIRTY_PAGES, 2, MEM_REGION_FLAG_DIRTY_LOG},
+ // SYZOS user code (generated by the fuzzer).
{X86_SYZOS_ADDR_USER_CODE, KVM_MAX_VCPU, MEM_REGION_FLAG_READONLY | MEM_REGION_FLAG_USER_CODE},
+ // Executor guest code.
{X86_SYZOS_ADDR_EXECUTOR_CODE, 4, MEM_REGION_FLAG_READONLY | MEM_REGION_FLAG_EXECUTOR_CODE},
+ // Scratch memory for code generated at runtime.
{X86_SYZOS_ADDR_SCRATCH_CODE, 1, 0},
+ // CPU stack.
{X86_SYZOS_ADDR_STACK_BOTTOM, 1, 0},
+ // IOAPIC memory.
{X86_SYZOS_ADDR_IOAPIC, 1, 0},
};
#endif
+#if SYZ_EXECUTOR || __NR_syz_kvm_setup_syzos_vm || __NR_syz_kvm_setup_cpu || __NR_syz_kvm_add_vcpu
+struct kvm_syz_vm {
+ int vmfd;
+ int next_cpu_id;
+ void* host_mem;
+ size_t total_pages;
+ void* user_text;
+ void* gpa0_mem;
+};
+#endif
+
#if SYZ_EXECUTOR || __NR_syz_kvm_add_vcpu
// See https://wiki.osdev.org/Interrupt_Descriptor_Table#Gate_Descriptor_2.
struct idt_entry_64 {
@@ -251,12 +275,12 @@ static inline void error_in_executor_fn_guest_addr()
DEFINE_GUEST_FN_TO_GPA_FN(executor_fn_guest_addr, X86_SYZOS_ADDR_EXECUTOR_CODE, error_in_executor_fn_guest_addr());
#define X86_NUM_IDT_ENTRIES 256
-static void syzos_setup_idt(struct kvm_sregs* sregs, char* host_mem)
+static void syzos_setup_idt(struct kvm_syz_vm* vm, struct kvm_sregs* sregs)
{
sregs->idt.base = X86_SYZOS_ADDR_VAR_IDT;
sregs->idt.limit = (X86_NUM_IDT_ENTRIES * sizeof(struct idt_entry_64)) - 1;
volatile struct idt_entry_64* idt =
- (volatile struct idt_entry_64*)(host_mem + sregs->idt.base);
+ (volatile struct idt_entry_64*)((uint64)vm->host_mem + sregs->idt.base);
uint64 handler_addr = executor_fn_guest_addr((uintptr_t)dummy_null_handler);
for (int i = 0; i < X86_NUM_IDT_ENTRIES; i++) {
idt[i].offset_low = (uint16)(handler_addr & 0xFFFF);
@@ -286,19 +310,19 @@ struct kvm_opt {
};
#endif
-#if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu || __NR_syz_kvm_add_vcpu
+#if SYZ_EXECUTOR || __NR_syz_kvm_add_vcpu
#define PAGE_MASK GENMASK_ULL(51, 12)
-static void map_4k_page(void* host_mem, uint64 gpa)
+static void map_4k_page(uint64 host_mem, uint64 gpa)
{
- uint64* pml4 = (uint64*)((uint64)host_mem + X86_SYZOS_ADDR_PML4);
+ uint64* pml4 = (uint64*)(host_mem + X86_SYZOS_ADDR_PML4);
// PML4 Entry (Level 4).
uint64 pml4_idx = (gpa >> 39) & 0x1FF;
// We assume all GPAs are < 512GB, so pml4_idx is always 0 - link it to the PDPT.
if (pml4[pml4_idx] == 0)
pml4[pml4_idx] = X86_PDE64_PRESENT | X86_PDE64_RW | (X86_SYZOS_ADDR_PDP & PAGE_MASK);
- uint64* pdpt = (uint64*)((uint64)host_mem + (pml4[0] & PAGE_MASK));
+ uint64* pdpt = (uint64*)(host_mem + (pml4[0] & PAGE_MASK));
// PDPT Entry (Level 3).
uint64 pdpt_idx = (gpa >> 30) & 0x1FF;
@@ -318,7 +342,7 @@ static void map_4k_page(void* host_mem, uint64 gpa)
if (*pd_addr_ptr == 0)
*pd_addr_ptr = X86_PDE64_PRESENT | X86_PDE64_RW | (pd_phys_addr & PAGE_MASK);
- uint64* pd = (uint64*)((uint64)host_mem + (*pd_addr_ptr & PAGE_MASK));
+ uint64* pd = (uint64*)(host_mem + (*pd_addr_ptr & PAGE_MASK));
// PD Entry (Level 2).
uint64 pd_idx = (gpa >> 21) & 0x1FF;
@@ -338,7 +362,7 @@ static void map_4k_page(void* host_mem, uint64 gpa)
if (*pt_addr_ptr == 0)
*pt_addr_ptr = X86_PDE64_PRESENT | X86_PDE64_RW | (pt_phys_addr & PAGE_MASK);
- uint64* pt = (uint64*)((uint64)host_mem + (*pt_addr_ptr & PAGE_MASK));
+ uint64* pt = (uint64*)(host_mem + (*pt_addr_ptr & PAGE_MASK));
// PT Entry (Level 1).
uint64 pt_idx = (gpa >> 12) & 0x1FF;
@@ -348,7 +372,7 @@ static void map_4k_page(void* host_mem, uint64 gpa)
pt[pt_idx] = (gpa & PAGE_MASK) | X86_PDE64_PRESENT | X86_PDE64_RW;
}
-static int map_4k_region(void* host_mem, uint64 gpa_start, int num_pages)
+static int map_4k_region(uint64 host_mem, uint64 gpa_start, int num_pages)
{
for (int i = 0; i < num_pages; i++)
map_4k_page(host_mem, gpa_start + (i * KVM_PAGE_SIZE));
@@ -357,27 +381,25 @@ static int map_4k_region(void* host_mem, uint64 gpa_start, int num_pages)
// We assume a 4-level page table, in the future we could add support for
// n-level if needed.
-// Assuming host_mem is mapped at GPA 0x0.
-static void setup_pg_table(void* host_mem)
+static void setup_pg_table(struct kvm_syz_vm* vm)
{
- int total = 1024;
+ int total = vm->total_pages;
+ // Page tables are located in the first memory region starting at 0x0.
+ uint64 host_mem = (uint64)vm->gpa0_mem;
// Zero-out all page table memory.
// This includes the 4 levels (PML4, PDPT, PDs) and 3 PTs
- memset((void*)((uint64)host_mem + X86_SYZOS_ADDR_PML4), 0, KVM_PAGE_SIZE);
- memset((void*)((uint64)host_mem + X86_SYZOS_ADDR_PDP), 0, KVM_PAGE_SIZE);
- memset((void*)((uint64)host_mem + X86_SYZOS_ADDR_PD), 0, KVM_PAGE_SIZE);
- memset((void*)((uint64)host_mem + X86_SYZOS_ADDR_PD_IOAPIC), 0, KVM_PAGE_SIZE);
- memset((void*)((uint64)host_mem + X86_SYZOS_ADDR_PT_LOW_MEM), 0, KVM_PAGE_SIZE);
- memset((void*)((uint64)host_mem + X86_SYZOS_ADDR_PT_UNUSED_MEM), 0, 2 * KVM_PAGE_SIZE);
- memset((void*)((uint64)host_mem + X86_SYZOS_ADDR_PT_IOAPIC), 0, KVM_PAGE_SIZE);
+ memset((void*)(host_mem + X86_SYZOS_ADDR_PML4), 0, KVM_PAGE_SIZE);
+ memset((void*)(host_mem + X86_SYZOS_ADDR_PDP), 0, KVM_PAGE_SIZE);
+ memset((void*)(host_mem + X86_SYZOS_ADDR_PD), 0, KVM_PAGE_SIZE);
+ memset((void*)(host_mem + X86_SYZOS_ADDR_PD_IOAPIC), 0, KVM_PAGE_SIZE);
+ memset((void*)(host_mem + X86_SYZOS_ADDR_PT_LOW_MEM), 0, KVM_PAGE_SIZE);
+ memset((void*)(host_mem + X86_SYZOS_ADDR_PT_UNUSED_MEM), 0, 2 * KVM_PAGE_SIZE);
+ memset((void*)(host_mem + X86_SYZOS_ADDR_PT_IOAPIC), 0, KVM_PAGE_SIZE);
// Map all the regions defined in setup_vm()
for (size_t i = 0; i < sizeof(syzos_mem_regions) / sizeof(syzos_mem_regions[0]); i++)
total -= map_4k_region(host_mem, syzos_mem_regions[i].gpa, syzos_mem_regions[i].pages);
map_4k_region(host_mem, X86_SYZOS_ADDR_UNUSED, total);
- // Mapping for MMIO region should be present even though there is no
- // corresponding physical memory.
- map_4k_region(host_mem, X86_SYZOS_ADDR_EXIT, 1);
}
// A 64-bit GDT entry for a code or data segment.
@@ -419,14 +441,14 @@ static void setup_gdt_64(struct gdt_entry* gdt)
// This only sets up a 64-bit VCPU.
// TODO: Should add support for other modes.
-static void setup_gdt_ldt_pg(int cpufd, void* host_mem)
+static void setup_gdt_ldt_pg(struct kvm_syz_vm* vm, int cpufd)
{
struct kvm_sregs sregs;
ioctl(cpufd, KVM_GET_SREGS, &sregs);
sregs.gdt.base = X86_SYZOS_ADDR_GDT;
sregs.gdt.limit = 3 * sizeof(struct gdt_entry) - 1;
- struct gdt_entry* gdt = (struct gdt_entry*)((uint64)host_mem + sregs.gdt.base);
+ struct gdt_entry* gdt = (struct gdt_entry*)((uint64)vm->host_mem + sregs.gdt.base);
struct kvm_segment seg_cs64;
memset(&seg_cs64, 0, sizeof(seg_cs64));
@@ -459,8 +481,8 @@ static void setup_gdt_ldt_pg(int cpufd, void* host_mem)
setup_gdt_64(gdt);
- syzos_setup_idt(&sregs, (char*)host_mem);
- setup_pg_table(host_mem);
+ syzos_setup_idt(vm, &sregs);
+ setup_pg_table(vm);
sregs.cr0 = X86_CR0_PE | X86_CR0_NE | X86_CR0_PG;
sregs.cr4 |= X86_CR4_PAE | X86_CR4_OSFXSR;
@@ -1052,17 +1074,15 @@ static void reset_cpu_regs(int cpufd, int cpu_id, size_t text_size)
ioctl(cpufd, KVM_SET_REGS, &regs);
}
-static void install_user_code(int cpufd, void* user_text_slot, int cpu_id, const void* text, size_t text_size, void* host_mem)
+static void install_user_code(struct kvm_syz_vm* vm, int cpufd, int cpu_id, const void* text, size_t text_size)
{
if ((cpu_id < 0) || (cpu_id >= KVM_MAX_VCPU))
return;
- if (!user_text_slot)
- return;
if (text_size > KVM_PAGE_SIZE)
text_size = KVM_PAGE_SIZE;
- void* target = (void*)((uint64)user_text_slot + (KVM_PAGE_SIZE * cpu_id));
+ void* target = (void*)((uint64)vm->user_text + (KVM_PAGE_SIZE * cpu_id));
memcpy(target, text, text_size);
- setup_gdt_ldt_pg(cpufd, host_mem);
+ setup_gdt_ldt_pg(vm, cpufd);
setup_cpuid(cpufd);
reset_cpu_regs(cpufd, cpu_id, text_size);
}
@@ -1107,32 +1127,25 @@ static void install_syzos_code(void* host_mem, size_t mem_size)
memcpy(host_mem, &__start_guest, size);
}
-static void setup_vm(int vmfd, void* host_mem, void** text_slot)
+static void setup_vm(int vmfd, struct kvm_syz_vm* vm)
{
- // Guest virtual memory layout (must be in sync with executor/kvm.h):
- // 0x00000000 - AMD64 data structures (20 pages, see kvm.h)
- // 0x00030000 - SMRAM (10 pages)
- // 0x00040000 - unmapped region to trigger a page faults for uexits etc. (1 page)
- // 0x00041000 - writable region with KVM_MEM_LOG_DIRTY_PAGES to fuzz dirty ring (2 pages)
- // 0x00050000 - user code (4 pages)
- // 0x00054000 - executor guest code (4 pages)
- // 0000058000 - scratch memory for code generated at runtime (1 page)
- // 0xfec00000 - IOAPIC (1 page)
- struct addr_size allocator = {.addr = host_mem, .size = KVM_GUEST_MEM_SIZE};
+ struct addr_size allocator = {.addr = vm->host_mem, .size = vm->total_pages * KVM_PAGE_SIZE};
int slot = 0; // Slot numbers do not matter, they just have to be different.
for (size_t i = 0; i < sizeof(syzos_mem_regions) / sizeof(syzos_mem_regions[0]); i++) {
const struct mem_region* r = &syzos_mem_regions[i];
+ if (r->flags & MEM_REGION_FLAG_NO_HOST_MEM)
+ continue;
struct addr_size next = alloc_guest_mem(&allocator, r->pages * KVM_PAGE_SIZE);
uint32 flags = 0;
if (r->flags & MEM_REGION_FLAG_DIRTY_LOG)
flags |= KVM_MEM_LOG_DIRTY_PAGES;
if (r->flags & MEM_REGION_FLAG_READONLY)
flags |= KVM_MEM_READONLY;
- if (r->flags & MEM_REGION_FLAG_USER_CODE) {
- if (text_slot)
- *text_slot = next.addr;
- }
+ if (r->flags & MEM_REGION_FLAG_USER_CODE)
+ vm->user_text = next.addr;
+ if (r->flags & MEM_REGION_FLAG_GPA0)
+ vm->gpa0_mem = next.addr;
if (r->flags & MEM_REGION_FLAG_EXECUTOR_CODE)
install_syzos_code(next.addr, next.size);
vm_set_user_memory_region(vmfd, slot++, flags, r->gpa, next.size, (uintptr_t)next.addr);
@@ -1144,29 +1157,17 @@ static void setup_vm(int vmfd, void* host_mem, void** text_slot)
}
#endif
-#if SYZ_EXECUTOR || __NR_syz_kvm_setup_syzos_vm || __NR_syz_kvm_add_vcpu
-struct kvm_syz_vm {
- int vmfd;
- int next_cpu_id;
- void* user_text;
- void* host_mem;
-};
-#endif
-
#if SYZ_EXECUTOR || __NR_syz_kvm_setup_syzos_vm
static long syz_kvm_setup_syzos_vm(volatile long a0, volatile long a1)
{
const int vmfd = a0;
void* host_mem = (void*)a1;
-
- void* user_text_slot = NULL;
struct kvm_syz_vm* ret = (struct kvm_syz_vm*)host_mem;
- host_mem = (void*)((uint64)host_mem + KVM_PAGE_SIZE);
- setup_vm(vmfd, host_mem, &user_text_slot);
+ ret->host_mem = (void*)((uint64)host_mem + KVM_PAGE_SIZE);
+ ret->total_pages = KVM_GUEST_PAGES - 1;
+ setup_vm(vmfd, ret);
ret->vmfd = vmfd;
ret->next_cpu_id = 0;
- ret->user_text = user_text_slot;
- ret->host_mem = host_mem;
return (long)ret;
}
#endif
@@ -1193,7 +1194,7 @@ static long syz_kvm_add_vcpu(volatile long a0, volatile long a1)
return -1;
// Only increment next_cpu_id if CPU creation succeeded.
vm->next_cpu_id++;
- install_user_code(cpufd, vm->user_text, cpu_id, text, text_size, vm->host_mem);
+ install_user_code(vm, cpufd, cpu_id, text, text_size);
return cpufd;
}
#endif
diff --git a/executor/kvm.h b/executor/kvm.h
index 57d8aa490..8e5dc62be 100644
--- a/executor/kvm.h
+++ b/executor/kvm.h
@@ -174,7 +174,8 @@
#define KVM_MAX_VCPU 4
#define KVM_PAGE_SIZE (1 << 12)
-#define KVM_GUEST_MEM_SIZE (1024 * KVM_PAGE_SIZE)
+#define KVM_GUEST_PAGES 1024
+#define KVM_GUEST_MEM_SIZE (KVM_GUEST_PAGES * KVM_PAGE_SIZE)
#define SZ_4K 0x00001000
#define SZ_64K 0x00010000
#define GENMASK_ULL(h, l) \