add initial slab allocation quarantine
parent
fea335282a
commit
3a488c9a27
|
@ -107,6 +107,8 @@ and will be migrated to the main configuration when proper sanity checks and
|
||||||
documentation are written. The following advanced options are available:
|
documentation are written. The following advanced options are available:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
#define SLAB_QUARANTINE_RANDOM_SIZE 0
|
||||||
|
#define SLAB_QUARANTINE_QUEUE_SIZE 0
|
||||||
#define GUARD_SLABS_INTERVAL 1
|
#define GUARD_SLABS_INTERVAL 1
|
||||||
#define GUARD_SIZE_DIVISOR 2
|
#define GUARD_SIZE_DIVISOR 2
|
||||||
#define REGION_QUARANTINE_RANDOM_SIZE 128
|
#define REGION_QUARANTINE_RANDOM_SIZE 128
|
||||||
|
@ -189,7 +191,7 @@ was a bit less important and if a core goal was finding latent bugs.
|
||||||
* Fine-grained randomization within memory regions
|
* Fine-grained randomization within memory regions
|
||||||
* Randomly sized guard regions for large allocations
|
* Randomly sized guard regions for large allocations
|
||||||
* Random slot selection within slabs
|
* Random slot selection within slabs
|
||||||
* [in-progress] Randomized delayed free for slab allocations
|
* Randomized delayed free for slab allocations
|
||||||
* [in-progress] Randomized allocation of slabs
|
* [in-progress] Randomized allocation of slabs
|
||||||
* [more randomization coming as the implementation is matured]
|
* [more randomization coming as the implementation is matured]
|
||||||
* Slab allocations are zeroed on free
|
* Slab allocations are zeroed on free
|
||||||
|
@ -204,8 +206,7 @@ was a bit less important and if a core goal was finding latent bugs.
|
||||||
* Detection of write-after-free by verifying zero filling is intact
|
* Detection of write-after-free by verifying zero filling is intact
|
||||||
* Memory in fresh allocations is consistently zeroed due to it either being
|
* Memory in fresh allocations is consistently zeroed due to it either being
|
||||||
fresh pages or zeroed on free after previous usage
|
fresh pages or zeroed on free after previous usage
|
||||||
* [in-progress] Delayed free via a combination of FIFO and randomization for
|
* Delayed free via a combination of FIFO and randomization for slab allocations
|
||||||
slab allocations
|
|
||||||
* Random canaries placed after each slab allocation to *absorb*
|
* Random canaries placed after each slab allocation to *absorb*
|
||||||
and then later detect overflows/underflows
|
and then later detect overflows/underflows
|
||||||
* High entropy per-slab random values
|
* High entropy per-slab random values
|
||||||
|
|
2
config.h
2
config.h
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define SLAB_QUARANTINE_RANDOM_SIZE 0
|
||||||
|
#define SLAB_QUARANTINE_QUEUE_SIZE 0
|
||||||
#define GUARD_SLABS_INTERVAL 1
|
#define GUARD_SLABS_INTERVAL 1
|
||||||
#define GUARD_SIZE_DIVISOR 2
|
#define GUARD_SIZE_DIVISOR 2
|
||||||
#define REGION_QUARANTINE_RANDOM_SIZE 128
|
#define REGION_QUARANTINE_RANDOM_SIZE 128
|
||||||
|
|
71
malloc.c
71
malloc.c
|
@ -28,6 +28,8 @@ extern int __register_atfork(void (*)(void), void (*)(void), void (*)(void), voi
|
||||||
#define atfork pthread_atfork
|
#define atfork pthread_atfork
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SLAB_QUARANTINE (SLAB_QUARANTINE_RANDOM_SIZE > 0 || SLAB_QUARANTINE_QUEUE_SIZE > 0)
|
||||||
|
|
||||||
static_assert(sizeof(void *) == 8, "64-bit only");
|
static_assert(sizeof(void *) == 8, "64-bit only");
|
||||||
|
|
||||||
static_assert(!WRITE_AFTER_FREE_CHECK || ZERO_ON_FREE, "WRITE_AFTER_FREE_CHECK depends on ZERO_ON_FREE");
|
static_assert(!WRITE_AFTER_FREE_CHECK || ZERO_ON_FREE, "WRITE_AFTER_FREE_CHECK depends on ZERO_ON_FREE");
|
||||||
|
@ -65,6 +67,9 @@ struct slab_metadata {
|
||||||
#ifdef SLAB_METADATA_COUNT
|
#ifdef SLAB_METADATA_COUNT
|
||||||
u16 count;
|
u16 count;
|
||||||
#endif
|
#endif
|
||||||
|
#if SLAB_QUARANTINE
|
||||||
|
u64 quarantine[4];
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const size_t min_align = 16;
|
static const size_t min_align = 16;
|
||||||
|
@ -172,6 +177,15 @@ struct __attribute__((aligned(CACHELINE_SIZE))) size_class {
|
||||||
struct libdivide_u32_t size_divisor;
|
struct libdivide_u32_t size_divisor;
|
||||||
struct libdivide_u64_t slab_size_divisor;
|
struct libdivide_u64_t slab_size_divisor;
|
||||||
|
|
||||||
|
#if SLAB_QUARANTINE_RANDOM_SIZE > 0
|
||||||
|
void *quarantine_random[SLAB_QUARANTINE_RANDOM_SIZE];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SLAB_QUARANTINE_QUEUE_SIZE > 0
|
||||||
|
void *quarantine_queue[SLAB_QUARANTINE_QUEUE_SIZE];
|
||||||
|
size_t quarantine_queue_index;
|
||||||
|
#endif
|
||||||
|
|
||||||
// slabs with at least one allocated slot and at least one free slot
|
// slabs with at least one allocated slot and at least one free slot
|
||||||
//
|
//
|
||||||
// LIFO doubly-linked list
|
// LIFO doubly-linked list
|
||||||
|
@ -264,6 +278,23 @@ static bool get_slot(struct slab_metadata *metadata, size_t index) {
|
||||||
return (metadata->bitmap[bucket] >> (index - bucket * 64)) & 1UL;
|
return (metadata->bitmap[bucket] >> (index - bucket * 64)) & 1UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SLAB_QUARANTINE
|
||||||
|
static void set_quarantine(struct slab_metadata *metadata, size_t index) {
|
||||||
|
size_t bucket = index / 64;
|
||||||
|
metadata->quarantine[bucket] |= 1UL << (index - bucket * 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_quarantine(struct slab_metadata *metadata, size_t index) {
|
||||||
|
size_t bucket = index / 64;
|
||||||
|
metadata->quarantine[bucket] &= ~(1UL << (index - bucket * 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_quarantine(struct slab_metadata *metadata, size_t index) {
|
||||||
|
size_t bucket = index / 64;
|
||||||
|
return (metadata->quarantine[bucket] >> (index - bucket * 64)) & 1UL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static u64 get_mask(size_t slots) {
|
static u64 get_mask(size_t slots) {
|
||||||
return slots < 64 ? ~0UL << slots : 0;
|
return slots < 64 ? ~0UL << slots : 0;
|
||||||
}
|
}
|
||||||
|
@ -548,6 +579,46 @@ static inline void deallocate_small(void *p, const size_t *expected_size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SLAB_QUARANTINE
|
||||||
|
if (get_quarantine(metadata, slot)) {
|
||||||
|
fatal_error("double free (quarantine)");
|
||||||
|
}
|
||||||
|
|
||||||
|
set_quarantine(metadata, slot);
|
||||||
|
|
||||||
|
#if SLAB_QUARANTINE_RANDOM_SIZE > 0
|
||||||
|
size_t random_index = get_random_u16_uniform(&c->rng, 16);
|
||||||
|
void *substitute = c->quarantine_random[random_index];
|
||||||
|
c->quarantine_random[random_index] = p;
|
||||||
|
|
||||||
|
if (substitute == NULL) {
|
||||||
|
mutex_unlock(&c->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = substitute;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SLAB_QUARANTINE_QUEUE_SIZE > 0
|
||||||
|
void *substitute = c->quarantine_queue[c->quarantine_queue_index];
|
||||||
|
c->quarantine_queue[c->quarantine_queue_index] = p;
|
||||||
|
c->quarantine_queue_index = (c->quarantine_queue_index + 1) % SLAB_QUARANTINE_QUEUE_SIZE;
|
||||||
|
|
||||||
|
if (substitute == NULL) {
|
||||||
|
mutex_unlock(&c->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = substitute;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
metadata = get_metadata(c, p);
|
||||||
|
slab = get_slab(c, slab_size, metadata);
|
||||||
|
slot = libdivide_u32_do((char *)p - (char *)slab, &c->size_divisor);
|
||||||
|
|
||||||
|
clear_quarantine(metadata, slot);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!has_free_slots(slots, metadata)) {
|
if (!has_free_slots(slots, metadata)) {
|
||||||
metadata->next = c->partial_slabs;
|
metadata->next = c->partial_slabs;
|
||||||
metadata->prev = NULL;
|
metadata->prev = NULL;
|
||||||
|
|
Loading…
Reference in New Issue