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}};