From e4fe6b58b46ccc8e3967b856226e55533d060c0b Mon Sep 17 00:00:00 2001 From: coco Date: Tue, 8 Dec 2015 18:23:06 +0100 Subject: [PATCH] added test for memory quirks --- tests/unit/Makefile | 3 +- tests/unit/test_mem_high.c | 97 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 tests/unit/test_mem_high.c diff --git a/tests/unit/Makefile b/tests/unit/Makefile index ae66d08e..09890b59 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -4,7 +4,7 @@ CFLAGS += -L ../../ CFLAGS += -lcmocka -lunicorn CFLAGS += -I ../../include -ALL_TESTS = test_sanity test_x86 test_mem_map +ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high .PHONY: all all: ${ALL_TESTS} @@ -23,6 +23,7 @@ test: ${ALL_TESTS} test_sanity: test_sanity.c test_x86: test_x86.c test_mem_map: test_mem_map.c +test_mem_high: test_mem_high.c ${ALL_TESTS}: gcc ${CFLAGS} -o $@ $^ diff --git a/tests/unit/test_mem_high.c b/tests/unit/test_mem_high.c new file mode 100644 index 00000000..83fe312f --- /dev/null +++ b/tests/unit/test_mem_high.c @@ -0,0 +1,97 @@ +/** + * Unicorn memory API tests + * + * This tests memory read/write and map/unmap functionality. + * One is necessary for doing the other. + */ +#include "unicorn_test.h" +#include +#include + +/* Called before every test to set up a new instance */ +static int setup(void **state) +{ + uc_engine *uc; + + uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); + + *state = uc; + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + uc_assert_success(uc_close(uc)); + + *state = NULL; + return 0; +} + +/******************************************************************************/ + +// mapping the last pages will silently fail +static void test_last_page_map(void **state) +{ + uc_engine *uc = *state; + + uint8_t writebuf[0x10]; + memset(writebuf, 0xCC, sizeof(writebuf)); + + const uint64_t mem_len = 0x1000; + const uint64_t last_page = 0xfffffffffffff000; + uc_assert_success(uc_mem_map(uc, last_page, mem_len, UC_PROT_NONE)); + uc_assert_success(uc_mem_write(uc, last_page, writebuf, sizeof(writebuf))); +} + +// segfaults with NULL-deref (caused by UC_PROT_NONE) +static void test_nullptr_deref_wrong_perms(void **state){ + uc_engine *uc = *state; + const uint64_t base_addr = 0x400000; + uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_NONE)); + uc_emu_start(uc, base_addr, base_addr + 1, 0, 0); +} + +static int number_of_memory_reads = 0; + +static void hook_mem64(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data) +{ + number_of_memory_reads += 1; + printf(">>> Memory is being accessed at 0x%lx, data size = %u\n", address, size); +} + +//if a read is performed from a big address whith a non-zero last digit, multiple read events are triggered +static void test_high_address_reads(void **state) +{ + uc_engine *uc = *state; + uc_hook trace2; + + uint64_t addr = 0x0010000000000001; + //addr = 0x0010000000000000; // uncomment to fix wrong? behaviour + //addr = 90000000; // uncomment to fix wrong? behaviour + // + uc_mem_map(uc, addr-(addr%4096), 4096*2, UC_PROT_ALL); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &addr)); + const uint64_t base_addr = 0x40000; + uint8_t code[] = {0x48,0x8b,0x00,0x90,0x90,0x90,0x90}; // mov rax, [rax], nops + uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL)); + uc_assert_success(uc_mem_write(uc, base_addr, code, 7)); + uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_MEM_READ, hook_mem64, NULL, (uint64_t)1, (uint64_t)0)); + uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0)); + if(number_of_memory_reads != 1) { + fail_msg("wrong number of memory reads for instruction %i", number_of_memory_reads); + } +} + +int main(void) { +#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown) + const struct CMUnitTest tests[] = { + test(test_last_page_map), + test(test_high_address_reads), + test(test_nullptr_deref_wrong_perms), + }; +#undef test + return cmocka_run_group_tests(tests, NULL, NULL); +}