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.
This commit is contained in:
Michael Lotz
2015-08-20 21:59:41 +02:00
parent 5d4501aa01
commit c4a9344a11
@@ -497,38 +497,30 @@ guarded_heap_add_area(guarded_heap& heap, uint32 counter)
static void* static void*
guarded_heap_allocate(guarded_heap& heap, size_t size, size_t alignment) guarded_heap_allocate_with_area(size_t size, size_t alignment)
{ {
if (alignment == 0) size_t infoSpace = alignment >= B_PAGE_SIZE ? B_PAGE_SIZE
alignment = 1; : (sizeof(guarded_heap_page) + alignment - 1) & ~(alignment - 1);
if (alignment > B_PAGE_SIZE) { size_t pagesNeeded = (size + infoSpace + B_PAGE_SIZE - 1) / 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 (alignment > B_PAGE_SIZE)
if (pagesNeeded * B_PAGE_SIZE >= GUARDED_HEAP_AREA_USE_THRESHOLD) { pagesNeeded += alignment / B_PAGE_SIZE - 1;
// 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; void* address = NULL;
area_id area = create_area("guarded_heap_huge_allocation", &address, area_id area = create_area("guarded_heap_huge_allocation", &address,
B_ANY_ADDRESS, (pagesNeeded + 1) * B_PAGE_SIZE, B_NO_LOCK, B_ANY_ADDRESS, (pagesNeeded + 1) * B_PAGE_SIZE, B_NO_LOCK,
B_READ_AREA | B_WRITE_AREA); B_READ_AREA | B_WRITE_AREA);
if (area < 0) { if (area < 0) {
panic("failed to create area for allocation of %" B_PRIuSIZE panic("failed to create area for allocation of %" B_PRIuSIZE " pages",
" pages", pagesNeeded); pagesNeeded);
return NULL; return NULL;
} }
// We just use a page object // We just use a page object
guarded_heap_page* page = (guarded_heap_page*)address; guarded_heap_page* page = (guarded_heap_page*)address;
page->flags = GUARDED_HEAP_PAGE_FLAG_USED page->flags = GUARDED_HEAP_PAGE_FLAG_USED | GUARDED_HEAP_PAGE_FLAG_FIRST
| GUARDED_HEAP_PAGE_FLAG_FIRST | GUARDED_HEAP_PAGE_FLAG_AREA; | GUARDED_HEAP_PAGE_FLAG_AREA;
page->allocation_size = size; page->allocation_size = size;
page->allocation_base = (void*)(((addr_t)address page->allocation_base = (void*)(((addr_t)address
+ pagesNeeded * B_PAGE_SIZE - size) & ~(alignment - 1)); + pagesNeeded * B_PAGE_SIZE - size) & ~(alignment - 1));
@@ -538,10 +530,44 @@ guarded_heap_allocate(guarded_heap& heap, size_t size, size_t alignment)
page->stack_trace, sStackTraceDepth, 2); page->stack_trace, sStackTraceDepth, 2);
page->free_stack_trace_depth = 0; 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), mprotect((void*)((addr_t)address + pagesNeeded * B_PAGE_SIZE),
B_PAGE_SIZE, 0); 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; return page->allocation_base;
}
static void*
guarded_heap_allocate(guarded_heap& heap, size_t size, size_t alignment)
{
if (alignment == 0)
alignment = 1;
size_t pagesNeeded = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE + 1;
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.
return guarded_heap_allocate_with_area(size, alignment);
} }
void* result = NULL; void* result = NULL;