diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index fc7bc1f4d9d4..dd2aa58fde94 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -1276,35 +1276,38 @@ static zend_always_inline int zend_mm_small_size_to_bin(size_t size) * before dereference by comparing them with a shadow. * * The shadow is a copy of the pointer, stored at the end of the slot. It is - * XOR'ed with a random key, and converted to big-endian so that smaller - * corruptions affect the most significant bytes, which has a high chance of - * resulting in an invalid address instead of pointing to an adjacent slot. + * XOR'ed with a random key and with its own address, and converted to + * big-endian so that smaller corruptions affect the most significant bytes, + * which has a high chance of resulting in an invalid address instead of + * pointing to an adjacent slot. Mixing in the holder address keeps the key from + * being stored verbatim when the encoded pointer is NULL, and prevents a valid + * shadow from being naïvely replayed into another slot. */ -#define ZEND_MM_FREE_SLOT_PTR_SHADOW(free_slot, bin_num) \ - *((zend_mm_free_slot**)((char*)(free_slot) + bin_data_size[(bin_num)] - sizeof(zend_mm_free_slot*))) +#define ZEND_MM_FREE_SLOT_PTR_SHADOW_ADDR(free_slot, bin_num) \ + ((zend_mm_free_slot**)((char*)(free_slot) + bin_data_size[(bin_num)] - sizeof(zend_mm_free_slot*))) -static zend_always_inline zend_mm_free_slot* zend_mm_encode_free_slot(const zend_mm_heap *heap, const zend_mm_free_slot *slot) +static zend_always_inline zend_mm_free_slot* zend_mm_encode_free_slot(const zend_mm_heap *heap, const void *holder, const zend_mm_free_slot *next) { #ifdef WORDS_BIGENDIAN - return (zend_mm_free_slot*)(((uintptr_t)slot) ^ heap->shadow_key); + return (zend_mm_free_slot*)((uintptr_t)next ^ heap->shadow_key ^ (uintptr_t)holder); #else - return (zend_mm_free_slot*)(BSWAPPTR((uintptr_t)slot) ^ heap->shadow_key); + return (zend_mm_free_slot*)(BSWAPPTR((uintptr_t)next) ^ heap->shadow_key ^ (uintptr_t)holder); #endif } -static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot_key(uintptr_t shadow_key, zend_mm_free_slot *slot) +static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot_key(uintptr_t shadow_key, const void *holder, zend_mm_free_slot *shadow) { #ifdef WORDS_BIGENDIAN - return (zend_mm_free_slot*)((uintptr_t)slot ^ shadow_key); + return (zend_mm_free_slot*)((uintptr_t)shadow ^ shadow_key ^ (uintptr_t)holder); #else - return (zend_mm_free_slot*)(BSWAPPTR((uintptr_t)slot ^ shadow_key)); + return (zend_mm_free_slot*)(BSWAPPTR((uintptr_t)shadow ^ shadow_key ^ (uintptr_t)holder)); #endif } -static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot(zend_mm_heap *heap, zend_mm_free_slot *slot) +static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot(zend_mm_heap *heap, const void *holder, zend_mm_free_slot *shadow) { - return zend_mm_decode_free_slot_key(heap->shadow_key, slot); + return zend_mm_decode_free_slot_key(heap->shadow_key, holder, shadow); } static zend_always_inline void zend_mm_set_next_free_slot(zend_mm_heap *heap, uint32_t bin_num, zend_mm_free_slot *slot, zend_mm_free_slot *next) @@ -1312,15 +1315,17 @@ static zend_always_inline void zend_mm_set_next_free_slot(zend_mm_heap *heap, ui ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE); slot->next_free_slot = next; - ZEND_MM_FREE_SLOT_PTR_SHADOW(slot, bin_num) = zend_mm_encode_free_slot(heap, next); + + zend_mm_free_slot **shadow_addr = ZEND_MM_FREE_SLOT_PTR_SHADOW_ADDR(slot, bin_num); + *shadow_addr = zend_mm_encode_free_slot(heap, shadow_addr, next); } static zend_always_inline zend_mm_free_slot *zend_mm_get_next_free_slot(zend_mm_heap *heap, uint32_t bin_num, zend_mm_free_slot* slot) { zend_mm_free_slot *next = slot->next_free_slot; if (EXPECTED(next != NULL)) { - zend_mm_free_slot *shadow = ZEND_MM_FREE_SLOT_PTR_SHADOW(slot, bin_num); - if (UNEXPECTED(next != zend_mm_decode_free_slot(heap, shadow))) { + zend_mm_free_slot **shadow_addr = ZEND_MM_FREE_SLOT_PTR_SHADOW_ADDR(slot, bin_num); + if (UNEXPECTED(next != zend_mm_decode_free_slot(heap, shadow_addr, *shadow_addr))) { zend_mm_panic("zend_mm_heap corrupted"); } } @@ -2028,8 +2033,8 @@ ZEND_API void zend_mm_refresh_key_child(zend_mm_heap *heap) } zend_mm_free_slot *next; while ((next = slot->next_free_slot)) { - zend_mm_free_slot *shadow = ZEND_MM_FREE_SLOT_PTR_SHADOW(slot, i); - if (UNEXPECTED(next != zend_mm_decode_free_slot_key(old_key, shadow))) { + zend_mm_free_slot **shadow_addr = ZEND_MM_FREE_SLOT_PTR_SHADOW_ADDR(slot, i); + if (UNEXPECTED(next != zend_mm_decode_free_slot_key(old_key, shadow_addr, *shadow_addr))) { zend_mm_panic("zend_mm_heap corrupted"); } zend_mm_set_next_free_slot(heap, i, slot, next);