From abdb3ef2a4769d9c3f816004a3926504369baa24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Sat, 31 May 2014 19:36:56 +0200 Subject: [PATCH] x86: add read-before-write check at runtime Finding read-before-writes statically is quite difficult. In native we can use valgrind, but on boards without an MMU we are out of luck. x86 has an MMU, let's use it. If `-DDEBUG_READ_BEFORE_WRITE` was set, then pages on the heap get initialized upon first use. If the page was read before written, then a debug message with the virtual and physical address of the memory location is printed, as well as the address of the offending instruction. --- cpu/x86/include/x86_memory.h | 4 ++++ cpu/x86/x86_memory.c | 38 ++++++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/cpu/x86/include/x86_memory.h b/cpu/x86/include/x86_memory.h index c92ccf51f9..23ef3c661d 100644 --- a/cpu/x86/include/x86_memory.h +++ b/cpu/x86/include/x86_memory.h @@ -107,6 +107,10 @@ typedef uint64_t pae_page_table_t[512] __attribute__((aligned(0x1000))); #define PT_XD (1ull << 63) /**< 1 = no execute */ #define PT_ADDR_MASK (((1ull << 48) - 1) & ~((1ull << 12) - 1)) +#ifdef DEBUG_READ_BEFORE_WRITE +# define PT_HEAP_BIT PT_USR9 +#endif + #define PF_EC_P (1u << 0) /**< 1 = page protection violation; 0 = page not present */ #define PF_EC_W (1u << 1) /**< 1 = accessed writingly; 0 = readingly */ #define PF_EC_U (1u << 2) /**< 1 = ring 3 */ diff --git a/cpu/x86/x86_memory.c b/cpu/x86/x86_memory.c index ed461777bf..e78e49ec6f 100644 --- a/cpu/x86/x86_memory.c +++ b/cpu/x86/x86_memory.c @@ -46,7 +46,12 @@ #define PT_CR3_BITS (0) #define PT_PDPT_BITS (PT_P) #define PT_PD_BITS (PT_P | PT_RW | PT_US) -#define PT_HEAP_BITS (PT_P | PT_RW | PT_US | pt_xd) + +#ifndef DEBUG_READ_BEFORE_WRITE +# define PT_HEAP_BITS (PT_P | PT_RW | PT_US | pt_xd) +#else +# define PT_HEAP_BITS (PT_HEAP_BIT | PT_RW | PT_US | pt_xd) +#endif static uint64_t pt_xd = PT_XD; @@ -330,12 +335,41 @@ static void pagefault_handler(uint8_t intr_num, struct x86_pushad *orig_ctx, uns x86_hlt(); } +#ifdef DEBUG_READ_BEFORE_WRITE + uint32_t virtual_addr = cr2_read(); + uint64_t pte = x86_get_pte(virtual_addr); +#endif + if (error_code & PF_EC_I) { puts("Page fault while fetching instruction."); x86_print_registers(orig_ctx, error_code); puts("Halting."); x86_hlt(); } +#ifdef DEBUG_READ_BEFORE_WRITE + else if ((pte != NO_PTE) && !(pte & PT_P) && (pte & PT_HEAP_BIT)) { + /* mark as present */ + TEMP_PAGE.indices[(virtual_addr >> 12) % 512] |= PT_P; + asm volatile ("invlpg (%0)" :: "r"(virtual_addr)); + + /* initialize for easier debugging */ + uint32_t *p = (uint32_t *) (virtual_addr & ~0xfff); + for (unsigned i = 0; i < 0x1000 / 4; ++i) { + const union { + char str_value[4]; + uint32_t int_value; + } debug_init = { .str_value = "RIOT" }; + *p++ = debug_init.int_value; + } + + /* print a warning if the page was read before written */ + if (!(error_code & PF_EC_W)) { + unsigned long *sp = (void *) orig_ctx->sp; /* ip, cs, flags */ + printf("DEBUG: Read before write on heap address 0x%08x (physical: 0x%016llx) at 0x%08x.\n", + virtual_addr, pte & PT_ADDR_MASK, sp[0]); + } + } +#endif else if (error_code & PF_EC_P) { printf("Page fault: access violation while %s present page.\n", error_code & PF_EC_W ? "writing to" : "reading from"); x86_print_registers(orig_ctx, error_code); @@ -362,8 +396,8 @@ void x86_init_memory(void) check_requirements(); init_pagetable(); - init_free_pages(); init_pagefault_handler(); + init_free_pages(); puts("Virtual memory initialized"); }