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
@@ -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;