From c4a9344a117d3c918506c5d91300a6ce3554c7a2 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Thu, 20 Aug 2015 21:59:41 +0200 Subject: [PATCH] libroot_debug: Support alignments > B_PAGE_SIZE in guarded heap. These are always allocated using an area. The allocation size is increased as to guarantee the availability of a suitable address. The pages between the allocation info and the actual, aligned start address and the pages past the allocation end are then protected. This commit also fixes corruption of the allocation info for large allocations that used areas. The alignment wasn't taken into account when calculating the amount of space needed. The alignment could then lead to rounding down the allocation start such that it would overlap with the allocation info. --- .../posix/malloc_debug/guarded_heap.cpp | 100 +++++++++++------- 1 file changed, 63 insertions(+), 37 deletions(-) diff --git a/src/system/libroot/posix/malloc_debug/guarded_heap.cpp b/src/system/libroot/posix/malloc_debug/guarded_heap.cpp index f527ff527d..63c8ee0b53 100644 --- a/src/system/libroot/posix/malloc_debug/guarded_heap.cpp +++ b/src/system/libroot/posix/malloc_debug/guarded_heap.cpp @@ -496,52 +496,78 @@ guarded_heap_add_area(guarded_heap& heap, uint32 counter) } +static void* +guarded_heap_allocate_with_area(size_t size, size_t alignment) +{ + size_t infoSpace = alignment >= B_PAGE_SIZE ? B_PAGE_SIZE + : (sizeof(guarded_heap_page) + alignment - 1) & ~(alignment - 1); + + size_t pagesNeeded = (size + infoSpace + B_PAGE_SIZE - 1) / B_PAGE_SIZE; + + if (alignment > B_PAGE_SIZE) + pagesNeeded += alignment / B_PAGE_SIZE - 1; + + void* address = NULL; + area_id area = create_area("guarded_heap_huge_allocation", &address, + B_ANY_ADDRESS, (pagesNeeded + 1) * B_PAGE_SIZE, B_NO_LOCK, + B_READ_AREA | B_WRITE_AREA); + if (area < 0) { + panic("failed to create area for allocation of %" B_PRIuSIZE " pages", + pagesNeeded); + return NULL; + } + + // We just use a page object + guarded_heap_page* page = (guarded_heap_page*)address; + page->flags = GUARDED_HEAP_PAGE_FLAG_USED | GUARDED_HEAP_PAGE_FLAG_FIRST + | GUARDED_HEAP_PAGE_FLAG_AREA; + page->allocation_size = size; + page->allocation_base = (void*)(((addr_t)address + + pagesNeeded * B_PAGE_SIZE - size) & ~(alignment - 1)); + page->alignment = alignment; + page->thread = find_thread(NULL); + page->alloc_stack_trace_depth = guarded_heap_fill_stack_trace( + page->stack_trace, sStackTraceDepth, 2); + page->free_stack_trace_depth = 0; + + if (alignment <= B_PAGE_SIZE) { + // Protect just the guard page. + mprotect((void*)((addr_t)address + pagesNeeded * B_PAGE_SIZE), + B_PAGE_SIZE, 0); + } else { + // Protect empty pages before the allocation start... + addr_t protectedStart = (addr_t)address + B_PAGE_SIZE; + size_t protectedSize = (addr_t)page->allocation_base - protectedStart; + if (protectedSize > 0) + mprotect((void*)protectedStart, protectedSize, 0); + + // ... and after allocation end. + size_t allocatedPages = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE; + protectedStart = (addr_t)page->allocation_base + + allocatedPages * B_PAGE_SIZE; + protectedSize = (addr_t)address + (pagesNeeded + 1) * B_PAGE_SIZE + - protectedStart; + + // There is at least the guard page. + mprotect((void*)protectedStart, protectedSize, 0); + } + + return page->allocation_base; +} + + static void* guarded_heap_allocate(guarded_heap& heap, size_t size, size_t alignment) { if (alignment == 0) alignment = 1; - if (alignment > B_PAGE_SIZE) { - panic("alignment of %" B_PRIuSIZE " not supported", alignment); - return NULL; - } - size_t pagesNeeded = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE + 1; - if (pagesNeeded * B_PAGE_SIZE >= GUARDED_HEAP_AREA_USE_THRESHOLD) { + if (alignment > B_PAGE_SIZE + || pagesNeeded * B_PAGE_SIZE >= GUARDED_HEAP_AREA_USE_THRESHOLD) { // Don't bother, use an area directly. Since it will also fault once // it is deleted, that fits our model quite nicely. - - pagesNeeded = (size + sizeof(guarded_heap_page) + B_PAGE_SIZE - 1) - / B_PAGE_SIZE; - - void* address = NULL; - area_id area = create_area("guarded_heap_huge_allocation", &address, - B_ANY_ADDRESS, (pagesNeeded + 1) * B_PAGE_SIZE, B_NO_LOCK, - B_READ_AREA | B_WRITE_AREA); - if (area < 0) { - panic("failed to create area for allocation of %" B_PRIuSIZE - " pages", pagesNeeded); - return NULL; - } - - // We just use a page object - guarded_heap_page* page = (guarded_heap_page*)address; - page->flags = GUARDED_HEAP_PAGE_FLAG_USED - | GUARDED_HEAP_PAGE_FLAG_FIRST | GUARDED_HEAP_PAGE_FLAG_AREA; - page->allocation_size = size; - page->allocation_base = (void*)(((addr_t)address - + pagesNeeded * B_PAGE_SIZE - size) & ~(alignment - 1)); - page->alignment = alignment; - page->thread = find_thread(NULL); - page->alloc_stack_trace_depth = guarded_heap_fill_stack_trace( - page->stack_trace, sStackTraceDepth, 2); - page->free_stack_trace_depth = 0; - - mprotect((void*)((addr_t)address + pagesNeeded * B_PAGE_SIZE), - B_PAGE_SIZE, 0); - - return page->allocation_base; + return guarded_heap_allocate_with_area(size, alignment); } void* result = NULL;