aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2024-12-03 16:47:21 +0100
committerAlexander Potapenko <glider@google.com>2024-12-05 10:57:12 +0000
commit1eac5906dd00ebafa761f57a933bedec8f09c7ba (patch)
tree771983c6436d8f97e9672dbc0ee8b8d3db81a592
parentd49ca8747d8174f229b838649098d8eb684dc75b (diff)
executor: arm64: detect data relocations in SyzOS
Detect and report ADRP instructions in the linked binaries to avoid crashes inside SyzOS. See https://github.com/google/syzkaller/issues/5565 for more context.
-rw-r--r--executor/common_kvm_arm64.h29
1 files changed, 28 insertions, 1 deletions
diff --git a/executor/common_kvm_arm64.h b/executor/common_kvm_arm64.h
index dcd83aa6b..59b01201a 100644
--- a/executor/common_kvm_arm64.h
+++ b/executor/common_kvm_arm64.h
@@ -68,6 +68,33 @@ static void vm_set_user_memory_region(int vmfd, uint32 slot, uint32 flags, uint6
ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
}
+#define ADRP_OPCODE 0x90000000
+#define ADRP_OPCODE_MASK 0x9f000000
+
+// Code loading SyzOS into guest memory does not handle data relocations (see
+// https://github.com/google/syzkaller/issues/5565), so SyzOS will crash soon after encountering an
+// ADRP instruction. Detect these instructions to catch regressions early.
+// The most common reason for using data relocaions is accessing global variables and constants.
+// Sometimes the compiler may choose to emit a read-only constant to zero-initialize a structure
+// or to generate a jump table for a switch statement.
+static void validate_guest_code(void* mem, size_t size)
+{
+ uint32* insns = (uint32*)mem;
+ for (size_t i = 0; i < size / 4; i++) {
+ if ((insns[i] & ADRP_OPCODE_MASK) == ADRP_OPCODE)
+ fail("ADRP instruction detected in SyzOS, exiting");
+ }
+}
+
+static void install_syzos_code(void* host_mem, size_t mem_size)
+{
+ size_t size = (char*)&__stop_guest - (char*)&__start_guest;
+ if (size > mem_size)
+ fail("SyzOS size exceeds guest memory");
+ memcpy(host_mem, &__start_guest, size);
+ validate_guest_code(host_mem, size);
+}
+
static void setup_vm(int vmfd, void* host_mem, void** text_slot)
{
// Guest physical memory layout (must be in sync with executor/kvm.h):
@@ -84,7 +111,7 @@ static void setup_vm(int vmfd, void* host_mem, void** text_slot)
int slot = 0; // Slot numbers do not matter, they just have to be different.
struct addr_size host_text = alloc_guest_mem(&allocator, 4 * KVM_PAGE_SIZE);
- memcpy(host_text.addr, &__start_guest, (char*)&__stop_guest - (char*)&__start_guest);
+ install_syzos_code(host_text.addr, host_text.size);
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 * KVM_PAGE_SIZE);