From 6e0dfea112951c25292cd8f108b840d55caf69a7 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Fri, 23 Jan 2026 16:22:42 +0100 Subject: executor: add boot arguments page to SYZOS layout Introduce a dedicated page at X86_SYZOS_ADDR_BOOT_ARGS to pass configuration data from the executor to the SYZOS guest. This will allow dynamic adjustments to the guest environment, such as specifying memory region sizes. - Added `MEM_REGION_FLAG_REMAINING` to flag the last memory region, which will consume the rest of the available guest memory. - Defined `struct syzos_boot_args` to pass the memory region layout to the guest. - Modified `syzos_mem_regions`: - Reduced X86_SYZOS_ADDR_VAR_IDT size to 10 pages. - Inserted the new X86_SYZOS_ADDR_BOOT_ARGS region. - Added a final region with MEM_REGION_FLAG_REMAINING. - Updated `setup_vm` to: - Calculate the size of the REMAINING region. - Populate the `syzos_boot_args` structure in the boot args page. - Updated `setup_pg_table` to use the REMAINING flag to map the last region. --- executor/common_kvm_amd64.h | 63 +++++++++++++++++++++++++++++++++++---------- executor/kvm.h | 2 ++ 2 files changed, 52 insertions(+), 13 deletions(-) (limited to 'executor') diff --git a/executor/common_kvm_amd64.h b/executor/common_kvm_amd64.h index 50d927088..06efc7f20 100644 --- a/executor/common_kvm_amd64.h +++ b/executor/common_kvm_amd64.h @@ -213,6 +213,7 @@ static void setup_64bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t g #define MEM_REGION_FLAG_EXECUTOR_CODE (1 << 3) #define MEM_REGION_FLAG_GPA0 (1 << 5) #define MEM_REGION_FLAG_NO_HOST_MEM (1 << 6) +#define MEM_REGION_FLAG_REMAINING (1 << 7) struct mem_region { uint64 gpa; @@ -220,12 +221,20 @@ struct mem_region { uint32 flags; }; +struct syzos_boot_args { + uint32 region_count; + uint32 reserved; + struct mem_region regions[]; +}; + // SYZOS guest virtual memory layout (must be in sync with executor/kvm.h): static const struct mem_region syzos_mem_regions[] = { // AMD64 fixed data structures (5 pages: Zero, GDT, PML4, PDP, PD). {X86_SYZOS_ADDR_ZERO, 5, MEM_REGION_FLAG_GPA0}, - // High fixed data (IDT, TSS). - {X86_SYZOS_ADDR_VAR_IDT, 11, 0}, + // High fixed data (IDT, TSS). Reduced to 10 pages to make room for Boot Args. + {X86_SYZOS_ADDR_VAR_IDT, 10, 0}, + // Boot Configuration Page. + {X86_SYZOS_ADDR_BOOT_ARGS, 1, 0}, // Dynamic Page Table Pool. {X86_SYZOS_ADDR_PT_POOL, X86_SYZOS_PT_POOL_SIZE, 0}, // SMRAM memory. @@ -246,6 +255,8 @@ static const struct mem_region syzos_mem_regions[] = { {X86_SYZOS_PER_VCPU_REGIONS_BASE, (KVM_MAX_VCPU * X86_SYZOS_L1_VCPU_REGION_SIZE) / KVM_PAGE_SIZE, 0}, // IOAPIC memory. {X86_SYZOS_ADDR_IOAPIC, 1, 0}, + // Remainder of memory (Unused Heap). Must be last. + {X86_SYZOS_ADDR_UNUSED, 0, MEM_REGION_FLAG_REMAINING}, }; #endif @@ -376,15 +387,25 @@ static void setup_pg_table(struct kvm_syz_vm* vm) // Zero-out the PT Pool memory. memset(vm->pt_pool_mem, 0, X86_SYZOS_PT_POOL_SIZE * KVM_PAGE_SIZE); - // Zero-out the fixed system pages (PML4/PDP/PD). - // They are in the first 5 pages of gpa0_mem. memset(vm->gpa0_mem, 0, 5 * 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(vm, &alloc, syzos_mem_regions[i].gpa, syzos_mem_regions[i].pages); - map_4k_region(vm, &alloc, X86_SYZOS_ADDR_UNUSED, total); + for (size_t i = 0; i < sizeof(syzos_mem_regions) / sizeof(syzos_mem_regions[0]); i++) { + int pages = syzos_mem_regions[i].pages; + if (syzos_mem_regions[i].flags & MEM_REGION_FLAG_REMAINING) { + if (total < 0) + fail("Guest memory accounting underflow"); + pages = total; + } + map_4k_region(vm, &alloc, syzos_mem_regions[i].gpa, pages); + + // Only consume 'total' if the region is actually backed by host RAM. + if (!(syzos_mem_regions[i].flags & MEM_REGION_FLAG_NO_HOST_MEM)) + total -= pages; + if (syzos_mem_regions[i].flags & MEM_REGION_FLAG_REMAINING) + break; + } } // A 64-bit GDT entry for a code or data segment. @@ -1152,12 +1173,18 @@ static void setup_vm(int vmfd, struct kvm_syz_vm* vm) { 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. + struct syzos_boot_args* boot_args = NULL; 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); + + size_t pages = r->pages; + if (r->flags & MEM_REGION_FLAG_REMAINING) + pages = allocator.size / KVM_PAGE_SIZE; + + struct addr_size next = alloc_guest_mem(&allocator, pages * KVM_PAGE_SIZE); uint32 flags = 0; if (r->flags & MEM_REGION_FLAG_DIRTY_LOG) flags |= KVM_MEM_LOG_DIRTY_PAGES; @@ -1169,14 +1196,24 @@ static void setup_vm(int vmfd, struct kvm_syz_vm* vm) vm->gpa0_mem = next.addr; if (r->gpa == X86_SYZOS_ADDR_PT_POOL) vm->pt_pool_mem = next.addr; + + if (r->gpa == X86_SYZOS_ADDR_BOOT_ARGS) { + boot_args = (struct syzos_boot_args*)next.addr; + boot_args->region_count = sizeof(syzos_mem_regions) / sizeof(syzos_mem_regions[0]); + for (size_t k = 0; k < boot_args->region_count; k++) + boot_args->regions[k] = syzos_mem_regions[k]; + } + + if ((r->flags & MEM_REGION_FLAG_REMAINING) && boot_args) + boot_args->regions[i].pages = pages; + 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); - } - // Map the remaining pages at an unused address. - struct addr_size next = alloc_guest_mem(&allocator, allocator.size); - vm_set_user_memory_region(vmfd, slot++, 0, X86_SYZOS_ADDR_UNUSED, next.size, (uintptr_t)next.addr); + if (r->flags & MEM_REGION_FLAG_REMAINING) + break; + } } #endif @@ -1289,4 +1326,4 @@ static long syz_kvm_assert_syzos_uexit(volatile long a0, volatile long a1, volat } #endif -#endif // EXECUTOR_COMMON_KVM_AMD64_H +#endif // EXECUTOR_COMMON_KVM_AMD64_H \ No newline at end of file diff --git a/executor/kvm.h b/executor/kvm.h index ab5178792..f377887a6 100644 --- a/executor/kvm.h +++ b/executor/kvm.h @@ -47,6 +47,8 @@ #define X86_SYZOS_ADDR_PDP 0x3000 #define X86_SYZOS_ADDR_VAR_IDT 0x25000 #define X86_SYZOS_ADDR_VAR_TSS 0x26000 +// Dedicated page for passing configuration (memory map) to L1. +#define X86_SYZOS_ADDR_BOOT_ARGS 0x2F000 #define X86_SYZOS_ADDR_SMRAM 0x30000 // Write to this page to trigger a page fault and stop KVM_RUN. -- cgit mrf-deployment