From 2fb4dcc9c10e100beedbbc223c2a9762bc45403e Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Tue, 23 Jul 2024 14:17:26 +0200 Subject: executor: arm64: sys/linux: introduce syzos API Allow guest payload to call syzos API functions. The available calls are enumerated by SYZOS_API_* constants, and have a form of: struct api_call { uint64 call; uint64 struct_size; /* arbitrary call-related data here */ }; Complex instruction sequences are too easy to break, so most of the time fuzzer won't be able to efficiently mutate them. We replace kvm_text_arm64 with a sequence of `struct api_call`, making it possible to intermix assembly instructions (SYZOS_API_CODE) with higher-level constructs. Right now the supported calls are: - SYZOS_API_UEXIT - abort from KVM_RUN (1 argument: exit code, uint64) - SYZOS_API_CODE - execute an ARM64 assembly blob (1 argument: inline array of int32's) --- executor/common_kvm_arm64_syzos.h | 66 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) (limited to 'executor/common_kvm_arm64_syzos.h') diff --git a/executor/common_kvm_arm64_syzos.h b/executor/common_kvm_arm64_syzos.h index b9edf0069..022378b34 100644 --- a/executor/common_kvm_arm64_syzos.h +++ b/executor/common_kvm_arm64_syzos.h @@ -11,10 +11,70 @@ // Start/end of the guest section. extern char *__start_guest, *__stop_guest; +typedef enum { + SYZOS_API_UEXIT, + SYZOS_API_CODE, + SYZOS_API_STOP, // Must be the last one +} syzos_api_id; + +struct api_call_header { + uint64 call; + uint64 size; +}; + +struct api_call_uexit { + struct api_call_header header; + uint64 exit_code; +}; + +struct api_call_code { + struct api_call_header header; + uint32 insns[]; +}; + +void guest_uexit(uint64 exit_code); +void guest_execute_code(uint32* insns, uint64 size); + // Main guest function that performs necessary setup and passes the control to the user-provided // payload. -GUEST_CODE static void guest_main() +GUEST_CODE static void guest_main(uint64 size) +{ + uint64 addr = ARM64_ADDR_USER_CODE; + + while (size >= sizeof(struct api_call_header)) { + struct api_call_header* cmd = (struct api_call_header*)addr; + if (cmd->call >= SYZOS_API_STOP) + return; + if (cmd->size > size) + return; + switch (cmd->call) { + case SYZOS_API_UEXIT: { + struct api_call_uexit* ucmd = (struct api_call_uexit*)cmd; + guest_uexit(ucmd->exit_code); + break; + } + case SYZOS_API_CODE: { + struct api_call_code* ccmd = (struct api_call_code*)cmd; + guest_execute_code(ccmd->insns, cmd->size - sizeof(struct api_call_header)); + break; + } + } + addr += cmd->size; + size -= cmd->size; + }; +} + +GUEST_CODE void guest_execute_code(uint32* insns, uint64 size) +{ + volatile void (*fn)() = (volatile void (*)())insns; + fn(); +} + +// Perform a userspace exit that can be handled by the host. +// The host returns from ioctl(KVM_RUN) with kvm_run.exit_reason=KVM_EXIT_MMIO, +// and can handle the call depending on the data passed as exit code. +GUEST_CODE void guest_uexit(uint64 exit_code) { - void (*guest_payload)() = (void (*)())ARM64_ADDR_USER_CODE; - guest_payload(); + volatile uint64* ptr = (volatile uint64*)ARM64_ADDR_UEXIT; + *ptr = exit_code; } -- cgit mrf-deployment