diff options
| author | Alexander Potapenko <glider@google.com> | 2024-09-12 10:35:56 +0200 |
|---|---|---|
| committer | Alexander Potapenko <glider@google.com> | 2024-09-25 09:05:57 +0000 |
| commit | 46547111bb3121d3c61f7bd2725008c540291bab (patch) | |
| tree | c4310b49e9eda27aab453c91386ed126f0c08ab8 /executor | |
| parent | 4e0b1d0a9f1fe6044c2113b5b0465cd3f53e29ba (diff) | |
executor: arm64: factor out install_user_code()
Prepare to let multiple (up to 4) CPUs run different pieces of code
by allocating 4 pages for ARM64_ADDR_USER_CODE.
Pass the CPU id to guest_main(), so that it can pick the correct
starting addres.
syz_kvm_setup_cpu() will implicitly use cpuid=0 to retain its current functionality.
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/common_kvm_arm64.h | 64 |
1 files changed, 42 insertions, 22 deletions
diff --git a/executor/common_kvm_arm64.h b/executor/common_kvm_arm64.h index e77a8d908..b90c0c48c 100644 --- a/executor/common_kvm_arm64.h +++ b/executor/common_kvm_arm64.h @@ -11,6 +11,7 @@ // Register encodings from https://docs.kernel.org/virt/kvm/api.html. #define KVM_ARM64_REGS_X0 0x6030000000100000UL +#define KVM_ARM64_REGS_X1 0x6030000000100002UL #define KVM_ARM64_REGS_PC 0x6030000000100040UL #define KVM_ARM64_REGS_SP_EL1 0x6030000000100044UL @@ -68,10 +69,12 @@ struct api_fn { void* fn; }; -static void setup_vm(int vmfd, void* host_mem, const void* text, size_t text_size) +#define SYZ_KVM_MAX_VCPU 4 +#define SYZ_KVM_PAGE_SIZE (4 << 10) + +static void setup_vm(int vmfd, void* host_mem, void** text_slot) { - const uintptr_t page_size = 4 << 10; - const uintptr_t guest_mem_size = 24 * page_size; + const uintptr_t guest_mem_size = 24 * SYZ_KVM_PAGE_SIZE; // Guest physical memory layout (must be in sync with executor/kvm.h): // 0x00000000 - unused pages @@ -79,30 +82,29 @@ static void setup_vm(int vmfd, void* host_mem, const void* text, size_t text_siz // 0x080a0000 - GICv3 redistributor region (MMIO, no memory allocated) // 0xdddd0000 - unmapped region to trigger a page faults for uexits etc. (1 page) // 0xdddd1000 - writable region with KVM_MEM_LOG_DIRTY_PAGES to fuzz dirty ring (2 pages) - // 0xeeee0000 - user code (1 page) + // 0xeeee0000 - user code (4 pages) // 0xeeee8000 - executor guest code (4 pages) // 0xeeef0000 - scratch memory for code generated at runtime (1 page) // 0xffff1000 - EL1 stack (1 page) struct addr_size allocator = {.addr = host_mem, .size = guest_mem_size}; int slot = 0; // Slot numbers do not matter, they just have to be different. - struct addr_size host_text = alloc_guest_mem(&allocator, 4 * page_size); + struct addr_size host_text = alloc_guest_mem(&allocator, 4 * SYZ_KVM_PAGE_SIZE); memcpy(host_text.addr, &__start_guest, (char*)&__stop_guest - (char*)&__start_guest); vm_set_user_memory_region(vmfd, slot++, KVM_MEM_READONLY, ARM64_ADDR_EXECUTOR_CODE, host_text.size, (uintptr_t)host_text.addr); - struct addr_size next = alloc_guest_mem(&allocator, 2 * page_size); + struct addr_size next = alloc_guest_mem(&allocator, 2 * SYZ_KVM_PAGE_SIZE); vm_set_user_memory_region(vmfd, slot++, KVM_MEM_LOG_DIRTY_PAGES, ARM64_ADDR_DIRTY_PAGES, next.size, (uintptr_t)next.addr); - next = alloc_guest_mem(&allocator, page_size); - if (text_size > next.size) - text_size = next.size; - memcpy(next.addr, text, text_size); + next = alloc_guest_mem(&allocator, SYZ_KVM_MAX_VCPU * SYZ_KVM_PAGE_SIZE); vm_set_user_memory_region(vmfd, slot++, KVM_MEM_READONLY, ARM64_ADDR_USER_CODE, next.size, (uintptr_t)next.addr); + if (text_slot) + *text_slot = next.addr; - next = alloc_guest_mem(&allocator, page_size); + next = alloc_guest_mem(&allocator, SYZ_KVM_PAGE_SIZE); vm_set_user_memory_region(vmfd, slot++, 0, ARM64_ADDR_EL1_STACK_BOTTOM, next.size, (uintptr_t)next.addr); - next = alloc_guest_mem(&allocator, page_size); + next = alloc_guest_mem(&allocator, SYZ_KVM_PAGE_SIZE); vm_set_user_memory_region(vmfd, slot++, 0, ARM64_ADDR_SCRATCH_CODE, next.size, (uintptr_t)next.addr); // Map the remaining pages at address 0. @@ -110,6 +112,30 @@ static void setup_vm(int vmfd, void* host_mem, const void* text, size_t text_siz vm_set_user_memory_region(vmfd, slot++, 0, 0, next.size, (uintptr_t)next.addr); } +// Set up CPU registers. +static void reset_cpu_regs(int cpufd, int cpu_id, size_t text_size) +{ + // PC points to the relative offset of guest_main() within the guest code. + vcpu_set_reg(cpufd, KVM_ARM64_REGS_PC, ARM64_ADDR_EXECUTOR_CODE + ((uint64)guest_main - (uint64)&__start_guest)); + vcpu_set_reg(cpufd, KVM_ARM64_REGS_SP_EL1, ARM64_ADDR_EL1_STACK_BOTTOM + SYZ_KVM_PAGE_SIZE - 128); + // Pass parameters to guest_main(). + vcpu_set_reg(cpufd, KVM_ARM64_REGS_X0, text_size); + vcpu_set_reg(cpufd, KVM_ARM64_REGS_X1, cpu_id); +} + +static void install_user_code(int cpufd, void* user_text_slot, int cpu_id, const void* text, size_t text_size) +{ + if ((cpu_id < 0) || (cpu_id >= SYZ_KVM_MAX_VCPU)) + return; + if (!user_text_slot) + return; + if (text_size > SYZ_KVM_PAGE_SIZE) + text_size = SYZ_KVM_PAGE_SIZE; + void* target = (void*)((uint64)user_text_slot + (SYZ_KVM_PAGE_SIZE * cpu_id)); + memcpy(target, text, text_size); + reset_cpu_regs(cpufd, cpu_id, text_size); +} + // syz_kvm_setup_cpu(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text, 1]], ntext len[text], flags flags[kvm_setup_flags], opts ptr[in, array[kvm_setup_opt, 0:2]], nopt len[opts]) static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { @@ -125,8 +151,6 @@ static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat (void)flags; (void)opt_count; - const uintptr_t page_size = 4 << 10; - (void)text_count; // fuzzer can spoof count and we need just 1 text, so ignore text_count int text_type = text_array_ptr[0].typ; const void* text = text_array_ptr[0].text; @@ -147,7 +171,8 @@ static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat } } - setup_vm(vmfd, host_mem, text, text_size); + void* user_text_slot = NULL; + setup_vm(vmfd, host_mem, &user_text_slot); struct kvm_vcpu_init init; // Queries KVM for preferred CPU target type. @@ -156,13 +181,8 @@ static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat // Use the modified struct kvm_vcpu_init to initialize the virtual CPU. ioctl(cpufd, KVM_ARM_VCPU_INIT, &init); - // Set up CPU registers. - // PC points to the relative offset of guest_main() within the guest code. - vcpu_set_reg(cpufd, KVM_ARM64_REGS_PC, ARM64_ADDR_EXECUTOR_CODE + ((uint64)guest_main - (uint64)&__start_guest)); - vcpu_set_reg(cpufd, KVM_ARM64_REGS_SP_EL1, ARM64_ADDR_EL1_STACK_BOTTOM + page_size - 128); - // Pass parameters to guest_main(). - vcpu_set_reg(cpufd, KVM_ARM64_REGS_X0, text_size); - + // Assume CPU is 0. + install_user_code(cpufd, user_text_slot, 0, text, text_size); return 0; } #endif |
