From f35344c2f7688ee9bf5d5fb131e633a8de638c4c Mon Sep 17 00:00:00 2001 From: jvoisin Date: Wed, 21 Oct 2026 10:07:20 +0200 Subject: [PATCH] Zend: Add ASan annotations to native ZendMM Poison freed small slots, page runs, and retained request-shutdown storage. Unpoison storage on allocation, reuse, and realloc growth. Temporarily expose free-list metadata during allocator accesses, including post-fork rekeying. Keep debug trailers accessible for leak detection. Add regression coverage for ASan shadow state across allocation, reallocation, freeing, GC, rekeying, and heap reuse. Document how to run ASan tests with native ZendMM. --- Zend/zend_alloc.c | 73 ++++++++++-- docs/source/miscellaneous/running-tests.rst | 22 ++++ ext/zend_test/test.c | 122 ++++++++++++++++++++ ext/zend_test/test.stub.php | 4 + ext/zend_test/test_arginfo.h | 15 ++- ext/zend_test/test_decl.h | 8 +- ext/zend_test/test_legacy_arginfo.h | 15 ++- ext/zend_test/tests/zend_mm_asan.phpt | 26 +++++ 8 files changed, 267 insertions(+), 18 deletions(-) create mode 100644 ext/zend_test/tests/zend_mm_asan.phpt diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index b293ee1a51dc..6e9ebc087356 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -87,6 +87,9 @@ typedef int pid_t; #include #ifdef __SANITIZE_ADDRESS__ # include +#else +# define ASAN_POISON_MEMORY_REGION(addr, size) ((void) 0) +# define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void) 0) #endif #ifndef _WIN32 @@ -890,9 +893,6 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment) if (zend_mm_use_huge_pages) { zend_mm_hugepage(ptr, size); } -#ifdef __SANITIZE_ADDRESS__ - ASAN_UNPOISON_MEMORY_REGION(ptr, size); -#endif return ptr; } else { size_t offset; @@ -932,9 +932,6 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment) if (zend_mm_use_huge_pages) { zend_mm_hugepage(ptr, size); } -# ifdef __SANITIZE_ADDRESS__ - ASAN_UNPOISON_MEMORY_REGION(ptr, size); -# endif #endif return ptr; } @@ -942,24 +939,33 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment) static void *zend_mm_chunk_alloc(zend_mm_heap *heap, size_t size, size_t alignment) { + void *ptr; + #if ZEND_MM_STORAGE if (UNEXPECTED(heap->storage)) { - void *ptr = heap->storage->handlers.chunk_alloc(heap->storage, size, alignment); + ptr = heap->storage->handlers.chunk_alloc(heap->storage, size, alignment); ZEND_ASSERT(((uintptr_t)((char*)ptr + (alignment-1)) & (alignment-1)) == (uintptr_t)ptr); - return ptr; - } + } else #endif - return zend_mm_chunk_alloc_int(size, alignment); + { + ptr = zend_mm_chunk_alloc_int(size, alignment); + } + if (ptr != NULL) { + ASAN_UNPOISON_MEMORY_REGION(ptr, size); + } + return ptr; } static void zend_mm_chunk_free(zend_mm_heap *heap, void *addr, size_t size) { #if ZEND_MM_STORAGE if (UNEXPECTED(heap->storage)) { + ASAN_UNPOISON_MEMORY_REGION(addr, size); heap->storage->handlers.chunk_free(heap->storage, addr, size); return; } #endif + ASAN_POISON_MEMORY_REGION(addr, size); zend_mm_munmap(addr, size); } @@ -975,6 +981,7 @@ static int zend_mm_chunk_truncate(zend_mm_heap *heap, void *addr, size_t old_siz } #endif #ifndef _WIN32 + ASAN_POISON_MEMORY_REGION((char*)addr + new_size, old_size - new_size); zend_mm_munmap((char*)addr + new_size, old_size - new_size); return 1; #else @@ -1300,6 +1307,7 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, uint32_t pages_count ZEND_F if (page_num == chunk->free_tail) { chunk->free_tail = page_num + pages_count; } + ASAN_UNPOISON_MEMORY_REGION(ZEND_MM_PAGE_ADDR(chunk, page_num), pages_count * ZEND_MM_PAGE_SIZE); return ZEND_MM_PAGE_ADDR(chunk, page_num); } @@ -1370,6 +1378,7 @@ static zend_always_inline void zend_mm_delete_chunk(zend_mm_heap *heap, zend_mm_ static zend_always_inline void zend_mm_free_pages_ex(zend_mm_heap *heap, zend_mm_chunk *chunk, uint32_t page_num, uint32_t pages_count, int free_chunk) { + ASAN_POISON_MEMORY_REGION(ZEND_MM_PAGE_ADDR(chunk, page_num), pages_count * ZEND_MM_PAGE_SIZE); chunk->free_pages += pages_count; zend_mm_bitset_reset_range(chunk->free_map, page_num, pages_count); chunk->map[page_num] = 0; @@ -1461,6 +1470,20 @@ static zend_always_inline uint32_t zend_mm_small_size_to_bin(size_t size) #define ZEND_MM_SMALL_SIZE_TO_BIN(size) zend_mm_small_size_to_bin(size) +#ifdef __SANITIZE_ADDRESS__ +static zend_always_inline void zend_mm_asan_poison_free_slot(void *ptr, uint32_t bin_num) +{ + size_t size = bin_data_size[bin_num]; +#if ZEND_DEBUG + /* Leak detection inspects the debug trailer of free slots. */ + size -= ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)); +#endif + ASAN_POISON_MEMORY_REGION(ptr, size); +} +#else +# define zend_mm_asan_poison_free_slot(ptr, bin_num) ((void) 0) +#endif + #if ZEND_MM_HEAP_PROTECTION /* We keep track of free slots by organizing them in a linked list, with the * first word of every free slot being a pointer to the next one. @@ -1507,14 +1530,18 @@ 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); + /* Free-list links live in poisoned storage, so expose them only while in use. */ + ASAN_UNPOISON_MEMORY_REGION(slot, bin_data_size[bin_num]); slot->next_free_slot = 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); + zend_mm_asan_poison_free_slot(slot, bin_num); } 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) { + ASAN_UNPOISON_MEMORY_REGION(slot, bin_data_size[bin_num]); zend_mm_free_slot *next = slot->next_free_slot; if (EXPECTED(next != NULL)) { zend_mm_free_slot **shadow_addr = ZEND_MM_FREE_SLOT_PTR_SHADOW_ADDR(slot, bin_num); @@ -1522,14 +1549,24 @@ static zend_always_inline zend_mm_free_slot *zend_mm_get_next_free_slot(zend_mm_ zend_mm_panic("zend_mm_heap corrupted"); } } + zend_mm_asan_poison_free_slot(slot, bin_num); return (zend_mm_free_slot*)next; } #else /* ZEND_MM_HEAP_PROTECTION */ # define zend_mm_set_next_free_slot(heap, bin_num, slot, next) do { \ - (slot)->next_free_slot = (next); \ + ASAN_UNPOISON_MEMORY_REGION(slot, bin_data_size[bin_num]); \ + (slot)->next_free_slot = (next); \ + zend_mm_asan_poison_free_slot(slot, bin_num); \ } while (0) -# define zend_mm_get_next_free_slot(heap, bin_num, slot) (slot)->next_free_slot + +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) +{ + ASAN_UNPOISON_MEMORY_REGION(slot, bin_data_size[bin_num]); + zend_mm_free_slot *next = slot->next_free_slot; + zend_mm_asan_poison_free_slot(slot, bin_num); + return next; +} #endif /* ZEND_MM_HEAP_PROTECTION */ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint32_t bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) @@ -1588,6 +1625,7 @@ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint dbg->size = 0; } while (0); #endif + zend_mm_asan_poison_free_slot(p, bin_num); /* return first element */ return bin; @@ -1609,6 +1647,7 @@ static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, uint32_t if (EXPECTED(heap->free_slot[bin_num] != NULL)) { zend_mm_free_slot *p = heap->free_slot[bin_num]; heap->free_slot[bin_num] = zend_mm_get_next_free_slot(heap, bin_num, p); + ASAN_UNPOISON_MEMORY_REGION(p, bin_data_size[bin_num]); return p; } else { return zend_mm_alloc_small_slow(heap, bin_num ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); @@ -1861,6 +1900,7 @@ static zend_never_inline void *zend_mm_realloc_huge(zend_mm_heap *heap, void *pt /* try to map tail right after this block */ if (zend_mm_chunk_extend(heap, ptr, old_size, new_size)) { + ASAN_UNPOISON_MEMORY_REGION((char*)ptr + old_size, new_size - old_size); #if ZEND_MM_STAT || ZEND_MM_LIMIT heap->real_size += new_size - old_size; #endif @@ -1997,6 +2037,7 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p int new_pages_count = (int)(new_size / ZEND_MM_PAGE_SIZE); int rest_pages_count = (int)((old_size - new_size) / ZEND_MM_PAGE_SIZE); + ASAN_POISON_MEMORY_REGION((char*)ptr + new_size, old_size - new_size); #if ZEND_MM_STAT heap->size -= rest_pages_count * ZEND_MM_PAGE_SIZE; #endif @@ -2019,6 +2060,7 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p /* try to allocate tail pages after this block */ if (page_num + new_pages_count <= ZEND_MM_PAGES && zend_mm_bitset_is_free_range(chunk->free_map, page_num + old_pages_count, new_pages_count - old_pages_count)) { + ASAN_UNPOISON_MEMORY_REGION((char*)ptr + old_size, new_size - old_size); #if ZEND_MM_STAT do { size_t size = heap->size + (new_size - old_size); @@ -2266,6 +2308,7 @@ ZEND_API void zend_mm_refresh_key_child(zend_mm_heap *heap) continue; } zend_mm_free_slot *next; + ASAN_UNPOISON_MEMORY_REGION(slot, bin_data_size[i]); while ((next = slot->next_free_slot)) { 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))) { @@ -2273,7 +2316,9 @@ ZEND_API void zend_mm_refresh_key_child(zend_mm_heap *heap) } zend_mm_set_next_free_slot(heap, i, slot, next); slot = next; + ASAN_UNPOISON_MEMORY_REGION(slot, bin_data_size[i]); } + zend_mm_asan_poison_free_slot(slot, i); } zend_mm_rekey_cached_chunks(heap, old_key); @@ -2775,6 +2820,8 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) p = heap->cached_chunks; while (p != NULL) { zend_mm_chunk *q = zend_mm_get_next_cached_chunk(heap, p); + ASAN_POISON_MEMORY_REGION(ZEND_MM_PAGE_ADDR(p, ZEND_MM_FIRST_PAGE), + ZEND_MM_CHUNK_SIZE - ZEND_MM_FIRST_PAGE * ZEND_MM_PAGE_SIZE); memset(p, 0, sizeof(zend_mm_chunk)); zend_mm_set_next_cached_chunk(heap, p, q); p = q; @@ -2782,6 +2829,8 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) /* reinitialize the first chunk and heap */ p = heap->main_chunk; + ASAN_POISON_MEMORY_REGION(ZEND_MM_PAGE_ADDR(p, ZEND_MM_FIRST_PAGE), + ZEND_MM_CHUNK_SIZE - ZEND_MM_FIRST_PAGE * ZEND_MM_PAGE_SIZE); p->heap = &p->heap_slot; p->next = p; p->prev = p; diff --git a/docs/source/miscellaneous/running-tests.rst b/docs/source/miscellaneous/running-tests.rst index bb2c56dcecd2..8ffbc8d6dda8 100644 --- a/docs/source/miscellaneous/running-tests.rst +++ b/docs/source/miscellaneous/running-tests.rst @@ -83,6 +83,28 @@ The test runner can run tests in parallel, by using the ``-j`` option: php run-tests.php -j24 ext/date/*.phpt +Testing the Native Allocator with AddressSanitizer +================================================== + +Build with ``--enable-address-sanitizer`` to enable AddressSanitizer (ASan). The test runner's +``--asan`` option selects the system allocator with allocation tracking. To test native ZendMM +allocation behavior instead, omit ``--asan``, set ``USE_ZEND_ALLOC=1``, and retain the +sanitizer-specific test exclusions: + +.. code:: shell + + USE_ZEND_ALLOC=1 SKIP_ASAN=1 SKIP_PERF_SENSITIVE=1 \ + sapi/cli/php run-tests.php Zend/tests + +Native ZendMM poisons freed small allocations and page runs, and unpoisons them when reused. It also +poisons storage released during request shutdown. These annotations detect accesses to freed storage +until it is reused; they do not provide a quarantine or exact requested-size bounds checking. +Allocation size-class padding remains accessible, as do the debug trailers inspected by leak +detection in debug builds. + +The ``zend_mm_asan.phpt`` test in ``ext/zend_test/tests`` checks allocator shadow state directly and +requires both ASan and ``--enable-zend-test``. + ************** Test results ************** diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 4867f2f41c69..3c2d09ccd494 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -46,6 +46,10 @@ #include +#ifdef __SANITIZE_ADDRESS__ +# include +#endif + #if defined(HAVE_LIBXML) && !defined(PHP_WIN32) # include # include @@ -269,6 +273,124 @@ static ZEND_FUNCTION(zend_test_callback) free(p); } +#ifdef __SANITIZE_ADDRESS__ +static ZEND_FUNCTION(zend_test_asan_zend_mm) +{ + zend_long old_size, new_size; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(old_size) + Z_PARAM_LONG(new_size) + ZEND_PARSE_PARAMETERS_END(); + + if (old_size < 1) { + zend_argument_value_error(1, "must be greater than 0"); + RETURN_THROWS(); + } + if (new_size < 1) { + zend_argument_value_error(2, "must be greater than 0"); + RETURN_THROWS(); + } + + zend_mm_heap *heap = zend_mm_startup(); + char *ptr = zend_mm_alloc(heap, old_size); + bool allocated = __asan_region_is_poisoned(ptr, old_size) == NULL; + memset(ptr, 0x42, old_size); + + size_t old_capacity = zend_mm_block_size(heap, ptr); + /* Unmapped huge allocations need not retain ASan shadow state. */ + bool old_huge = old_capacity > ZEND_MM_MAX_LARGE_SIZE; + char *old_ptr = ptr; + ptr = zend_mm_realloc(heap, ptr, new_size); + size_t new_capacity = zend_mm_block_size(heap, ptr); + bool new_huge = new_capacity > ZEND_MM_MAX_LARGE_SIZE; + bool reallocated = __asan_region_is_poisoned(ptr, new_size) == NULL; + bool moved = ptr == old_ptr || old_huge || __asan_address_is_poisoned(old_ptr); + bool truncated = ptr != old_ptr || old_huge || new_capacity >= old_capacity + || __asan_address_is_poisoned(ptr + old_capacity - 1); + bool preserved = true; + for (size_t i = 0; i < (size_t) MIN(old_size, new_size); i++) { + preserved &= ptr[i] == 0x42; + } + zend_mm_free(heap, ptr); + bool released = new_huge || (__asan_address_is_poisoned(ptr) + && __asan_address_is_poisoned(ptr + new_size - 1)); + zend_mm_refresh_key_child(heap); + bool rekeyed = new_huge || __asan_address_is_poisoned(ptr); + ptr = zend_mm_alloc(heap, new_size); + bool reused = __asan_region_is_poisoned(ptr, new_size) == NULL; + zend_mm_free(heap, ptr); + zend_mm_gc(heap); + bool collected = new_huge || __asan_address_is_poisoned(ptr); + + ptr = zend_mm_realloc2(heap, NULL, old_size, 0); + memset(ptr, 0x24, old_size); + ptr = zend_mm_realloc2(heap, ptr, new_size, MIN(old_size, new_size)); + bool copied = __asan_region_is_poisoned(ptr, new_size) == NULL; + for (size_t i = 0; i < (size_t) MIN(old_size, new_size); i++) { + copied &= ptr[i] == 0x24; + } + zend_mm_free(heap, ptr); + + ptr = zend_mm_alloc(heap, old_size); + reused &= __asan_region_is_poisoned(ptr, old_size) == NULL; + zend_mm_shutdown(heap, false, true); + bool reset = old_huge || (__asan_address_is_poisoned(ptr) + && __asan_address_is_poisoned(ptr + old_size - 1)); + ptr = zend_mm_alloc(heap, old_size); + bool restarted = __asan_region_is_poisoned(ptr, old_size) == NULL; + zend_mm_free(heap, ptr); + + zend_mm_heap *orig_heap = zend_mm_set_heap(heap); + ptr = emalloc(1); + bool specialized = __asan_region_is_poisoned(ptr, 1) == NULL; + efree_size(ptr, 1); + specialized &= __asan_address_is_poisoned(ptr); + ptr = emalloc(64); + specialized &= __asan_region_is_poisoned(ptr, 64) == NULL; + efree_size(ptr, 64); + specialized &= __asan_address_is_poisoned(ptr); + ptr = emalloc(8192); + specialized &= __asan_region_is_poisoned(ptr, 8192) == NULL; + efree_size(ptr, 8192); + specialized &= __asan_address_is_poisoned(ptr); + zend_mm_set_heap(orig_heap); + + char *blocks[4]; + for (size_t i = 0; i < 4; i++) { + blocks[i] = zend_mm_alloc(heap, 1024 * 1024); + memset(blocks[i], 0x42, 1024 * 1024); + } + zend_mm_shutdown(heap, false, true); + bool cached = true; + for (size_t i = 0; i < 4; i++) { + blocks[i] = zend_mm_alloc(heap, 1024 * 1024); + cached &= __asan_region_is_poisoned(blocks[i], 1024 * 1024) == NULL; + memset(blocks[i], 0x24, 1024 * 1024); + } + for (size_t i = 0; i < 4; i++) { + zend_mm_free(heap, blocks[i]); + } + zend_mm_shutdown(heap, true, true); + + array_init(return_value); + add_assoc_bool(return_value, "allocated", allocated); + add_assoc_bool(return_value, "reallocated", reallocated); + add_assoc_bool(return_value, "moved", moved); + add_assoc_bool(return_value, "truncated", truncated); + add_assoc_bool(return_value, "preserved", preserved); + add_assoc_bool(return_value, "released", released); + add_assoc_bool(return_value, "rekeyed", rekeyed); + add_assoc_bool(return_value, "collected", collected); + add_assoc_bool(return_value, "reused", reused); + add_assoc_bool(return_value, "copied", copied); + add_assoc_bool(return_value, "reset", reset); + add_assoc_bool(return_value, "restarted", restarted); + add_assoc_bool(return_value, "specialized", specialized); + add_assoc_bool(return_value, "cached", cached); +} +#endif + /* Leak a refcounted variable */ static ZEND_FUNCTION(zend_leak_variable) { diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index db5ca629ade2..ce3b30b823aa 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -301,6 +301,10 @@ function zend_delref(mixed $variable): void {} function zend_test_erealloc_block_size(int $old_size, int $new_size): array {} +#ifdef __SANITIZE_ADDRESS__ + function zend_test_asan_zend_mm(int $old_size, int $new_size): array {} +#endif + function zend_bool(bool $param): bool {} function zend_bool_or_null(bool|null $param): bool|null {} function zend_bool_slow_zpp(bool $param): bool {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 87622c185cff..763d9a06d151 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 test.stub.php instead. - * Stub hash: ea2674e5d5dbf60aa5efab31132064b52a07bddd + * Stub hash: 3e659c4bdb92db8fa5066daacdb9d9f1d1e59e7e * Has decl header: yes */ #include "zend_attributes.h" @@ -64,6 +64,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_erealloc_block_size, 0 ZEND_ARG_TYPE_INFO(0, new_size, IS_LONG, 0) ZEND_END_ARG_INFO() +#if defined(__SANITIZE_ADDRESS__) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_asan_zend_mm, 0, 2, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, old_size, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, new_size, IS_LONG, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_bool, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, param, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -463,6 +470,9 @@ static ZEND_FUNCTION(zend_leak_variable); static ZEND_FUNCTION(zend_leak_bytes); static ZEND_FUNCTION(zend_delref); static ZEND_FUNCTION(zend_test_erealloc_block_size); +#if defined(__SANITIZE_ADDRESS__) +static ZEND_FUNCTION(zend_test_asan_zend_mm); +#endif static ZEND_FUNCTION(zend_bool); static ZEND_FUNCTION(zend_bool_or_null); static ZEND_FUNCTION(zend_bool_slow_zpp); @@ -645,6 +655,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_leak_bytes, arginfo_zend_leak_bytes) ZEND_FE(zend_delref, arginfo_zend_delref) ZEND_FE(zend_test_erealloc_block_size, arginfo_zend_test_erealloc_block_size) +#if defined(__SANITIZE_ADDRESS__) + ZEND_FE(zend_test_asan_zend_mm, arginfo_zend_test_asan_zend_mm) +#endif ZEND_FE(zend_bool, arginfo_zend_bool) ZEND_FE(zend_bool_or_null, arginfo_zend_bool_or_null) ZEND_FE(zend_bool_slow_zpp, arginfo_zend_bool_slow_zpp) diff --git a/ext/zend_test/test_decl.h b/ext/zend_test/test_decl.h index db7652512c83..1c98df57ed2d 100644 --- a/ext/zend_test/test_decl.h +++ b/ext/zend_test/test_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: ea2674e5d5dbf60aa5efab31132064b52a07bddd */ + * Stub hash: 3e659c4bdb92db8fa5066daacdb9d9f1d1e59e7e */ -#ifndef ZEND_TEST_DECL_ea2674e5d5dbf60aa5efab31132064b52a07bddd_H -#define ZEND_TEST_DECL_ea2674e5d5dbf60aa5efab31132064b52a07bddd_H +#ifndef ZEND_TEST_DECL_3e659c4bdb92db8fa5066daacdb9d9f1d1e59e7e_H +#define ZEND_TEST_DECL_3e659c4bdb92db8fa5066daacdb9d9f1d1e59e7e_H typedef enum zend_enum_ZendTestUnitEnum { ZEND_ENUM_ZendTestUnitEnum_Foo = 1, @@ -27,4 +27,4 @@ typedef enum zend_enum_ZendTestEnumWithInterface { ZEND_ENUM_ZendTestEnumWithInterface_Bar = 2, } zend_enum_ZendTestEnumWithInterface; -#endif /* ZEND_TEST_DECL_ea2674e5d5dbf60aa5efab31132064b52a07bddd_H */ +#endif /* ZEND_TEST_DECL_3e659c4bdb92db8fa5066daacdb9d9f1d1e59e7e_H */ diff --git a/ext/zend_test/test_legacy_arginfo.h b/ext/zend_test/test_legacy_arginfo.h index a673b373142a..ec73482e3210 100644 --- a/ext/zend_test/test_legacy_arginfo.h +++ b/ext/zend_test/test_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: ea2674e5d5dbf60aa5efab31132064b52a07bddd + * Stub hash: 3e659c4bdb92db8fa5066daacdb9d9f1d1e59e7e * Has decl header: yes */ #include "zend_constants.h" @@ -59,6 +59,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_test_erealloc_block_size, 0, 0, 2) ZEND_ARG_INFO(0, new_size) ZEND_END_ARG_INFO() +#if defined(__SANITIZE_ADDRESS__) +ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_test_asan_zend_mm, 0, 0, 2) + ZEND_ARG_INFO(0, old_size) + ZEND_ARG_INFO(0, new_size) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_bool, 0, 0, 1) ZEND_ARG_INFO(0, param) ZEND_END_ARG_INFO() @@ -387,6 +394,9 @@ static ZEND_FUNCTION(zend_leak_variable); static ZEND_FUNCTION(zend_leak_bytes); static ZEND_FUNCTION(zend_delref); static ZEND_FUNCTION(zend_test_erealloc_block_size); +#if defined(__SANITIZE_ADDRESS__) +static ZEND_FUNCTION(zend_test_asan_zend_mm); +#endif static ZEND_FUNCTION(zend_bool); static ZEND_FUNCTION(zend_bool_or_null); static ZEND_FUNCTION(zend_bool_slow_zpp); @@ -541,6 +551,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_leak_bytes, arginfo_zend_leak_bytes) ZEND_FE(zend_delref, arginfo_zend_delref) ZEND_FE(zend_test_erealloc_block_size, arginfo_zend_test_erealloc_block_size) +#if defined(__SANITIZE_ADDRESS__) + ZEND_FE(zend_test_asan_zend_mm, arginfo_zend_test_asan_zend_mm) +#endif ZEND_FE(zend_bool, arginfo_zend_bool) ZEND_FE(zend_bool_or_null, arginfo_zend_bool_or_null) ZEND_FE(zend_bool_slow_zpp, arginfo_zend_bool_slow_zpp) diff --git a/ext/zend_test/tests/zend_mm_asan.phpt b/ext/zend_test/tests/zend_mm_asan.phpt new file mode 100644 index 000000000000..d65df42e82c9 --- /dev/null +++ b/ext/zend_test/tests/zend_mm_asan.phpt @@ -0,0 +1,26 @@ +--TEST-- +Native ZendMM ASan allocation lifetime annotations +--EXTENSIONS-- +zend_test +--SKIPIF-- + +--FILE-- + $passed) { + if (!$passed) { + echo "$oldSize -> $newSize: $check failed\n"; + } + } + } +} +echo "Done\n"; +?> +--EXPECT-- +Done