From e4fe6b58b46ccc8e3967b856226e55533d060c0b Mon Sep 17 00:00:00 2001
From: coco <coco@hexgolems.com>
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 <stdio.h>
+#include <string.h>
+
+/* 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);
+}