Author: Vegard Nossum <vegard.nossum@xxxxxxxxx> AuthorDate: Sun, 22 Feb 2009 22:28:32 +0100 Commit: Vegard Nossum <vegard.nossum@xxxxxxxxx> CommitDate: Sun, 22 Feb 2009 23:22:13 +0100 kmemcheck: fix shadow updates that cross page boundaries This patch fixes a memory corruption. For page allocations of order > 0 and possibly also scatter-gather DMA, kmemcheck_mark_initialized() might cross page boundaries for the tracked object. kmemcheck would then do a memset() on the shadow bytemap, but the memset could cross into another (arbitrary) page. Fix it by breaking up the shadow update across page boundaries, and memset() each page separately. Signed-off-by: Vegard Nossum <vegard.nossum@xxxxxxxxx> --- arch/x86/mm/kmemcheck/shadow.c | 38 ++++++++++++++++++++++++++++++++++---- 1 files changed, 34 insertions(+), 4 deletions(-) diff --git a/arch/x86/mm/kmemcheck/shadow.c b/arch/x86/mm/kmemcheck/shadow.c index aab8c18..e773b6b 100644 --- a/arch/x86/mm/kmemcheck/shadow.c +++ b/arch/x86/mm/kmemcheck/shadow.c @@ -37,12 +37,42 @@ void *kmemcheck_shadow_lookup(unsigned long address) static void mark_shadow(void *address, unsigned int n, enum kmemcheck_shadow status) { + unsigned long addr = (unsigned long) address; + unsigned long last_addr = addr + n - 1; + unsigned long page = addr & PAGE_MASK; + unsigned long last_page = last_addr & PAGE_MASK; + unsigned int first_n; void *shadow; - shadow = kmemcheck_shadow_lookup((unsigned long) address); - if (!shadow) - return; - memset(shadow, status, n); + /* If the memory range crosses a page boundary, stop there. */ + if (page == last_page) + first_n = n; + else + first_n = page + PAGE_SIZE - addr; + + shadow = kmemcheck_shadow_lookup(addr); + if (shadow) + memset(shadow, status, first_n); + + addr += first_n; + n -= first_n; + + /* Do full-page memset()s. */ + while (n >= PAGE_SIZE) { + shadow = kmemcheck_shadow_lookup(addr); + if (shadow) + memset(shadow, status, PAGE_SIZE); + + addr += PAGE_SIZE; + n -= PAGE_SIZE; + } + + /* Do the remaining page, if any. */ + if (n > 0) { + shadow = kmemcheck_shadow_lookup(addr); + if (shadow) + memset(shadow, status, n); + } } void kmemcheck_mark_unallocated(void *address, unsigned int n) -- To unsubscribe from this list: send the line "unsubscribe linux-tip-commits" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html