diff --git a/tests/unit/Makefile b/tests/unit/Makefile index fd4f3eef..55f38be5 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -13,7 +13,7 @@ endif ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \ test_tb_x86 test_multihook test_pc_change test_x86_soft_paging \ - test_hookcounts + test_hookcounts test_hang .PHONY: all all: ${ALL_TESTS} @@ -35,6 +35,7 @@ test: ${ALL_TESTS} ./test_pc_change ./test_x86_soft_paging ./test_hookcounts + ./test_hang test_sanity: test_sanity.c test_x86: test_x86.c @@ -46,6 +47,7 @@ test_multihook: test_multihook.c test_pc_change: test_pc_change.c test_x86_soft_paging: test_x86_soft_paging.c test_hookcounts: test_hookcounts.c +test_hang: test_hang.c ${ALL_TESTS}: ${CC} ${CFLAGS} -o $@ $^ diff --git a/tests/unit/test_hang.c b/tests/unit/test_hang.c new file mode 100644 index 00000000..4f7a8fef --- /dev/null +++ b/tests/unit/test_hang.c @@ -0,0 +1,101 @@ +/* + refer to issue #575. + to run correctly unicorn needs to be compiled for AArch64. +*/ + +#include "unicorn_test.h" +#include + +uint64_t trunc_page(uint64_t addr) +{ + return (addr & ~(4095)); +} + +/* Called before every test to set up a new instance */ +static int init(void **state) +{ + printf("[+] Initializing Unicorn...\n"); + uc_engine *uc; + + if (uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc) != UC_ERR_OK) { + printf("Error on open. Be sure that your unicorn library supports AArch64.\n"); + return -1; + } + + *state = uc; + + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + printf("[+] Exiting...\n"); + uc_engine *uc = *state; + + uc_close(uc); + + *state = NULL; + return 0; +} + +void test_hang(void **state) +{ + uint32_t code[] = { + 0xd503201f, /* NOP */ + 0xd503201f, /* NOP */ + 0xd503201f, /* NOP */ + 0xaa0103e0 /* MOV X0, X1 */ + }; + + uc_engine *uc = *state; + + uint64_t x0 = 0; + uint64_t x1 = 1; + + /* + * emulation will hang if some instruction hits every quarter of a page, + * i.e. these offsets: + * 0x1400, 0x1800, 0x1c00, 0x2000 + * + * in this test, the code to be emulated is mapped just before the 0x1400 + * offset, so that the final instruction emulated (MOV X0, X1) hits the offset, + * causing the hang. + * If you try to write the code just four bytes behind, the hang doesn't occur. + * + * So far, this strange behaviour has only been observed with AArch64 Unicorn APIs. + */ + + uint64_t addr = 0x13f0; // try to map at (0x13f0 - 0x4) and the hang doesn't occur + uint64_t trunc_addr = trunc_page(addr); // round down to nearest page + + uc_mem_map(uc, trunc_addr, 2 * 1024 * 1024, UC_PROT_ALL); + + if (uc_mem_write(uc, addr, &code, sizeof(code))) { + printf("error on write\n"); + return; + } + + uc_reg_write(uc, UC_ARM64_REG_X0, &x0); + uc_reg_write(uc, UC_ARM64_REG_X1, &x1); + + if (uc_emu_start(uc, addr, addr + sizeof(code), 0, 0)) { + printf("error on start\n"); + return; + } + + uc_reg_read(uc, UC_ARM64_REG_X0, &x0); + uc_reg_read(uc, UC_ARM64_REG_X1, &x1); + + printf("x0: %#llx\n", x0); + printf("x1: %#llx\n", x1); +} + +int main(int argc, const char * argv[]) { + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_hang, init, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL);; +}