@@ -188,6 +188,7 @@ typedef zend_ulong zend_mm_bitset; /* 4-byte or 8-byte integer */
188188typedef zend_mm_bitset zend_mm_page_map [ZEND_MM_PAGE_MAP_LEN ]; /* 64B */
189189
190190#define ZEND_MM_IS_FRUN 0x00000000
191+ #define ZEND_MM_IS_RETIRED 0x20000000
191192#define ZEND_MM_IS_LRUN 0x40000000
192193#define ZEND_MM_IS_SRUN 0x80000000
193194
@@ -229,6 +230,7 @@ typedef struct _zend_mm_chunk zend_mm_chunk;
229230typedef struct _zend_mm_huge_list zend_mm_huge_list ;
230231
231232static bool zend_mm_use_huge_pages = false;
233+ static bool zend_mm_no_reuse = false;
232234
233235/*
234236 * Memory is retrieved from OS by chunks of fixed size 2MB.
@@ -287,6 +289,7 @@ struct _zend_mm_heap {
287289#endif
288290
289291 zend_mm_huge_list * huge_list ; /* list of huge allocated blocks */
292+ zend_mm_huge_list * retired_huge_list ;
290293
291294 zend_mm_chunk * main_chunk ;
292295 zend_mm_chunk * cached_chunks ; /* list of unused chunks */
@@ -1261,6 +1264,12 @@ static zend_always_inline void zend_mm_free_large(zend_mm_heap *heap, zend_mm_ch
12611264#if ZEND_MM_STAT
12621265 heap -> size -= pages_count * ZEND_MM_PAGE_SIZE ;
12631266#endif
1267+ if (UNEXPECTED (zend_mm_no_reuse )) {
1268+ ZEND_MM_CHECK (!(chunk -> map [page_num ] & ZEND_MM_IS_RETIRED ), "zend_mm_heap corrupted (double free)" );
1269+ chunk -> map [page_num ] |= ZEND_MM_IS_RETIRED ;
1270+ memset (ZEND_MM_PAGE_ADDR (chunk , page_num ), 0 , pages_count * ZEND_MM_PAGE_SIZE );
1271+ return ;
1272+ }
12641273 zend_mm_free_pages (heap , chunk , page_num , pages_count );
12651274}
12661275
@@ -1496,6 +1505,11 @@ static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr,
14961505 } while (0 );
14971506#endif
14981507
1508+ if (UNEXPECTED (zend_mm_no_reuse )) {
1509+ memset (ptr , 0 , bin_data_size [bin_num ]);
1510+ return ;
1511+ }
1512+
14991513 p = (zend_mm_free_slot * )ptr ;
15001514#if ZEND_MM_HEAP_PROTECTION
15011515 /* Catch the most common double-free pattern for free. */
@@ -1755,6 +1769,11 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p
17551769 zend_mm_debug_info * dbg ;
17561770#endif
17571771
1772+ if (UNEXPECTED (zend_mm_no_reuse ) && ptr != NULL ) {
1773+ old_size = zend_mm_size (heap , ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
1774+ return zend_mm_realloc_slow (heap , ptr , size , MIN (old_size , MIN (size , copy_size )) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
1775+ }
1776+
17581777 page_offset = ZEND_MM_ALIGNED_OFFSET (ptr , ZEND_MM_CHUNK_SIZE );
17591778 if (UNEXPECTED (page_offset == 0 )) {
17601779 if (EXPECTED (ptr == NULL )) {
@@ -1968,6 +1987,12 @@ static size_t zend_mm_del_huge_block(zend_mm_heap *heap, void *ptr ZEND_FILE_LIN
19681987 list -> next -> prev = list -> prev ;
19691988 }
19701989 size = list -> size ;
1990+ if (UNEXPECTED (zend_mm_no_reuse )) {
1991+ list -> next = heap -> retired_huge_list ;
1992+ list -> prev = NULL ;
1993+ heap -> retired_huge_list = list ;
1994+ return size ;
1995+ }
19711996 zend_mm_free_heap (heap , list ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
19721997 return size ;
19731998 }
@@ -2094,10 +2119,14 @@ static void zend_mm_free_huge(zend_mm_heap *heap, void *ptr ZEND_FILE_LINE_DC ZE
20942119 ZEND_MM_CHECK (ZEND_MM_ALIGNED_OFFSET (ptr , ZEND_MM_CHUNK_SIZE ) == 0 , "zend_mm_heap corrupted" );
20952120 size = zend_mm_del_huge_block (heap , ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
20962121 zend_mm_check_huge_block_size (heap , size );
2097- zend_mm_chunk_free (heap , ptr , size );
2122+ if (UNEXPECTED (zend_mm_no_reuse )) {
2123+ memset (ptr , 0 , size );
2124+ } else {
2125+ zend_mm_chunk_free (heap , ptr , size );
20982126#if ZEND_MM_STAT || ZEND_MM_LIMIT
2099- heap -> real_size -= size ;
2127+ heap -> real_size -= size ;
21002128#endif
2129+ }
21012130#if ZEND_MM_STAT
21022131 heap -> size -= size ;
21032132#endif
@@ -2196,6 +2225,7 @@ static zend_mm_heap *zend_mm_init(void)
21962225 heap -> storage = NULL ;
21972226#endif
21982227 heap -> huge_list = NULL ;
2228+ heap -> retired_huge_list = NULL ;
21992229#if ZEND_DEBUG
22002230 heap -> pid = getpid ();
22012231#endif
@@ -2213,6 +2243,10 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
22132243 bool has_free_pages ;
22142244 size_t collected = 0 ;
22152245
2246+ if (UNEXPECTED (zend_mm_no_reuse )) {
2247+ return 0 ;
2248+ }
2249+
22162250#if ZEND_MM_CUSTOM
22172251 if (heap -> use_custom_heap ) {
22182252 size_t (* gc )(void ) = heap -> custom_heap ._gc ;
@@ -2377,6 +2411,10 @@ static zend_long zend_mm_find_leaks(zend_mm_heap *heap, zend_mm_chunk *p, uint32
23772411 i += bin_pages [bin_num ];
23782412 } else /* if (p->map[i] & ZEND_MM_IS_LRUN) */ {
23792413 int pages_count = ZEND_MM_LRUN_PAGES (p -> map [i ]);
2414+ if (p -> map [i ] & ZEND_MM_IS_RETIRED ) {
2415+ i += pages_count ;
2416+ continue ;
2417+ }
23802418 zend_mm_debug_info * dbg = (zend_mm_debug_info * )((char * )p + ZEND_MM_PAGE_SIZE * (i + pages_count ) - ZEND_MM_ALIGNED_SIZE (sizeof (zend_mm_debug_info )));
23812419
23822420 if (dbg -> filename == leak -> filename && dbg -> lineno == leak -> lineno ) {
@@ -2496,6 +2534,10 @@ static void zend_mm_check_leaks(zend_mm_heap *heap)
24962534 i += bin_pages [bin_num ];
24972535 } else /* if (p->map[i] & ZEND_MM_IS_LRUN) */ {
24982536 int pages_count = ZEND_MM_LRUN_PAGES (p -> map [i ]);
2537+ if (p -> map [i ] & ZEND_MM_IS_RETIRED ) {
2538+ i += pages_count ;
2539+ continue ;
2540+ }
24992541 zend_mm_debug_info * dbg = (zend_mm_debug_info * )((char * )p + ZEND_MM_PAGE_SIZE * (i + pages_count ) - ZEND_MM_ALIGNED_SIZE (sizeof (zend_mm_debug_info )));
25002542
25012543 leak .addr = (void * )((char * )p + ZEND_MM_PAGE_SIZE * i );
@@ -2583,6 +2625,21 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
25832625 }
25842626#endif
25852627
2628+ /* Retired mappings stay reserved until all request callbacks have finished. */
2629+ list = heap -> retired_huge_list ;
2630+ heap -> retired_huge_list = NULL ;
2631+ while (list != NULL ) {
2632+ zend_mm_huge_list * next = list -> next ;
2633+ size_t size = list -> size ;
2634+ zend_mm_check_huge_block_size (heap , size );
2635+ zend_mm_chunk_free (heap , list -> ptr , size );
2636+ #if ZEND_MM_STAT || ZEND_MM_LIMIT
2637+ heap -> real_size -= size ;
2638+ #endif
2639+ zend_mm_free_heap (heap , list ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC );
2640+ list = next ;
2641+ }
2642+
25862643#if ZEND_DEBUG
25872644 if (!silent ) {
25882645 char * tmp = getenv ("ZEND_ALLOC_PRINT_LEAKS" );
@@ -3401,6 +3458,9 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals)
34013458#if ZEND_MM_CUSTOM
34023459 tmp = getenv ("USE_ZEND_ALLOC" );
34033460 if (tmp && !ZEND_ATOL (tmp )) {
3461+ if (zend_mm_no_reuse ) {
3462+ zend_mm_panic ("ZEND_MM_NO_REUSE requires native ZendMM (USE_ZEND_ALLOC=1)" );
3463+ }
34043464 bool tracked = (tmp = getenv ("USE_TRACKED_ALLOC" )) && ZEND_ATOL (tmp );
34053465 zend_mm_heap * mm_heap = alloc_globals -> mm_heap = malloc (sizeof (zend_mm_heap ));
34063466 memset (mm_heap , 0 , sizeof (zend_mm_heap ));
@@ -3449,6 +3509,12 @@ static void alloc_globals_dtor(zend_alloc_globals *alloc_globals)
34493509
34503510ZEND_API void start_memory_manager (void )
34513511{
3512+ const char * no_reuse = getenv ("ZEND_MM_NO_REUSE" );
3513+ if (no_reuse && strcmp (no_reuse , "0" ) != 0 && strcmp (no_reuse , "1" ) != 0 ) {
3514+ zend_mm_panic ("ZEND_MM_NO_REUSE must be 0 or 1" );
3515+ }
3516+ zend_mm_no_reuse = no_reuse && strcmp (no_reuse , "1" ) == 0 ;
3517+
34523518#ifndef _WIN32
34533519# if defined(_SC_PAGESIZE )
34543520 REAL_PAGE_SIZE = sysconf (_SC_PAGESIZE );
@@ -3506,6 +3572,10 @@ ZEND_API void zend_mm_set_custom_handlers_ex(zend_mm_heap *heap,
35063572#if ZEND_MM_CUSTOM
35073573 zend_mm_heap * _heap = (zend_mm_heap * )heap ;
35083574
3575+ if (UNEXPECTED (zend_mm_no_reuse ) && (_malloc || _free || _realloc || _gc || _shutdown )) {
3576+ zend_mm_panic ("ZEND_MM_NO_REUSE does not support custom heap handlers" );
3577+ }
3578+
35093579 if (!_malloc && !_free && !_realloc ) {
35103580 _heap -> use_custom_heap = ZEND_MM_CUSTOM_HEAP_NONE ;
35113581 } else {
@@ -3585,6 +3655,9 @@ ZEND_API zend_mm_heap *zend_mm_startup(void)
35853655
35863656ZEND_API zend_mm_heap * zend_mm_startup_ex (const zend_mm_handlers * handlers , void * data , size_t data_size )
35873657{
3658+ if (UNEXPECTED (zend_mm_no_reuse )) {
3659+ zend_mm_panic ("ZEND_MM_NO_REUSE does not support custom storage handlers" );
3660+ }
35883661#if ZEND_MM_STORAGE
35893662 zend_mm_storage * storage ;
35903663 zend_mm_storage tmp_storage = {
@@ -3636,6 +3709,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
36363709#endif
36373710 heap -> storage = & tmp_storage ;
36383711 heap -> huge_list = NULL ;
3712+ heap -> retired_huge_list = NULL ;
36393713 memset (heap -> free_slot , 0 , sizeof (heap -> free_slot ));
36403714 storage = _zend_mm_alloc (heap , sizeof (zend_mm_storage ) + data_size ZEND_FILE_LINE_CC ZEND_FILE_LINE_CC );
36413715 if (!storage ) {
@@ -3702,4 +3776,3 @@ ZEND_API char * __zend_strdup(const char *s)
37023776 }
37033777 zend_out_of_memory ();
37043778}
3705-
0 commit comments