diff options
| author | Alexander Potapenko <glider@google.com> | 2025-09-30 18:17:15 +0200 |
|---|---|---|
| committer | Alexander Potapenko <glider@google.com> | 2025-10-17 06:51:20 +0000 |
| commit | c42fde19c0fd97583e4c8f6f88e2059ba630bc08 (patch) | |
| tree | 296fd533ff295df11a28799223bf878b1dfa3a24 /executor | |
| parent | 2606b77513573de02745daefe9de925c9494cfff (diff) | |
executor: use dynamic page table allocation for guest
Use a pool of 32 pages to allocate PT and PE entries for the guest
page tables.
This eliminates the need for manually assigned page table entries
that are brittle and may break when someone changes the memory
layout.
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/common_kvm_amd64.h | 87 | ||||
| -rw-r--r-- | executor/kvm.h | 14 |
2 files changed, 38 insertions, 63 deletions
diff --git a/executor/common_kvm_amd64.h b/executor/common_kvm_amd64.h index 44e209650..ce778792d 100644 --- a/executor/common_kvm_amd64.h +++ b/executor/common_kvm_amd64.h @@ -219,8 +219,8 @@ struct mem_region { // SYZOS guest virtual memory layout (must be in sync with executor/kvm.h): static const struct mem_region syzos_mem_regions[] = { - // AMD64 data structures (20 pages starting at GPA 0x0, see kvm.h). - {X86_SYZOS_ADDR_ZERO, 20, MEM_REGION_FLAG_GPA0}, + // AMD64 data structures (48 pages starting at GPA 0x0, see kvm.h). + {X86_SYZOS_ADDR_ZERO, 48, MEM_REGION_FLAG_GPA0}, // SMRAM memory. {X86_SYZOS_ADDR_SMRAM, 10, 0}, // Unmapped region to trigger a page faults for uexits etc. @@ -313,56 +313,41 @@ struct kvm_opt { #if SYZ_EXECUTOR || __NR_syz_kvm_add_vcpu #define PAGE_MASK GENMASK_ULL(51, 12) -static void map_4k_page(uint64 host_mem, uint64 gpa) +typedef struct { + uint64 next_page; + uint64 last_page; +} page_alloc_t; + +static uint64 pg_alloc(page_alloc_t* alloc) +{ + if (alloc->next_page >= alloc->last_page) + fail("page table allocation failed"); + uint64 page = alloc->next_page; + alloc->next_page += KVM_PAGE_SIZE; + return page; +} + +static void map_4k_page(uint64 host_mem, page_alloc_t* alloc, uint64 gpa) { 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*)(host_mem + (pml4[0] & PAGE_MASK)); + pml4[pml4_idx] = X86_PDE64_PRESENT | X86_PDE64_RW | pg_alloc(alloc); + uint64* pdpt = (uint64*)(host_mem + (pml4[pml4_idx] & PAGE_MASK)); // PDPT Entry (Level 3). uint64 pdpt_idx = (gpa >> 30) & 0x1FF; - uint64* pd_addr_ptr = &pdpt[pdpt_idx]; - uint64 pd_phys_addr = 0; - - // Determine which Page Directory (PD) to use based on the address - if (gpa >= X86_SYZOS_ADDR_IOAPIC) { - // High-memory IOAPIC region (0xfec00000). - // PDPT index will be 3 (for 0xC0000000 - 0xFFFFFFFFF). - pd_phys_addr = X86_SYZOS_ADDR_PD_IOAPIC; - } else { - // Low-memory region (< 1GB). - // PDPT index will be 0 (for 0x0 - 0x3FFFFFFF). - pd_phys_addr = X86_SYZOS_ADDR_PD; - } - if (*pd_addr_ptr == 0) - *pd_addr_ptr = X86_PDE64_PRESENT | X86_PDE64_RW | (pd_phys_addr & PAGE_MASK); - - uint64* pd = (uint64*)(host_mem + (*pd_addr_ptr & PAGE_MASK)); + if (pdpt[pdpt_idx] == 0) + pdpt[pdpt_idx] = X86_PDE64_PRESENT | X86_PDE64_RW | pg_alloc(alloc); + uint64* pd = (uint64*)(host_mem + (pdpt[pdpt_idx] & PAGE_MASK)); // PD Entry (Level 2). uint64 pd_idx = (gpa >> 21) & 0x1FF; - uint64* pt_addr_ptr = &pd[pd_idx]; - uint64 pt_phys_addr = 0; - - // Determine which Page Table (PT) to use. - if (gpa >= X86_SYZOS_ADDR_IOAPIC) { - pt_phys_addr = X86_SYZOS_ADDR_PT_IOAPIC; - } else if (gpa >= X86_SYZOS_ADDR_UNUSED) { - const uint64 unused_base_pd_idx = (X86_SYZOS_ADDR_UNUSED >> 21) & 0x1FF; - const uint64 gpa_pd_idx = (gpa >> 21) & 0x1FF; - pt_phys_addr = X86_SYZOS_ADDR_PT_UNUSED_MEM + (gpa_pd_idx - unused_base_pd_idx) * KVM_PAGE_SIZE; - } else { - pt_phys_addr = X86_SYZOS_ADDR_PT_LOW_MEM; - } - if (*pt_addr_ptr == 0) - *pt_addr_ptr = X86_PDE64_PRESENT | X86_PDE64_RW | (pt_phys_addr & PAGE_MASK); - - uint64* pt = (uint64*)(host_mem + (*pt_addr_ptr & PAGE_MASK)); + if (pd[pd_idx] == 0) + pd[pd_idx] = X86_PDE64_PRESENT | X86_PDE64_RW | pg_alloc(alloc); + uint64* pt = (uint64*)(host_mem + (pd[pd_idx] & PAGE_MASK)); // PT Entry (Level 1). uint64 pt_idx = (gpa >> 12) & 0x1FF; @@ -372,10 +357,10 @@ static void map_4k_page(uint64 host_mem, uint64 gpa) pt[pt_idx] = (gpa & PAGE_MASK) | X86_PDE64_PRESENT | X86_PDE64_RW; } -static int map_4k_region(uint64 host_mem, uint64 gpa_start, int num_pages) +static int map_4k_region(uint64 host_mem, page_alloc_t* alloc, 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)); + map_4k_page(host_mem, alloc, gpa_start + (i * KVM_PAGE_SIZE)); return num_pages; } @@ -386,20 +371,18 @@ static void setup_pg_table(struct kvm_syz_vm* vm) int total = vm->total_pages; // Page tables are located in the first memory region starting at 0x0. uint64 host_mem = (uint64)vm->gpa0_mem; + + page_alloc_t alloc = {.next_page = X86_SYZOS_ADDR_PT_POOL, + .last_page = X86_SYZOS_ADDR_PT_POOL + 32 * KVM_PAGE_SIZE}; + // Zero-out all page table memory. - // This includes the 4 levels (PML4, PDPT, PDs) and 3 PTs - 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); + for (uint64 i = 0; i < (alloc.last_page - alloc.next_page); i += KVM_PAGE_SIZE) + memset((void*)(host_mem + alloc.next_page + i), 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); + total -= map_4k_region(host_mem, &alloc, syzos_mem_regions[i].gpa, syzos_mem_regions[i].pages); + map_4k_region(host_mem, &alloc, X86_SYZOS_ADDR_UNUSED, total); } // A 64-bit GDT entry for a code or data segment. diff --git a/executor/kvm.h b/executor/kvm.h index 8e5dc62be..eb0bb7b56 100644 --- a/executor/kvm.h +++ b/executor/kvm.h @@ -41,17 +41,9 @@ #define X86_SYZOS_ADDR_PML4 0x2000 // PDP for GPAs 0x0 - 0x7fffffffff. #define X86_SYZOS_ADDR_PDP 0x3000 -// Lowmem PD for GPAs 0x0 - 0x3fffffff. -#define X86_SYZOS_ADDR_PD 0x4000 -// IOAPIC PD for GPAs 0xc0000000 - 0xffffffff. -#define X86_SYZOS_ADDR_PD_IOAPIC 0x5000 -// Lowmem PT for GPAs 0x000000 - 0x1fffff. -#define X86_SYZOS_ADDR_PT_LOW_MEM 0x6000 -// Two PTs for unused memory for GPAs 0x200000 - 0x3fffff. -#define X86_SYZOS_ADDR_PT_UNUSED_MEM 0x7000 -// IOAPIC PT for GPAs 0xfed00000 - 0xfedfffff. -#define X86_SYZOS_ADDR_PT_IOAPIC 0x9000 -#define X86_SYZOS_ADDR_VAR_IDT 0x10000 +// Pool of 32 pages for dynamic PT/PD allocations. +#define X86_SYZOS_ADDR_PT_POOL 0x5000 +#define X86_SYZOS_ADDR_VAR_IDT 0x25000 #define X86_SYZOS_ADDR_SMRAM 0x30000 // Write to this page to trigger a page fault and stop KVM_RUN. |
