From f935c09f5d89581a50e678355661dd5075b7f7d5 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sat, 1 Mar 2025 14:13:29 +0100 Subject: [PATCH 1/4] Add heap runtime-enabled heap debugging capabilities --- Zend/zend_alloc.c | 285 ++++++++++++++++++++++++++++++++++- ext/zend_test/test.c | 14 ++ ext/zend_test/test.stub.php | 2 + ext/zend_test/test_arginfo.h | 6 +- 4 files changed, 304 insertions(+), 3 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 12e322d0347b3..e9cdf1764721d 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -305,7 +305,17 @@ struct _zend_mm_heap { size_t (*_gc)(void); void (*_shutdown)(bool full, bool silent); } custom_heap; - HashTable *tracked_allocs; + union { + HashTable *tracked_allocs; + struct { + bool poison_alloc; + uint8_t poison_alloc_value; + bool poison_free; + uint8_t poison_free_value; + uint8_t padding; + bool check_freelists_on_shutdown; + } debug; + }; #endif pid_t pid; zend_random_bytes_insecure_state rand_state; @@ -2389,8 +2399,19 @@ static void zend_mm_check_leaks(zend_mm_heap *heap) #if ZEND_MM_CUSTOM static void *tracked_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); static void tracked_free_all(zend_mm_heap *heap); +static void *poison_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); #endif +static void zend_mm_check_freelists(zend_mm_heap *heap) +{ + for (int bin_num = 0; bin_num < ZEND_MM_BINS; bin_num++) { + zend_mm_free_slot *slot = heap->free_slot[bin_num]; + while (slot) { + slot = zend_mm_get_next_free_slot(heap, bin_num, slot); + } + } +} + ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) { zend_mm_chunk *p; @@ -2555,8 +2576,9 @@ ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *ptr if (size_zv) { return Z_LVAL_P(size_zv); } + } else if (heap->custom_heap._malloc != poison_malloc) { + return 0; } - return 0; } #endif return zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); @@ -3021,6 +3043,257 @@ static void tracked_free_all(zend_mm_heap *heap) { } #endif +static void* poison_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + zend_mm_heap *heap = AG(mm_heap); + + if (SIZE_MAX - heap->debug.padding * 2 < size) { + zend_mm_panic("Integer overflow in memory allocation"); + } + size += heap->debug.padding * 2; + + void *ptr = zend_mm_alloc_heap(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + + if (EXPECTED(ptr)) { + if (heap->debug.poison_alloc) { + memset(ptr, heap->debug.poison_alloc_value, size); + } + + ptr = (char*)ptr + heap->debug.padding; + } + + return ptr; +} + +static void poison_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + zend_mm_heap *heap = AG(mm_heap); + + if (EXPECTED(ptr)) { + /* zend_mm_shutdown() will try to free the heap when custom handlers + * are installed */ + if (UNEXPECTED(ptr == heap)) { + return; + } + + ptr = (char*)ptr - heap->debug.padding; + + size_t size = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + + if (heap->debug.poison_free) { + memset(ptr, heap->debug.poison_free_value, size); + } + } + + zend_mm_free_heap(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); +} + +static void* poison_realloc(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + zend_mm_heap *heap = AG(mm_heap); + + if (SIZE_MAX - heap->debug.padding * 2 < size) { + zend_mm_panic("Integer overflow in memory allocation"); + } + size += heap->debug.padding * 2; + + size_t oldsize; + if (ptr) { + ptr = (char*)ptr - heap->debug.padding; + oldsize = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + + if (oldsize > size) { + if (heap->debug.poison_free) { + memset((char*)ptr + size, heap->debug.poison_free_value, oldsize - size); + } + } + } else { + oldsize = 0; + } + + void *old_ptr = ptr; + ptr = zend_mm_realloc_heap(heap, ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + + if (EXPECTED(ptr)) { + size = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + if (old_ptr != ptr) { + if (old_ptr) { + /* old ptr was freed */ + if (heap->debug.poison_free && oldsize <= ZEND_MM_MAX_SMALL_SIZE) { +#if ZEND_DEBUG + size_t len = oldsize - sizeof(zend_mm_free_slot*) - sizeof(zend_mm_debug_info); +#else + size_t len = oldsize - sizeof(zend_mm_free_slot*) * 2; +#endif + memset((char*)old_ptr + sizeof(zend_mm_free_slot*), heap->debug.poison_free_value, len); + } + } + /* ptr is a new allocation */ + if (heap->debug.poison_alloc) { + if (oldsize == 0) { +#if ZEND_DEBUG + memset(ptr, heap->debug.poison_alloc_value, size - sizeof(zend_mm_debug_info)); +#else + memset(ptr, heap->debug.poison_alloc_value, size); +#endif + /* old content was copied to ptr, don't override it */ + } else if (size > oldsize) { +#if ZEND_DEBUG + memset((char*)ptr + oldsize - sizeof(zend_mm_debug_info), heap->debug.poison_alloc_value, size - oldsize); +#else + memset((char*)ptr + oldsize, heap->debug.poison_alloc_value, size - oldsize); +#endif + } + } + } else if (size > oldsize) { + if (heap->debug.poison_alloc) { +#if ZEND_DEBUG + memset((char*)ptr + oldsize - sizeof(zend_mm_debug_info), heap->debug.poison_alloc_value, size - oldsize); +#else + memset((char*)ptr + oldsize, heap->debug.poison_alloc_value, size - oldsize); +#endif + } + } else if (size < oldsize) { + if (heap->debug.poison_free) { +#if ZEND_DEBUG + memset((char*)ptr + size - sizeof(zend_mm_debug_info), heap->debug.poison_free_value, oldsize - size); +#else + memset((char*)ptr + size, heap->debug.poison_free_value, oldsize - size); +#endif + } + } + + ptr = (char*)ptr + heap->debug.padding; + } + + return ptr; +} + +static size_t poison_gc(void) +{ + zend_mm_heap *heap = AG(mm_heap); + + void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + size_t (*_gc)(void); + void (*_shutdown)(bool, bool); + + zend_mm_get_custom_handlers_ex(heap, &_malloc, &_free, &_realloc, &_gc, &_shutdown); + zend_mm_set_custom_handlers_ex(heap, NULL, NULL, NULL, NULL, NULL); + + size_t collected = zend_mm_gc(heap); + + zend_mm_set_custom_handlers_ex(heap, _malloc, _free, _realloc, _gc, _shutdown); + + return collected; +} + +static void poison_shutdown(bool full, bool silent) +{ + zend_mm_heap *heap = AG(mm_heap); + + void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + size_t (*_gc)(void); + void (*_shutdown)(bool, bool); + + zend_mm_get_custom_handlers_ex(heap, &_malloc, &_free, &_realloc, &_gc, &_shutdown); + zend_mm_set_custom_handlers_ex(heap, NULL, NULL, NULL, NULL, NULL); + + if (heap->debug.check_freelists_on_shutdown) { + zend_mm_check_freelists(heap); + } + + zend_mm_shutdown(heap, full, silent); + + if (!full) { + zend_mm_set_custom_handlers_ex(heap, _malloc, _free, _realloc, _gc, _shutdown); + } +} + +static void poison_enable(zend_mm_heap *heap, char *parameters) +{ + char *tmp = parameters; + char *end = tmp + strlen(tmp); + + /* Trim heading/trailing whitespaces */ + while (*tmp == ' ' || *tmp == '\t' || *tmp == '\n') { + tmp++; + } + while (end != tmp && (*(end-1) == ' ' || *(end-1) == '\t' || *(end-1) == '\n')) { + end--; + } + + if (tmp == end) { + return; + } + + while (1) { + char *key = tmp; + + tmp = memchr(tmp, '=', end - tmp); + if (!tmp) { + size_t key_len = end - key; + fprintf(stderr, "Unexpected EOF after ZEND_MM_DEBUG parameter '%.*s', expected '='\n", + (int)key_len, key); + return; + } + + size_t key_len = tmp - key; + char *value = tmp + 1; + + if (key_len == strlen("poison_alloc") + && !memcmp(key, "poison_alloc", key_len)) { + + heap->debug.poison_alloc = true; + heap->debug.poison_alloc_value = (uint8_t) ZEND_STRTOUL(value, &tmp, 0); + + } else if (key_len == strlen("poison_free") + && !memcmp(key, "poison_free", key_len)) { + + heap->debug.poison_free = true; + heap->debug.poison_free_value = (uint8_t) ZEND_STRTOUL(value, &tmp, 0); + + } else if (key_len == strlen("padding") + && !memcmp(key, "padding", key_len)) { + + uint8_t padding = ZEND_STRTOUL(value, &tmp, 0); + if (ZEND_MM_ALIGNED_SIZE(padding) != padding) { + fprintf(stderr, "ZEND_MM_DEBUG padding must be a multiple of %u, %u given\n", + (unsigned int)ZEND_MM_ALIGNMENT, + (unsigned int)padding); + return; + } + heap->debug.padding = padding; + + } else if (key_len == strlen("check_freelists_on_shutdown") + && !memcmp(key, "check_freelists_on_shutdown", key_len)) { + + heap->debug.check_freelists_on_shutdown = (bool) ZEND_STRTOUL(value, &tmp, 0); + + } else { + fprintf(stderr, "Unknown ZEND_MM_DEBUG parameter: '%.*s'\n", + (int)key_len, key); + return; + } + + if (tmp == end) { + break; + } + if (*tmp != ',') { + fprintf(stderr, "Unexpected '%c' after value of ZEND_MM_DEBUG parameter '%.*s', expected ','\n", + *tmp, (int)key_len, key); + return; + } + tmp++; + } + + zend_mm_set_custom_handlers_ex(heap, poison_malloc, poison_free, + poison_realloc, poison_gc, poison_shutdown); +} + static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) { char *tmp; @@ -3057,6 +3330,14 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) zend_mm_use_huge_pages = true; } alloc_globals->mm_heap = zend_mm_init(); + +#if ZEND_MM_CUSTOM + ZEND_ASSERT(!alloc_globals->mm_heap->tracked_allocs); + tmp = getenv("ZEND_MM_DEBUG"); + if (tmp) { + poison_enable(alloc_globals->mm_heap, tmp); + } +#endif } #ifdef ZTS diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index e3f87ee1e1636..565895dac72e1 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -15,6 +15,7 @@ */ #include "zend_modules.h" +#include "zend_types.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -182,6 +183,19 @@ static ZEND_FUNCTION(zend_leak_variable) Z_ADDREF_P(zv); } +static ZEND_FUNCTION(zend_delref) +{ + zval *zv; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zv) == FAILURE) { + RETURN_THROWS(); + } + + Z_TRY_DELREF_P(zv); + + RETURN_NULL(); +} + /* Tests Z_PARAM_OBJ_OR_STR */ static ZEND_FUNCTION(zend_string_or_object) { diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 59cb9661e4e43..e0e1f32833ef5 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -235,6 +235,8 @@ function zend_leak_variable(mixed $variable): void {} function zend_leak_bytes(int $bytes = 3): void {} + function zend_delref(mixed $variable): void {} + function zend_string_or_object(object|string $param): object|string {} function zend_string_or_object_or_null(object|string|null $param): object|string|null {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index c558b58f65169..f13df2ae4bea2 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3082e62e96d5f4383c98638513463c676a7c3a69 */ + * Stub hash: 80b2dbc373baccd5ee4df047070d95e4c44effcd */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -42,6 +42,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_leak_bytes, 0, 0, IS_VOID, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bytes, IS_LONG, 0, "3") ZEND_END_ARG_INFO() +#define arginfo_zend_delref arginfo_zend_leak_variable + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_zend_string_or_object, 0, 1, MAY_BE_OBJECT|MAY_BE_STRING) ZEND_ARG_TYPE_MASK(0, param, MAY_BE_OBJECT|MAY_BE_STRING, NULL) ZEND_END_ARG_INFO() @@ -264,6 +266,7 @@ static ZEND_FUNCTION(zend_create_unterminated_string); static ZEND_FUNCTION(zend_terminate_string); static ZEND_FUNCTION(zend_leak_variable); static ZEND_FUNCTION(zend_leak_bytes); +static ZEND_FUNCTION(zend_delref); static ZEND_FUNCTION(zend_string_or_object); static ZEND_FUNCTION(zend_string_or_object_or_null); static ZEND_FUNCTION(zend_string_or_stdclass); @@ -369,6 +372,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_terminate_string, arginfo_zend_terminate_string) ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable) ZEND_FE(zend_leak_bytes, arginfo_zend_leak_bytes) + ZEND_FE(zend_delref, arginfo_zend_delref) ZEND_FE(zend_string_or_object, arginfo_zend_string_or_object) ZEND_FE(zend_string_or_object_or_null, arginfo_zend_string_or_object_or_null) ZEND_FE(zend_string_or_stdclass, arginfo_zend_string_or_stdclass) From 52b97d818f10c8cd84802154cc0e9372d769fb56 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 28 Mar 2025 19:31:21 +0100 Subject: [PATCH 2/4] realloc_always_move --- Zend/zend_alloc.c | 29 ++++++++++++++++++++- ext/zend_test/tests/opline_dangling.phpt | 6 +++++ ext/zend_test/tests/opline_dangling_02.phpt | 6 +++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index e9cdf1764721d..1d6c734d721fe 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -314,6 +314,7 @@ struct _zend_mm_heap { uint8_t poison_free_value; uint8_t padding; bool check_freelists_on_shutdown; + bool realloc_always_move; } debug; }; #endif @@ -2404,7 +2405,7 @@ static void *poison_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) static void zend_mm_check_freelists(zend_mm_heap *heap) { - for (int bin_num = 0; bin_num < ZEND_MM_BINS; bin_num++) { + for (uint32_t bin_num = 0; bin_num < ZEND_MM_BINS; bin_num++) { zend_mm_free_slot *slot = heap->free_slot[bin_num]; while (slot) { slot = zend_mm_get_next_free_slot(heap, bin_num, slot); @@ -3092,6 +3093,27 @@ static void* poison_realloc(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_L { zend_mm_heap *heap = AG(mm_heap); + if (heap->debug.realloc_always_move) { + void *new = poison_malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + new = (char*)new - heap->debug.padding; + + if (ptr) { + ptr = (char*)ptr - heap->debug.padding; + size_t oldsize = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + size_t size = zend_mm_size(heap, new ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + +#if ZEND_DEBUG + oldsize -= sizeof(zend_mm_debug_info); + size -= sizeof(zend_mm_debug_info); +#endif + + memcpy(new, ptr, MIN(oldsize, size)); + poison_free((char*)ptr + heap->debug.padding ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + + return (char*)new + heap->debug.padding; + } + if (SIZE_MAX - heap->debug.padding * 2 < size) { zend_mm_panic("Integer overflow in memory allocation"); } @@ -3273,6 +3295,11 @@ static void poison_enable(zend_mm_heap *heap, char *parameters) heap->debug.check_freelists_on_shutdown = (bool) ZEND_STRTOUL(value, &tmp, 0); + } else if (key_len == strlen("realloc_always_move") + && !memcmp(key, "realloc_always_move", key_len)) { + + heap->debug.realloc_always_move = (bool) ZEND_STRTOUL(value, &tmp, 0); + } else { fprintf(stderr, "Unknown ZEND_MM_DEBUG parameter: '%.*s'\n", (int)key_len, key); diff --git a/ext/zend_test/tests/opline_dangling.phpt b/ext/zend_test/tests/opline_dangling.phpt index 3b27b544ca34f..e8765c3048bb2 100644 --- a/ext/zend_test/tests/opline_dangling.phpt +++ b/ext/zend_test/tests/opline_dangling.phpt @@ -6,6 +6,12 @@ https://github.com/php/php-src/pull/12758 zend_test --ENV-- USE_ZEND_ALLOC=1 +--SKIPIF-- + --INI-- zend_test.observe_opline_in_zendmm=1 --FILE-- diff --git a/ext/zend_test/tests/opline_dangling_02.phpt b/ext/zend_test/tests/opline_dangling_02.phpt index 585a9c8395b59..8e0ace62db567 100644 --- a/ext/zend_test/tests/opline_dangling_02.phpt +++ b/ext/zend_test/tests/opline_dangling_02.phpt @@ -4,6 +4,12 @@ possible segfault in `ZEND_FUNC_GET_ARGS` zend_test --ENV-- USE_ZEND_ALLOC=1 +--SKIPIF-- + --INI-- zend_test.observe_opline_in_zendmm=1 --FILE-- From 91fc0dd4208df202aab2610ce0dcca0423896535 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 28 Mar 2025 19:55:18 +0100 Subject: [PATCH 3/4] Remove realloc_always_move, make poison_realloc always-always-moving --- Zend/zend_alloc.c | 102 ++++------------------------------------------ 1 file changed, 9 insertions(+), 93 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 1d6c734d721fe..f371999d9f492 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -314,7 +314,6 @@ struct _zend_mm_heap { uint8_t poison_free_value; uint8_t padding; bool check_freelists_on_shutdown; - bool realloc_always_move; } debug; }; #endif @@ -3093,102 +3092,24 @@ static void* poison_realloc(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_L { zend_mm_heap *heap = AG(mm_heap); - if (heap->debug.realloc_always_move) { - void *new = poison_malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - new = (char*)new - heap->debug.padding; + void *new = poison_malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + new = (char*)new - heap->debug.padding; - if (ptr) { - ptr = (char*)ptr - heap->debug.padding; - size_t oldsize = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - size_t size = zend_mm_size(heap, new ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - -#if ZEND_DEBUG - oldsize -= sizeof(zend_mm_debug_info); - size -= sizeof(zend_mm_debug_info); -#endif - - memcpy(new, ptr, MIN(oldsize, size)); - poison_free((char*)ptr + heap->debug.padding ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - } - - return (char*)new + heap->debug.padding; - } - - if (SIZE_MAX - heap->debug.padding * 2 < size) { - zend_mm_panic("Integer overflow in memory allocation"); - } - size += heap->debug.padding * 2; - - size_t oldsize; if (ptr) { ptr = (char*)ptr - heap->debug.padding; - oldsize = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + size_t oldsize = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + size_t size = zend_mm_size(heap, new ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - if (oldsize > size) { - if (heap->debug.poison_free) { - memset((char*)ptr + size, heap->debug.poison_free_value, oldsize - size); - } - } - } else { - oldsize = 0; - } - - void *old_ptr = ptr; - ptr = zend_mm_realloc_heap(heap, ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - - if (EXPECTED(ptr)) { - size = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - if (old_ptr != ptr) { - if (old_ptr) { - /* old ptr was freed */ - if (heap->debug.poison_free && oldsize <= ZEND_MM_MAX_SMALL_SIZE) { -#if ZEND_DEBUG - size_t len = oldsize - sizeof(zend_mm_free_slot*) - sizeof(zend_mm_debug_info); -#else - size_t len = oldsize - sizeof(zend_mm_free_slot*) * 2; -#endif - memset((char*)old_ptr + sizeof(zend_mm_free_slot*), heap->debug.poison_free_value, len); - } - } - /* ptr is a new allocation */ - if (heap->debug.poison_alloc) { - if (oldsize == 0) { -#if ZEND_DEBUG - memset(ptr, heap->debug.poison_alloc_value, size - sizeof(zend_mm_debug_info)); -#else - memset(ptr, heap->debug.poison_alloc_value, size); -#endif - /* old content was copied to ptr, don't override it */ - } else if (size > oldsize) { -#if ZEND_DEBUG - memset((char*)ptr + oldsize - sizeof(zend_mm_debug_info), heap->debug.poison_alloc_value, size - oldsize); -#else - memset((char*)ptr + oldsize, heap->debug.poison_alloc_value, size - oldsize); -#endif - } - } - } else if (size > oldsize) { - if (heap->debug.poison_alloc) { #if ZEND_DEBUG - memset((char*)ptr + oldsize - sizeof(zend_mm_debug_info), heap->debug.poison_alloc_value, size - oldsize); -#else - memset((char*)ptr + oldsize, heap->debug.poison_alloc_value, size - oldsize); -#endif - } - } else if (size < oldsize) { - if (heap->debug.poison_free) { -#if ZEND_DEBUG - memset((char*)ptr + size - sizeof(zend_mm_debug_info), heap->debug.poison_free_value, oldsize - size); -#else - memset((char*)ptr + size, heap->debug.poison_free_value, oldsize - size); + oldsize -= sizeof(zend_mm_debug_info); + size -= sizeof(zend_mm_debug_info); #endif - } - } - ptr = (char*)ptr + heap->debug.padding; + memcpy(new, ptr, MIN(oldsize, size)); + poison_free((char*)ptr + heap->debug.padding ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } - return ptr; + return (char*)new + heap->debug.padding; } static size_t poison_gc(void) @@ -3295,11 +3216,6 @@ static void poison_enable(zend_mm_heap *heap, char *parameters) heap->debug.check_freelists_on_shutdown = (bool) ZEND_STRTOUL(value, &tmp, 0); - } else if (key_len == strlen("realloc_always_move") - && !memcmp(key, "realloc_always_move", key_len)) { - - heap->debug.realloc_always_move = (bool) ZEND_STRTOUL(value, &tmp, 0); - } else { fprintf(stderr, "Unknown ZEND_MM_DEBUG parameter: '%.*s'\n", (int)key_len, key); From f84ae06c9244147b709d300ff131de392c69b8b1 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc <365207+arnaud-lb@users.noreply.github.com> Date: Mon, 31 Mar 2025 14:05:19 +0200 Subject: [PATCH 4/4] Do not copy padding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tim Düsterhus --- Zend/zend_alloc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index f371999d9f492..b9f3bc015217c 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -3093,23 +3093,23 @@ static void* poison_realloc(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_L zend_mm_heap *heap = AG(mm_heap); void *new = poison_malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - new = (char*)new - heap->debug.padding; if (ptr) { - ptr = (char*)ptr - heap->debug.padding; - size_t oldsize = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - size_t size = zend_mm_size(heap, new ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + /* Determine the size of the old allocation from the unpadded pointer. */ + size_t oldsize = zend_mm_size(heap, (char*)ptr - heap->debug.padding ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + + /* Remove the padding size to determine the size that is available to the user. */ + oldsize -= (2 * heap->debug.padding); #if ZEND_DEBUG oldsize -= sizeof(zend_mm_debug_info); - size -= sizeof(zend_mm_debug_info); #endif memcpy(new, ptr, MIN(oldsize, size)); - poison_free((char*)ptr + heap->debug.padding ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + poison_free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } - return (char*)new + heap->debug.padding; + return new; } static size_t poison_gc(void)