mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-05 23:19:31 +03:00
update free list encoding to stronger formula with addition last
This commit is contained in:
parent
e3391d9a53
commit
77134e1ad0
2 changed files with 18 additions and 13 deletions
|
@ -397,24 +397,26 @@ Encoding/Decoding the free list next pointers
|
||||||
|
|
||||||
This is to protect against buffer overflow exploits where the
|
This is to protect against buffer overflow exploits where the
|
||||||
free list is mutated. Many hardened allocators xor the next pointer `p`
|
free list is mutated. Many hardened allocators xor the next pointer `p`
|
||||||
with a secret key `k1`, as `p^k1`, but if the attacker can guess
|
with a secret key `k1`, as `p^k1`. This prevents overwriting with known
|
||||||
|
values but might be still too weak: if the attacker can guess
|
||||||
the pointer `p` this can reveal `k1` (since `p^k1^p == k1`).
|
the pointer `p` this can reveal `k1` (since `p^k1^p == k1`).
|
||||||
Moreover, if multiple blocks can be read, the attacker can
|
Moreover, if multiple blocks can be read as well, the attacker can
|
||||||
xor both as `(p1^k1) ^ (p2^k1) == p1^p2` which may reveal a lot
|
xor both as `(p1^k1) ^ (p2^k1) == p1^p2` which may reveal a lot
|
||||||
about the pointers (and subsequently `k1`).
|
about the pointers (and subsequently `k1`).
|
||||||
|
|
||||||
Instead mimalloc uses an extra key `k2` and encode as `rotl(p+k2,13)^k1`.
|
Instead mimalloc uses an extra key `k2` and encodes as `((p^k2)<<<k1)+k1`.
|
||||||
Since these operations are not associative, the above approaches do not
|
Since these operations are not associative, the above approaches do not
|
||||||
work so well any more even if the `p` can be guesstimated. (We include
|
work so well any more even if the `p` can be guesstimated. For example,
|
||||||
the rotation since xor and addition are otherwise linear in the lowest bit)
|
for the read case we can subtract two entries to discard the `+k1` term,
|
||||||
Both keys are unique per page.
|
but that leads to `((p1^k2)<<<k1) - ((p2^k2)<<<k1)` at best.
|
||||||
|
We include the left-rotation since xor and addition are otherwise linear
|
||||||
|
in the lowest bit. Finally, both keys are unique per page which reduces
|
||||||
|
the re-use of keys by a large factor.
|
||||||
|
|
||||||
We also pass a separate `null` value to be used as `NULL` or otherwise
|
We also pass a separate `null` value to be used as `NULL` or otherwise
|
||||||
`rotl(k2,13)^k1` would appear (too) often as a sentinel value.
|
`(k2<<<k1)+k1` would appear (too) often as a sentinel value.
|
||||||
------------------------------------------------------------------- */
|
------------------------------------------------------------------- */
|
||||||
|
|
||||||
#define MI_ENCODE_ROTATE_BITS (13)
|
|
||||||
|
|
||||||
static inline bool mi_is_in_same_segment(const void* p, const void* q) {
|
static inline bool mi_is_in_same_segment(const void* p, const void* q) {
|
||||||
return (_mi_ptr_segment(p) == _mi_ptr_segment(q));
|
return (_mi_ptr_segment(p) == _mi_ptr_segment(q));
|
||||||
}
|
}
|
||||||
|
@ -429,14 +431,17 @@ static inline bool mi_is_in_same_page(const void* p, const void* q) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uintptr_t mi_rotl(uintptr_t x, uintptr_t shift) {
|
static inline uintptr_t mi_rotl(uintptr_t x, uintptr_t shift) {
|
||||||
|
shift %= MI_INTPTR_BITS;
|
||||||
return ((x << shift) | (x >> (MI_INTPTR_BITS - shift)));
|
return ((x << shift) | (x >> (MI_INTPTR_BITS - shift)));
|
||||||
}
|
}
|
||||||
static inline uintptr_t mi_rotr(uintptr_t x, uintptr_t shift) {
|
static inline uintptr_t mi_rotr(uintptr_t x, uintptr_t shift) {
|
||||||
|
shift %= MI_INTPTR_BITS;
|
||||||
return ((x >> shift) | (x << (MI_INTPTR_BITS - shift)));
|
return ((x >> shift) | (x << (MI_INTPTR_BITS - shift)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* block, uintptr_t key1, uintptr_t key2 ) {
|
static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* block, uintptr_t key1, uintptr_t key2 ) {
|
||||||
#ifdef MI_ENCODE_FREELIST
|
#ifdef MI_ENCODE_FREELIST
|
||||||
mi_block_t* b = (mi_block_t*)(mi_rotr(block->next ^ key1, MI_ENCODE_ROTATE_BITS) - key2);
|
mi_block_t* b = (mi_block_t*)(mi_rotr(block->next - key1, key1) ^ key2);
|
||||||
if (mi_unlikely((void*)b==null)) { b = NULL; }
|
if (mi_unlikely((void*)b==null)) { b = NULL; }
|
||||||
return b;
|
return b;
|
||||||
#else
|
#else
|
||||||
|
@ -448,7 +453,7 @@ static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* bl
|
||||||
static inline void mi_block_set_nextx(const void* null, mi_block_t* block, const mi_block_t* next, uintptr_t key1, uintptr_t key2) {
|
static inline void mi_block_set_nextx(const void* null, mi_block_t* block, const mi_block_t* next, uintptr_t key1, uintptr_t key2) {
|
||||||
#ifdef MI_ENCODE_FREELIST
|
#ifdef MI_ENCODE_FREELIST
|
||||||
if (mi_unlikely(next==NULL)) { next = (mi_block_t*)null; }
|
if (mi_unlikely(next==NULL)) { next = (mi_block_t*)null; }
|
||||||
block->next = mi_rotl((mi_encoded_t)next + key2, MI_ENCODE_ROTATE_BITS) ^ key1;
|
block->next = mi_rotl((uintptr_t)next ^ key2, key1) + key1;
|
||||||
#else
|
#else
|
||||||
UNUSED(key1); UNUSED(key2); UNUSED(null);
|
UNUSED(key1); UNUSED(key2); UNUSED(null);
|
||||||
block->next = (mi_encoded_t)next;
|
block->next = (mi_encoded_t)next;
|
||||||
|
@ -485,7 +490,7 @@ static inline void mi_block_set_next(const mi_page_t* page, mi_block_t* block, c
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
static inline uintptr_t _mi_random_shuffle(uintptr_t x) {
|
static inline uintptr_t _mi_random_shuffle(uintptr_t x) {
|
||||||
mi_assert_internal(x!=0);
|
if (x==0) { x = 17; } // ensure we don't get stuck in generating zeros
|
||||||
#if (MI_INTPTR_SIZE==8)
|
#if (MI_INTPTR_SIZE==8)
|
||||||
// by Sebastiano Vigna, see: <http://xoshiro.di.unimi.it/splitmix64.c>
|
// by Sebastiano Vigna, see: <http://xoshiro.di.unimi.it/splitmix64.c>
|
||||||
x ^= x >> 30;
|
x ^= x >> 30;
|
||||||
|
|
|
@ -479,7 +479,7 @@ static void mi_page_free_list_extend_secure(mi_heap_t* const heap, mi_page_t* co
|
||||||
counts[current]--;
|
counts[current]--;
|
||||||
mi_block_t* const free_start = blocks[current];
|
mi_block_t* const free_start = blocks[current];
|
||||||
// and iterate through the rest; use `random_shuffle` for performance
|
// and iterate through the rest; use `random_shuffle` for performance
|
||||||
uintptr_t rnd = _mi_random_shuffle(r);
|
uintptr_t rnd = _mi_random_shuffle(r|1); // ensure not 0
|
||||||
for (size_t i = 1; i < extend; i++) {
|
for (size_t i = 1; i < extend; i++) {
|
||||||
// call random_shuffle only every INTPTR_SIZE rounds
|
// call random_shuffle only every INTPTR_SIZE rounds
|
||||||
const size_t round = i%MI_INTPTR_SIZE;
|
const size_t round = i%MI_INTPTR_SIZE;
|
||||||
|
|
Loading…
Add table
Reference in a new issue