@@ -305,7 +305,17 @@ struct _zend_mm_heap {
305305 size_t (* _gc )(void );
306306 void (* _shutdown )(bool full , bool silent );
307307 } custom_heap ;
308- HashTable * tracked_allocs ;
308+ union {
309+ HashTable * tracked_allocs ;
310+ struct {
311+ bool poison_alloc ;
312+ uint8_t poison_alloc_value ;
313+ bool poison_free ;
314+ uint8_t poison_free_value ;
315+ uint8_t padding ;
316+ bool check_freelists_on_shutdown ;
317+ } debug ;
318+ };
309319#endif
310320 pid_t pid ;
311321 zend_random_bytes_insecure_state rand_state ;
@@ -2389,8 +2399,19 @@ static void zend_mm_check_leaks(zend_mm_heap *heap)
23892399#if ZEND_MM_CUSTOM
23902400static void * tracked_malloc (size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
23912401static void tracked_free_all (zend_mm_heap * heap );
2402+ static void * poison_malloc (size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
23922403#endif
23932404
2405+ static void zend_mm_check_freelists (zend_mm_heap * heap )
2406+ {
2407+ for (uint32_t bin_num = 0 ; bin_num < ZEND_MM_BINS ; bin_num ++ ) {
2408+ zend_mm_free_slot * slot = heap -> free_slot [bin_num ];
2409+ while (slot ) {
2410+ slot = zend_mm_get_next_free_slot (heap , bin_num , slot );
2411+ }
2412+ }
2413+ }
2414+
23942415ZEND_API void zend_mm_shutdown (zend_mm_heap * heap , bool full , bool silent )
23952416{
23962417 zend_mm_chunk * p ;
@@ -2555,8 +2576,9 @@ ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *ptr
25552576 if (size_zv ) {
25562577 return Z_LVAL_P (size_zv );
25572578 }
2579+ } else if (heap -> custom_heap ._malloc != poison_malloc ) {
2580+ return 0 ;
25582581 }
2559- return 0 ;
25602582 }
25612583#endif
25622584 return zend_mm_size (heap , ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
@@ -3021,6 +3043,200 @@ static void tracked_free_all(zend_mm_heap *heap) {
30213043}
30223044#endif
30233045
3046+ static void * poison_malloc (size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC )
3047+ {
3048+ zend_mm_heap * heap = AG (mm_heap );
3049+
3050+ if (SIZE_MAX - heap -> debug .padding * 2 < size ) {
3051+ zend_mm_panic ("Integer overflow in memory allocation" );
3052+ }
3053+ size += heap -> debug .padding * 2 ;
3054+
3055+ void * ptr = zend_mm_alloc_heap (heap , size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3056+
3057+ if (EXPECTED (ptr )) {
3058+ if (heap -> debug .poison_alloc ) {
3059+ memset (ptr , heap -> debug .poison_alloc_value , size );
3060+ }
3061+
3062+ ptr = (char * )ptr + heap -> debug .padding ;
3063+ }
3064+
3065+ return ptr ;
3066+ }
3067+
3068+ static void poison_free (void * ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC )
3069+ {
3070+ zend_mm_heap * heap = AG (mm_heap );
3071+
3072+ if (EXPECTED (ptr )) {
3073+ /* zend_mm_shutdown() will try to free the heap when custom handlers
3074+ * are installed */
3075+ if (UNEXPECTED (ptr == heap )) {
3076+ return ;
3077+ }
3078+
3079+ ptr = (char * )ptr - heap -> debug .padding ;
3080+
3081+ size_t size = zend_mm_size (heap , ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3082+
3083+ if (heap -> debug .poison_free ) {
3084+ memset (ptr , heap -> debug .poison_free_value , size );
3085+ }
3086+ }
3087+
3088+ zend_mm_free_heap (heap , ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3089+ }
3090+
3091+ static void * poison_realloc (void * ptr , size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC )
3092+ {
3093+ zend_mm_heap * heap = AG (mm_heap );
3094+
3095+ void * new = poison_malloc (size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3096+
3097+ if (ptr ) {
3098+ /* Determine the size of the old allocation from the unpadded pointer. */
3099+ size_t oldsize = zend_mm_size (heap , (char * )ptr - heap -> debug .padding ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3100+
3101+ /* Remove the padding size to determine the size that is available to the user. */
3102+ oldsize -= (2 * heap -> debug .padding );
3103+
3104+ #if ZEND_DEBUG
3105+ oldsize -= sizeof (zend_mm_debug_info );
3106+ #endif
3107+
3108+ memcpy (new , ptr , MIN (oldsize , size ));
3109+ poison_free (ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3110+ }
3111+
3112+ return new ;
3113+ }
3114+
3115+ static size_t poison_gc (void )
3116+ {
3117+ zend_mm_heap * heap = AG (mm_heap );
3118+
3119+ void * (* _malloc )(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3120+ void (* _free )(void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3121+ void * (* _realloc )(void * , size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3122+ size_t (* _gc )(void );
3123+ void (* _shutdown )(bool , bool );
3124+
3125+ zend_mm_get_custom_handlers_ex (heap , & _malloc , & _free , & _realloc , & _gc , & _shutdown );
3126+ zend_mm_set_custom_handlers_ex (heap , NULL , NULL , NULL , NULL , NULL );
3127+
3128+ size_t collected = zend_mm_gc (heap );
3129+
3130+ zend_mm_set_custom_handlers_ex (heap , _malloc , _free , _realloc , _gc , _shutdown );
3131+
3132+ return collected ;
3133+ }
3134+
3135+ static void poison_shutdown (bool full , bool silent )
3136+ {
3137+ zend_mm_heap * heap = AG (mm_heap );
3138+
3139+ void * (* _malloc )(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3140+ void (* _free )(void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3141+ void * (* _realloc )(void * , size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3142+ size_t (* _gc )(void );
3143+ void (* _shutdown )(bool , bool );
3144+
3145+ zend_mm_get_custom_handlers_ex (heap , & _malloc , & _free , & _realloc , & _gc , & _shutdown );
3146+ zend_mm_set_custom_handlers_ex (heap , NULL , NULL , NULL , NULL , NULL );
3147+
3148+ if (heap -> debug .check_freelists_on_shutdown ) {
3149+ zend_mm_check_freelists (heap );
3150+ }
3151+
3152+ zend_mm_shutdown (heap , full , silent );
3153+
3154+ if (!full ) {
3155+ zend_mm_set_custom_handlers_ex (heap , _malloc , _free , _realloc , _gc , _shutdown );
3156+ }
3157+ }
3158+
3159+ static void poison_enable (zend_mm_heap * heap , char * parameters )
3160+ {
3161+ char * tmp = parameters ;
3162+ char * end = tmp + strlen (tmp );
3163+
3164+ /* Trim heading/trailing whitespaces */
3165+ while (* tmp == ' ' || * tmp == '\t' || * tmp == '\n' ) {
3166+ tmp ++ ;
3167+ }
3168+ while (end != tmp && (* (end - 1 ) == ' ' || * (end - 1 ) == '\t' || * (end - 1 ) == '\n' )) {
3169+ end -- ;
3170+ }
3171+
3172+ if (tmp == end ) {
3173+ return ;
3174+ }
3175+
3176+ while (1 ) {
3177+ char * key = tmp ;
3178+
3179+ tmp = memchr (tmp , '=' , end - tmp );
3180+ if (!tmp ) {
3181+ size_t key_len = end - key ;
3182+ fprintf (stderr , "Unexpected EOF after ZEND_MM_DEBUG parameter '%.*s', expected '='\n" ,
3183+ (int )key_len , key );
3184+ return ;
3185+ }
3186+
3187+ size_t key_len = tmp - key ;
3188+ char * value = tmp + 1 ;
3189+
3190+ if (key_len == strlen ("poison_alloc" )
3191+ && !memcmp (key , "poison_alloc" , key_len )) {
3192+
3193+ heap -> debug .poison_alloc = true;
3194+ heap -> debug .poison_alloc_value = (uint8_t ) ZEND_STRTOUL (value , & tmp , 0 );
3195+
3196+ } else if (key_len == strlen ("poison_free" )
3197+ && !memcmp (key , "poison_free" , key_len )) {
3198+
3199+ heap -> debug .poison_free = true;
3200+ heap -> debug .poison_free_value = (uint8_t ) ZEND_STRTOUL (value , & tmp , 0 );
3201+
3202+ } else if (key_len == strlen ("padding" )
3203+ && !memcmp (key , "padding" , key_len )) {
3204+
3205+ uint8_t padding = ZEND_STRTOUL (value , & tmp , 0 );
3206+ if (ZEND_MM_ALIGNED_SIZE (padding ) != padding ) {
3207+ fprintf (stderr , "ZEND_MM_DEBUG padding must be a multiple of %u, %u given\n" ,
3208+ (unsigned int )ZEND_MM_ALIGNMENT ,
3209+ (unsigned int )padding );
3210+ return ;
3211+ }
3212+ heap -> debug .padding = padding ;
3213+
3214+ } else if (key_len == strlen ("check_freelists_on_shutdown" )
3215+ && !memcmp (key , "check_freelists_on_shutdown" , key_len )) {
3216+
3217+ heap -> debug .check_freelists_on_shutdown = (bool ) ZEND_STRTOUL (value , & tmp , 0 );
3218+
3219+ } else {
3220+ fprintf (stderr , "Unknown ZEND_MM_DEBUG parameter: '%.*s'\n" ,
3221+ (int )key_len , key );
3222+ return ;
3223+ }
3224+
3225+ if (tmp == end ) {
3226+ break ;
3227+ }
3228+ if (* tmp != ',' ) {
3229+ fprintf (stderr , "Unexpected '%c' after value of ZEND_MM_DEBUG parameter '%.*s', expected ','\n" ,
3230+ * tmp , (int )key_len , key );
3231+ return ;
3232+ }
3233+ tmp ++ ;
3234+ }
3235+
3236+ zend_mm_set_custom_handlers_ex (heap , poison_malloc , poison_free ,
3237+ poison_realloc , poison_gc , poison_shutdown );
3238+ }
3239+
30243240static void alloc_globals_ctor (zend_alloc_globals * alloc_globals )
30253241{
30263242 char * tmp ;
@@ -3057,6 +3273,14 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals)
30573273 zend_mm_use_huge_pages = true;
30583274 }
30593275 alloc_globals -> mm_heap = zend_mm_init ();
3276+
3277+ #if ZEND_MM_CUSTOM
3278+ ZEND_ASSERT (!alloc_globals -> mm_heap -> tracked_allocs );
3279+ tmp = getenv ("ZEND_MM_DEBUG" );
3280+ if (tmp ) {
3281+ poison_enable (alloc_globals -> mm_heap , tmp );
3282+ }
3283+ #endif
30603284}
30613285
30623286#ifdef ZTS
0 commit comments