From 7f1eb4532dc4570d34ad51294a1fb91e452e24b9 Mon Sep 17 00:00:00 2001 From: "Takacs, Philipp" Date: Thu, 6 Oct 2022 15:53:25 +0200 Subject: [PATCH] add basic mmu tests Some simple tests for diffrent mmu. Basicly add some tlb entries, enable the mmu try to read from virtual address The aarm64 test was provided by imre-kis-arm in #1718 --- tests/unit/test_arm64.c | 101 ++++++++++++++++++++++++++++++++ tests/unit/test_riscv.c | 71 ++++++++++++++++++++++ tests/unit/test_x86.c | 126 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) diff --git a/tests/unit/test_arm64.c b/tests/unit/test_arm64.c index e82869fd..8fcc9f04 100644 --- a/tests/unit/test_arm64.c +++ b/tests/unit/test_arm64.c @@ -372,6 +372,106 @@ static void test_arm64_block_invalid_mem_read_write_sync(void) OK(uc_close(uc)); } +static void test_arm64_mmu(void) +{ + uc_engine *uc; + char *data; + char tlbe[8]; + uint64_t x0, x1, x2; + /* + * Not exact the binary, but aarch64-linux-gnu-as generate this code and reference sometimes data after ttb0_base. + * // Read data from physical address + * ldr X0, =0x40000000 + * ldr X1, [X0] + + * // Initialize translation table control registers + * ldr X0, =0x180803F20 + * msr TCR_EL1, X0 + * ldr X0, =0xFFFFFFFF + * msr MAIR_EL1, X0 + + * // Set translation table + * adr X0, ttb0_base + * msr TTBR0_EL1, X0 + + * // Enable caches and the MMU + * mrs X0, SCTLR_EL1 + * orr X0, X0, #(0x1 << 2) // The C bit (data cache). + * orr X0, X0, #(0x1 << 12) // The I bit (instruction cache) + * orr X0, X0, #0x1 // The M bit (MMU). + * msr SCTLR_EL1, X0 + * dsb SY + * isb + + * // Read the same memory area through virtual address + * ldr X0, =0x80000000 + * ldr X2, [X0] + * + * // Stop + * b . + */ + char code[] = "\x00\x81\x00\x58\x01\x00\x40\xf9\x00\x81\x00\x58\x40\x20\x18\xd5\x00\x81\x00\x58\x00\xa2\x18\xd5\x40\x7f\x00\x10\x00\x20\x18\xd5\x00\x10\x38\xd5\x00\x00\x7e\xb2\x00\x00\x74\xb2\x00\x00\x40\xb2\x00\x10\x18\xd5\x9f\x3f\x03\xd5\xdf\x3f\x03\xd5\xe0\x7f\x00\x58\x02\x00\x40\xf9\x00\x00\x00\x14\x1f\x20\x03\xd5\x1f\x20\x03\xd5\x1F\x20\x03\xD5\x1F\x20\x03\xD5"; + + data = malloc(0x1000); + TEST_CHECK(data != NULL); + + OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); + OK(uc_mem_map(uc, 0, 0x2000, UC_PROT_ALL)); + OK(uc_mem_write(uc, 0, code, sizeof(code) - 1)); + + // generate tlb entries + tlbe[0] = 0x41; + tlbe[1] = 0x07; + tlbe[2] = 0; + tlbe[3] = 0; + tlbe[4] = 0; + tlbe[5] = 0; + tlbe[6] = 0; + tlbe[7] = 0; + OK(uc_mem_write(uc, 0x1000, tlbe, sizeof(tlbe))); + tlbe[3] = 0x40; + OK(uc_mem_write(uc, 0x1008, tlbe, sizeof(tlbe))); + OK(uc_mem_write(uc, 0x1010, tlbe, sizeof(tlbe))); + OK(uc_mem_write(uc, 0x1018, tlbe, sizeof(tlbe))); + + //mentioned data referenced by the asm generated my aarch64-linux-gnu-as + tlbe[0] = 0; + tlbe[1] = 0; + OK(uc_mem_write(uc, 0x1020, tlbe, sizeof(tlbe))); + tlbe[0] = 0x20; + tlbe[1] = 0x3f; + tlbe[2] = 0x80; + tlbe[3] = 0x80; + tlbe[4] = 0x1; + OK(uc_mem_write(uc, 0x1028, tlbe, sizeof(tlbe))); + tlbe[0] = 0xff; + tlbe[1] = 0xff; + tlbe[2] = 0xff; + tlbe[3] = 0xff; + tlbe[4] = 0x00; + OK(uc_mem_write(uc, 0x1030, tlbe, sizeof(tlbe))); + tlbe[0] = 0x00; + tlbe[1] = 0x00; + tlbe[2] = 0x00; + tlbe[3] = 0x80; + OK(uc_mem_write(uc, 0x1038, tlbe, sizeof(tlbe))); + + for (size_t i = 0; i < 0x1000; i++) { + data[i] = 0x44; + } + OK(uc_mem_map_ptr(uc, 0x40000000, 0x1000, UC_PROT_READ, data)); + + OK(uc_emu_start(uc, 0, 0x44, 0, 0)); + OK(uc_reg_read(uc, UC_ARM64_REG_X0, &x0)); + OK(uc_reg_read(uc, UC_ARM64_REG_X1, &x1)); + OK(uc_reg_read(uc, UC_ARM64_REG_X2, &x2)); + + TEST_CHECK(x0 == 0x80000000); + TEST_CHECK(x1 == 0x4444444444444444); + TEST_CHECK(x2 == 0x4444444444444444); + free(data); +} + TEST_LIST = {{"test_arm64_until", test_arm64_until}, {"test_arm64_code_patching", test_arm64_code_patching}, {"test_arm64_code_patching_count", test_arm64_code_patching_count}, @@ -385,4 +485,5 @@ TEST_LIST = {{"test_arm64_until", test_arm64_until}, {"test_arm64_block_sync_pc", test_arm64_block_sync_pc}, {"test_arm64_block_invalid_mem_read_write_sync", test_arm64_block_invalid_mem_read_write_sync}, + {"test_arm64_mmu", test_arm64_mmu}, {NULL, NULL}}; diff --git a/tests/unit/test_riscv.c b/tests/unit/test_riscv.c index 26e92b9c..00dbc08d 100644 --- a/tests/unit/test_riscv.c +++ b/tests/unit/test_riscv.c @@ -634,6 +634,76 @@ static void test_riscv_correct_address_in_long_jump_hook(void) OK(uc_close(uc)); } +static void test_riscv_mmu_prepare_tlb(uc_engine *uc, uint32_t data_address, uint32_t code_address) +{ + uint64_t tlbe; + uint32_t sptbr = 0x2000; + + OK(uc_mem_map(uc, sptbr, 0x3000, UC_PROT_ALL)); //tlb base + + tlbe = ((sptbr + 0x1000) >> 2) | 1; + OK(uc_mem_write(uc, sptbr, &tlbe, sizeof(tlbe))); + tlbe = ((sptbr + 0x2000) >> 2) | 1; + OK(uc_mem_write(uc, sptbr + 0x1000, &tlbe, sizeof(tlbe))); + + tlbe = (code_address >> 2) | (7 << 1) | 1; + OK(uc_mem_write(uc, sptbr + 0x2000 + 0x15*8, &tlbe, sizeof(tlbe))); + + tlbe = (data_address >> 2) | (7 << 1) | 1; + OK(uc_mem_write(uc, sptbr + 0x2000 + 0x16*8, &tlbe, sizeof(tlbe))); +} + +static void test_riscv_mmu_hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *userdata) +{ + if (address == 0x15010) { + OK(uc_emu_stop(uc)); + } +} + +static void test_riscv_mmu(void) +{ + uc_engine *uc; + uc_hook h; + uint32_t code_address = 0x5000; + uint32_t data_address = 0x6000; + uint32_t data_value = 0x41414141; + uint32_t data_result = 0; + + /* + li t3, (8 << 60) | 2 + csrw sptbr, t3 + li t0, (1 << 11) | (1 << 5) + csrw mstatus, t0 + la t1, 0x15000 + csrw mepc, t1 + mret + */ + char code_m[] = "\x1b\x0e\xf0\xff" "\x13\x1e\xfe\x03" "\x13\x0e\x2e\x00" "\x73\x10\x0e\x18" "\xb7\x12\x00\x00" "\x9b\x82\x02\x82" "\x73\x90\x02\x30" "\x37\x53\x01\x00" "\x73\x10\x13\x34" "\x73\x00\x20\x30"; + + /* + li t0, 0x41414141 + li t1, 0x16000 + sw t0, 0(t1) + nop + */ + char code_s[] = "\xb7\x42\x41\x41" "\x9b\x82\x12\x14" "\x37\x63\x01\x00" "\x23\x20\x53\x00" "\x13\x00\x00\x00"; + + OK(uc_open(UC_ARCH_RISCV, UC_MODE_RISCV64, &uc)); + OK(uc_hook_add(uc, &h, UC_HOOK_CODE, test_riscv_mmu_hook_code, NULL, 1, 0)); + OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL)); + OK(uc_mem_map(uc, code_address, 0x1000, UC_PROT_ALL)); + OK(uc_mem_map(uc, data_address, 0x1000, UC_PROT_ALL)); + OK(uc_mem_write(uc, code_address, &code_s, sizeof(code_s))); + OK(uc_mem_write(uc, 0x1000, &code_m, sizeof(code_m))); + + test_riscv_mmu_prepare_tlb(uc, data_address, code_address); + + OK(uc_emu_start(uc, 0x1000, sizeof(code_m) - 1, 0, 0)); + OK(uc_mem_read(uc, data_address, &data_result, sizeof(data_result))); + + TEST_CHECK(data_value == data_result); +} + TEST_LIST = { {"test_riscv32_nop", test_riscv32_nop}, {"test_riscv64_nop", test_riscv64_nop}, @@ -657,4 +727,5 @@ TEST_LIST = { test_riscv_correct_address_in_small_jump_hook}, {"test_riscv_correct_address_in_long_jump_hook", test_riscv_correct_address_in_long_jump_hook}, + {"test_riscv_mmu", test_riscv_mmu}, {NULL, NULL}}; diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 110e4b41..9bcb7d81 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -1259,6 +1259,131 @@ static void test_x86_16_incorrect_ip(void) OK(uc_close(uc)); } +static void test_x86_mmu_prepare_tlb(uc_engine *uc, uint64_t vaddr, uint64_t tlb_base) +{ + uint64_t cr0; + uint64_t cr4; + uc_x86_msr msr = {.rid = 0x0c0000080, .value = 0}; + uint64_t pml4o = ((vaddr & 0x00ff8000000000) >> 39)*8; + uint64_t pdpo = ((vaddr & 0x00007fc0000000) >> 30)*8; + uint64_t pdo = ((vaddr & 0x0000003fe00000) >> 21)*8; + uint64_t pml4e = (tlb_base + 0x1000) | 1 | (1 << 2); + uint64_t pdpe = (tlb_base + 0x2000) | 1 | (1 << 2); + uint64_t pde = (tlb_base + 0x3000) | 1 | (1 << 2); + OK(uc_mem_write(uc, tlb_base + pml4o, &pml4e, sizeof(pml4o))); + OK(uc_mem_write(uc, tlb_base + 0x1000 + pdpo, &pdpe, sizeof(pdpe))); + OK(uc_mem_write(uc, tlb_base + 0x2000 + pdo, &pde, sizeof(pde))); + OK(uc_reg_write(uc, UC_X86_REG_CR3, &tlb_base)); + OK(uc_reg_read(uc, UC_X86_REG_CR0, &cr0)); + OK(uc_reg_read(uc, UC_X86_REG_CR4, &cr4)); + OK(uc_reg_read(uc, UC_X86_REG_MSR, &msr)); + cr0 |= 1; + cr0 |= 1l << 31; + cr4 |= 1l << 5; + msr.value |= 1l << 8; + OK(uc_reg_write(uc, UC_X86_REG_CR0, &cr0)); + OK(uc_reg_write(uc, UC_X86_REG_CR4, &cr4)); + OK(uc_reg_write(uc, UC_X86_REG_MSR, &msr)); +} + +static void test_x86_mmu_pt_set(uc_engine *uc, uint64_t vaddr, uint64_t paddr, uint64_t tlb_base) +{ + uint64_t pto = ((vaddr & 0x000000001ff000) >> 12)*8; + uint32_t pte = (paddr) | 1 | (1 << 2); + uc_mem_write(uc, tlb_base + 0x3000 + pto, &pte, sizeof(pte)); +} + +static void test_x86_mmu_callback(uc_engine *uc, void *userdata) +{ + bool *parrent_done = userdata; + uint64_t rax; + OK(uc_reg_read(uc, UC_X86_REG_RAX, &rax)); + switch (rax) { + case 57: + /* fork */ + break; + case 60: + /* exit */ + uc_emu_stop(uc); + return; + default: + TEST_CHECK(false); + } + + if (!(*parrent_done)) { + *parrent_done = true; + rax = 27; + OK(uc_reg_write(uc, UC_X86_REG_RAX, &rax)); + uc_emu_stop(uc); + } +} + +static void test_x86_mmu(void) +{ + bool parrent_done = false; + uint64_t tlb_base = 0x3000; + uint64_t parrent, child; + uint64_t rax, rip; + uc_context *context; + uc_engine *uc; + uc_hook h1; + + /* + * mov rax, 57 + * syscall + * test rax, rax + * jz child + * xor rax, rax + * mov rax, 60 + * mov [0x4000], rax + * syscall + * + * child: + * xor rcx, rcx + * mov rcx, 42 + * mov [0x4000], rcx + * mov rax, 60 + * syscall + */ + char code[] = "\xB8\x39\x00\x00\x00\x0F\x05\x48\x85\xC0\x74\x0F\xB8\x3C\x00\x00\x00\x48\x89\x04\x25\x00\x40\x00\x00\x0F\x05\xB9\x2A\x00\x00\x00\x48\x89\x0C\x25\x00\x40\x00\x00\xB8\x3C\x00\x00\x00\x0F\x05"; + + OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); + OK(uc_hook_add(uc, &h1, UC_HOOK_INSN, &test_x86_mmu_callback, &parrent_done, 1, 0, UC_X86_INS_SYSCALL)); + OK(uc_context_alloc(uc, &context)); + + OK(uc_mem_map(uc, 0x0, 0x1000, UC_PROT_ALL)); //Code + OK(uc_mem_write(uc, 0x0, code, sizeof(code) - 1)); + OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL)); //Parrent + OK(uc_mem_map(uc, 0x2000, 0x1000, UC_PROT_ALL)); //Child + OK(uc_mem_map(uc, tlb_base, 0x4000, UC_PROT_ALL)); //TLB + + test_x86_mmu_prepare_tlb(uc, 0x0, tlb_base); + test_x86_mmu_pt_set(uc, 0x2000, 0x0, tlb_base); + test_x86_mmu_pt_set(uc, 0x4000, 0x1000, tlb_base); + + OK(uc_ctl_flush_tlb(uc)); + OK(uc_emu_start(uc, 0x2000, 0x0, 0, 0)); + + OK(uc_context_save(uc, context)); + OK(uc_reg_read(uc, UC_X86_REG_RIP, &rip)); + + OK(uc_emu_start(uc, rip, 0x0, 0, 0)); + + /* restore for child */ + OK(uc_context_restore(uc, context)); + test_x86_mmu_prepare_tlb(uc, 0x0, tlb_base); + test_x86_mmu_pt_set(uc, 0x4000, 0x2000, tlb_base); + rax = 0; + OK(uc_reg_write(uc, UC_X86_REG_RAX, &rax)); + OK(uc_ctl_flush_tlb(uc)); + + OK(uc_emu_start(uc, rip, 0x0, 0, 0)); + OK(uc_mem_read(uc, 0x1000, &parrent, sizeof(parrent))); + OK(uc_mem_read(uc, 0x2000, &child, sizeof(child))); + TEST_CHECK(parrent == 60); + TEST_CHECK(child == 42); +} + TEST_LIST = { {"test_x86_in", test_x86_in}, {"test_x86_out", test_x86_out}, @@ -1302,4 +1427,5 @@ TEST_LIST = { #endif {"test_x86_lazy_mapping", test_x86_lazy_mapping}, {"test_x86_16_incorrect_ip", test_x86_16_incorrect_ip}, + {"test_x86_mmu", test_x86_mmu}, {NULL, NULL}};