From 1a10c17e8b8073a3343aaa1ea1db3670d84842d7 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Mon, 8 Oct 2018 15:50:31 -0400 Subject: [PATCH] add quarantine for large allocations --- README.md | 9 ++++++++- config.h | 2 ++ malloc.c | 32 ++++++++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8211db4..5d78d5c 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,8 @@ features with a significant performance or memory usage cost. #define SLAB_CANARY true #define GUARD_SLABS_INTERVAL 1 #define GUARD_SIZE_DIVISOR 2 +#define REGION_QUARANTINE_SIZE 1024 +#define REGION_QUARANTINE_SKIP_THRESHOLD (32 * 1024 * 1024) ``` There will be more control over enabled features in the future along with @@ -127,7 +129,12 @@ allocation and then unmapped on free. * [in-progress] Randomized delayed free for slab allocations * [in-progress] Randomized allocation of slabs * [more randomization coming as the implementation is matured] -* Slab allocations are zeroed on free and large allocations are unmapped +* Slab allocations are zeroed on free +* Large allocations are purged and memory protected on free with the memory + mapping kept reserved in a quarantine to detect use-after-free + * The quarantine is a FIFO ring buffer, with the oldest mapping in the + quarantine being unmapped to make room for the most recently freed + mapping * Detection of write-after-free by verifying zero filling is intact * Memory in fresh allocations is consistently zeroed due to it either being fresh pages or zeroed on free after previous usage diff --git a/config.h b/config.h index bd6aa0e..3258aa3 100644 --- a/config.h +++ b/config.h @@ -9,5 +9,7 @@ #define SLAB_CANARY true #define GUARD_SLABS_INTERVAL 1 #define GUARD_SIZE_DIVISOR 2 +#define REGION_QUARANTINE_SIZE 1024 +#define REGION_QUARANTINE_SKIP_THRESHOLD (32 * 1024 * 1024) #endif diff --git a/malloc.c b/malloc.c index 05ee42c..d569946 100644 --- a/malloc.c +++ b/malloc.c @@ -522,6 +522,27 @@ static struct region_info *regions; static size_t regions_total = initial_region_table_size; static size_t regions_free = initial_region_table_size; static struct mutex regions_lock = MUTEX_INITIALIZER; +static struct region_info regions_quarantine[REGION_QUARANTINE_SIZE]; +static size_t regions_quarantine_index; + +static void regions_quarantine_deallocate_pages(void *p, size_t size, size_t guard_size) { + if (size >= REGION_QUARANTINE_SKIP_THRESHOLD) { + deallocate_pages(p, size, guard_size); + return; + } + + if (unlikely(memory_map_fixed(p, size))) { + deallocate_pages(p, size, guard_size); + return; + } + + struct region_info old = regions_quarantine[regions_quarantine_index]; + if (old.p != NULL) { + deallocate_pages(old.p, old.size, old.guard_size); + } + regions_quarantine[regions_quarantine_index] = (struct region_info){p, size, guard_size}; + regions_quarantine_index = (regions_quarantine_index + 1) % REGION_QUARANTINE_SIZE; +} static size_t hash_page(void *p) { uintptr_t u = (uintptr_t)p >> PAGE_SHIFT; @@ -792,7 +813,7 @@ static void deallocate_large(void *p, size_t *expected_size) { regions_delete(region); mutex_unlock(®ions_lock); - deallocate_pages(p, size, guard_size); + regions_quarantine_deallocate_pages(p, size, guard_size); } static size_t adjust_size_for_canaries(size_t size) { @@ -829,7 +850,10 @@ EXPORT void *h_calloc(size_t nmemb, size_t size) { return p; } -static const size_t mremap_threshold = 4 * 1024 * 1024; +#define MREMAP_THRESHOLD (32 * 1024 * 1024) + +static_assert(MREMAP_THRESHOLD >= REGION_QUARANTINE_SKIP_THRESHOLD, + "mremap threshold must be above region quarantine limit"); EXPORT void *h_realloc(void *old, size_t size) { if (old == NULL) { @@ -874,7 +898,7 @@ EXPORT void *h_realloc(void *old, size_t size) { return NULL; } void *new_guard_end = (char *)new_end + old_guard_size; - memory_unmap(new_guard_end, old_rounded_size - rounded_size); + regions_quarantine_deallocate_pages(new_guard_end, old_rounded_size - rounded_size, 0); mutex_lock(®ions_lock); struct region_info *region = regions_find(old); @@ -907,7 +931,7 @@ EXPORT void *h_realloc(void *old, size_t size) { } size_t copy_size = size < old_size ? size : old_size; - if (copy_size >= mremap_threshold) { + if (copy_size >= MREMAP_THRESHOLD) { void *new = allocate(size); if (new == NULL) { return NULL;