aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
Diffstat (limited to 'executor')
-rw-r--r--executor/common_kvm_arm64.h89
-rw-r--r--executor/kvm.h3
2 files changed, 78 insertions, 14 deletions
diff --git a/executor/common_kvm_arm64.h b/executor/common_kvm_arm64.h
index 6369cdd81..b4de3cfcf 100644
--- a/executor/common_kvm_arm64.h
+++ b/executor/common_kvm_arm64.h
@@ -5,6 +5,12 @@
// Implementation of syz_kvm_setup_cpu pseudo-syscall.
+#include "kvm.h"
+
+// Register encodings from https://docs.kernel.org/virt/kvm/api.html.
+#define KVM_ARM64_REGS_PC 0x6030000000100040UL
+#define KVM_ARM64_REGS_SP_EL1 0x6030000000100044UL
+
struct kvm_text {
uintptr_t typ;
const void* text;
@@ -16,6 +22,51 @@ struct kvm_opt {
uint64 val;
};
+// Call KVM_SET_USER_MEMORY_REGION for the given pages.
+static void vm_set_user_memory_region(int vmfd, uint32 slot, uint32 flags, uint64 guest_phys_addr, uint64 memory_size, uint64 userspace_addr)
+{
+ struct kvm_userspace_memory_region memreg;
+ memreg.slot = slot;
+ memreg.flags = flags;
+ memreg.guest_phys_addr = guest_phys_addr;
+ memreg.memory_size = memory_size;
+ memreg.userspace_addr = userspace_addr;
+ ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
+}
+
+// Set the value of the specified register.
+static void vcpu_set_reg(int vcpu_fd, uint64 id, uint64 val)
+{
+ struct kvm_one_reg reg = {.id = id, .addr = (uint64)&val};
+ ioctl(vcpu_fd, KVM_SET_ONE_REG, &reg);
+}
+
+struct addr_size {
+ void* addr;
+ size_t size;
+};
+
+static struct addr_size alloc_guest_mem(struct addr_size* free, size_t size)
+{
+ struct addr_size ret = {.addr = NULL, .size = 0};
+
+ if (free->size < size)
+ return ret;
+ ret.addr = free->addr;
+ ret.size = size;
+ free->addr = (void*)((char*)free->addr + size);
+ free->size -= size;
+ return ret;
+}
+
+static void fill_with_ret(void* addr, int size)
+{
+ uint32* insn = (uint32*)addr;
+
+ for (int i = 0; i < size / 4; i++)
+ insn[i] = 0xd65f03c0; // RET
+}
+
// 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)
{
@@ -32,13 +83,12 @@ static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat
(void)opt_count;
const uintptr_t page_size = 4 << 10;
- const uintptr_t guest_mem = 0;
const uintptr_t guest_mem_size = 24 * page_size;
(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;
- int text_size = text_array_ptr[0].size;
+ size_t text_size = text_array_ptr[0].size;
(void)text_type;
(void)opt_array_ptr;
@@ -55,15 +105,26 @@ static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat
}
}
- for (uintptr_t i = 0; i < guest_mem_size / page_size; i++) {
- struct kvm_userspace_memory_region memreg;
- memreg.slot = i;
- memreg.flags = 0; // can be KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY
- memreg.guest_phys_addr = guest_mem + i * page_size;
- memreg.memory_size = page_size;
- memreg.userspace_addr = (uintptr_t)host_mem + i * page_size;
- ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
- }
+ // Guest physical memory layout:
+ // 0x00000000 - unused pages
+ // 0xeeee0000 - user code (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 next = alloc_guest_mem(&allocator, page_size);
+ // Fill the guest code page with RET instructions to be on the safe side.
+ fill_with_ret(next.addr, next.size);
+ if (text_size > next.size)
+ text_size = next.size;
+ memcpy(next.addr, text, text_size);
+ vm_set_user_memory_region(vmfd, slot++, KVM_MEM_READONLY, ARM64_ADDR_USER_CODE, next.size, (uintptr_t)next.addr);
+ next = alloc_guest_mem(&allocator, page_size);
+ vm_set_user_memory_region(vmfd, slot++, 0, ARM64_ADDR_EL1_STACK_BOTTOM, next.size, (uintptr_t)next.addr);
+
+ // Map the remaining pages at address 0.
+ next = alloc_guest_mem(&allocator, allocator.size);
+ vm_set_user_memory_region(vmfd, slot++, 0, 0, next.size, (uintptr_t)next.addr);
struct kvm_vcpu_init init;
// Queries KVM for preferred CPU target type.
@@ -72,9 +133,9 @@ 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);
- if (text_size > 1000)
- text_size = 1000;
- memcpy(host_mem, text, text_size);
+ // Set up CPU registers.
+ vcpu_set_reg(cpufd, KVM_ARM64_REGS_PC, ARM64_ADDR_USER_CODE);
+ vcpu_set_reg(cpufd, KVM_ARM64_REGS_SP_EL1, ARM64_ADDR_EL1_STACK_BOTTOM + page_size - 128);
return 0;
}
diff --git a/executor/kvm.h b/executor/kvm.h
index 18b88d1f9..217ce24ff 100644
--- a/executor/kvm.h
+++ b/executor/kvm.h
@@ -75,3 +75,6 @@
#define NEXT_INSN $0xbadc0de
#define PREFIX_SIZE 0xba1d
+
+#define ARM64_ADDR_USER_CODE 0xeeee0000
+#define ARM64_ADDR_EL1_STACK_BOTTOM 0xffff1000