add quarantine for large allocations

This commit is contained in:
Daniel Micay 2018-10-08 15:50:31 -04:00
parent cc9699f1b4
commit 1a10c17e8b
3 changed files with 38 additions and 5 deletions

View File

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

View File

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

View File

@ -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(&regions_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(&regions_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;