From ddd54114724f5c2adefa2f4c5249c1af13295327 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 2 Dec 2019 18:51:57 +0100 Subject: [PATCH] LibC: Protect empty-but-kept-around ChunkedBlocks with PROT_NONE We now keep a separate queue of empty ChunkedBlocks in each allocator. The underlying memory for each block is mprotect'ed with PROT_NONE to provoke crashes on use-after-free. This is not going to catch *all* use-after-frees, but if it catches some, that's still pretty nice. :^) The malloc memory region names are now updated to reflect their reuse status: "malloc: ChunkedBlock(size) (free/reused)" --- Libraries/LibC/malloc.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/Libraries/LibC/malloc.cpp b/Libraries/LibC/malloc.cpp index db3fc2beeb..a78cf41d34 100644 --- a/Libraries/LibC/malloc.cpp +++ b/Libraries/LibC/malloc.cpp @@ -89,6 +89,7 @@ struct ChunkedBlock : public CommonHeader struct Allocator { size_t size { 0 }; size_t block_count { 0 }; + ChunkedBlock* empty_block_queue { nullptr }; InlineLinkedList usable_blocks; InlineLinkedList full_blocks; }; @@ -176,6 +177,20 @@ void* malloc(size_t size) break; } + if (!block && allocator->empty_block_queue) { + block = allocator->empty_block_queue; + int rc = mprotect(block, PAGE_SIZE, PROT_READ | PROT_WRITE); + if (rc < 0) { + perror("mprotect"); + ASSERT_NOT_REACHED(); + } + allocator->empty_block_queue = block->m_next; + char buffer[64]; + snprintf(buffer, sizeof(buffer), "malloc: ChunkedBlock(%zu) (reused)", good_size); + set_mmap_name(block, PAGE_SIZE, buffer); + allocator->usable_blocks.append(block); + } + if (!block) { char buffer[64]; snprintf(buffer, sizeof(buffer), "malloc: ChunkedBlock(%zu)", good_size); @@ -262,13 +277,13 @@ void free(void* ptr) #ifdef MALLOC_DEBUG dbgprintf("Keeping block %p around for size class %u\n", block, good_size); #endif - if (allocator->usable_blocks.tail() != block) { -#ifdef MALLOC_DEBUG - dbgprintf("Moving block %p to tail of list for size class %u\n", block, good_size); -#endif - allocator->usable_blocks.remove(block); - allocator->usable_blocks.append(block); - } + allocator->usable_blocks.remove(block); + block->m_next = allocator->empty_block_queue; + allocator->empty_block_queue = block; + char buffer[64]; + snprintf(buffer, sizeof(buffer), "malloc: ChunkedBlock(%zu) (free)", good_size); + set_mmap_name(block, PAGE_SIZE, buffer); + mprotect(block, PAGE_SIZE, PROT_NONE); return; } #ifdef MALLOC_DEBUG