@@ -305,7 +305,17 @@ struct _zend_mm_heap {
305
305
size_t (* _gc )(void );
306
306
void (* _shutdown )(bool full , bool silent );
307
307
} 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
+ };
309
319
#endif
310
320
pid_t pid ;
311
321
zend_random_bytes_insecure_state rand_state ;
@@ -2389,8 +2399,19 @@ static void zend_mm_check_leaks(zend_mm_heap *heap)
2389
2399
#if ZEND_MM_CUSTOM
2390
2400
static void * tracked_malloc (size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
2391
2401
static 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 );
2392
2403
#endif
2393
2404
2405
+ static void zend_mm_check_freelists (zend_mm_heap * heap )
2406
+ {
2407
+ for (int 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
+
2394
2415
ZEND_API void zend_mm_shutdown (zend_mm_heap * heap , bool full , bool silent )
2395
2416
{
2396
2417
zend_mm_chunk * p ;
@@ -2555,8 +2576,9 @@ ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *ptr
2555
2576
if (size_zv ) {
2556
2577
return Z_LVAL_P (size_zv );
2557
2578
}
2579
+ } else if (heap -> custom_heap ._malloc != poison_malloc ) {
2580
+ return 0 ;
2558
2581
}
2559
- return 0 ;
2560
2582
}
2561
2583
#endif
2562
2584
return zend_mm_size (heap , ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
@@ -3021,6 +3043,253 @@ static void tracked_free_all(zend_mm_heap *heap) {
3021
3043
}
3022
3044
#endif
3023
3045
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
+ if (SIZE_MAX - heap -> debug .padding * 2 < size ) {
3096
+ zend_mm_panic ("Integer overflow in memory allocation" );
3097
+ }
3098
+ size += heap -> debug .padding * 2 ;
3099
+
3100
+ size_t oldsize ;
3101
+ if (ptr ) {
3102
+ ptr = (char * )ptr - heap -> debug .padding ;
3103
+ oldsize = zend_mm_size (heap , ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3104
+
3105
+ if (oldsize > size ) {
3106
+ if (heap -> debug .poison_free ) {
3107
+ memset ((char * )ptr + size , heap -> debug .poison_free_value , oldsize - size );
3108
+ }
3109
+ }
3110
+ } else {
3111
+ oldsize = 0 ;
3112
+ }
3113
+
3114
+ void * old_ptr = ptr ;
3115
+ ptr = zend_mm_realloc_heap (heap , ptr , size , 0 , size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3116
+
3117
+ if (EXPECTED (ptr )) {
3118
+ size = zend_mm_size (heap , ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
3119
+ if (old_ptr != ptr ) {
3120
+ if (old_ptr ) {
3121
+ /* old ptr was freed */
3122
+ if (heap -> debug .poison_free && oldsize <= ZEND_MM_MAX_SMALL_SIZE ) {
3123
+ #if ZEND_DEBUG
3124
+ size_t len = oldsize - sizeof (zend_mm_free_slot * ) - sizeof (zend_mm_debug_info );
3125
+ #else
3126
+ size_t len = oldsize - sizeof (zend_mm_free_slot * ) * 2 ;
3127
+ #endif
3128
+ memset ((char * )old_ptr + sizeof (zend_mm_free_slot * ), heap -> debug .poison_free_value , len );
3129
+ }
3130
+ }
3131
+ /* ptr is a new allocation */
3132
+ if (heap -> debug .poison_alloc ) {
3133
+ if (oldsize == 0 ) {
3134
+ #if ZEND_DEBUG
3135
+ memset (ptr , heap -> debug .poison_alloc_value , size - sizeof (zend_mm_debug_info ));
3136
+ #else
3137
+ memset (ptr , heap -> debug .poison_alloc_value , size );
3138
+ #endif
3139
+ /* old content was copied to ptr, don't override it */
3140
+ } else if (size > oldsize ) {
3141
+ #if ZEND_DEBUG
3142
+ memset ((char * )ptr + oldsize - sizeof (zend_mm_debug_info ), heap -> debug .poison_alloc_value , size - oldsize );
3143
+ #else
3144
+ memset ((char * )ptr + oldsize , heap -> debug .poison_alloc_value , size - oldsize );
3145
+ #endif
3146
+ }
3147
+ }
3148
+ } else if (size > oldsize ) {
3149
+ if (heap -> debug .poison_alloc ) {
3150
+ #if ZEND_DEBUG
3151
+ memset ((char * )ptr + oldsize - sizeof (zend_mm_debug_info ), heap -> debug .poison_alloc_value , size - oldsize );
3152
+ #else
3153
+ memset ((char * )ptr + oldsize , heap -> debug .poison_alloc_value , size - oldsize );
3154
+ #endif
3155
+ }
3156
+ } else if (size < oldsize ) {
3157
+ if (heap -> debug .poison_free ) {
3158
+ #if ZEND_DEBUG
3159
+ memset ((char * )ptr + size - sizeof (zend_mm_debug_info ), heap -> debug .poison_free_value , oldsize - size );
3160
+ #else
3161
+ memset ((char * )ptr + size , heap -> debug .poison_free_value , oldsize - size );
3162
+ #endif
3163
+ }
3164
+ }
3165
+
3166
+ ptr = (char * )ptr + heap -> debug .padding ;
3167
+ }
3168
+
3169
+ return ptr ;
3170
+ }
3171
+
3172
+ static size_t poison_gc (void )
3173
+ {
3174
+ zend_mm_heap * heap = AG (mm_heap );
3175
+
3176
+ void * (* _malloc )(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3177
+ void (* _free )(void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3178
+ void * (* _realloc )(void * , size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3179
+ size_t (* _gc )(void );
3180
+ void (* _shutdown )(bool , bool );
3181
+
3182
+ zend_mm_get_custom_handlers_ex (heap , & _malloc , & _free , & _realloc , & _gc , & _shutdown );
3183
+ zend_mm_set_custom_handlers_ex (heap , NULL , NULL , NULL , NULL , NULL );
3184
+
3185
+ size_t collected = zend_mm_gc (heap );
3186
+
3187
+ zend_mm_set_custom_handlers_ex (heap , _malloc , _free , _realloc , _gc , _shutdown );
3188
+
3189
+ return collected ;
3190
+ }
3191
+
3192
+ static void poison_shutdown (bool full , bool silent )
3193
+ {
3194
+ zend_mm_heap * heap = AG (mm_heap );
3195
+
3196
+ void * (* _malloc )(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3197
+ void (* _free )(void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3198
+ void * (* _realloc )(void * , size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC );
3199
+ size_t (* _gc )(void );
3200
+ void (* _shutdown )(bool , bool );
3201
+
3202
+ zend_mm_get_custom_handlers_ex (heap , & _malloc , & _free , & _realloc , & _gc , & _shutdown );
3203
+ zend_mm_set_custom_handlers_ex (heap , NULL , NULL , NULL , NULL , NULL );
3204
+
3205
+ if (heap -> debug .check_freelists_on_shutdown ) {
3206
+ zend_mm_check_freelists (heap );
3207
+ }
3208
+
3209
+ zend_mm_shutdown (heap , full , silent );
3210
+
3211
+ if (!full ) {
3212
+ zend_mm_set_custom_handlers_ex (heap , _malloc , _free , _realloc , _gc , _shutdown );
3213
+ }
3214
+ }
3215
+
3216
+ static void poison_enable (zend_mm_heap * heap , char * parameters )
3217
+ {
3218
+ char * tmp = parameters ;
3219
+ char * end = tmp + strlen (tmp );
3220
+
3221
+ /* Trim heading/trailing whitespaces */
3222
+ while (* tmp == ' ' || * tmp == '\t' || * tmp == '\n' ) {
3223
+ tmp ++ ;
3224
+ }
3225
+ while (end != tmp && (* (end - 1 ) == ' ' || * (end - 1 ) == '\t' || * (end - 1 ) == '\n' )) {
3226
+ end -- ;
3227
+ }
3228
+
3229
+ while (1 ) {
3230
+ char * key = tmp ;
3231
+
3232
+ tmp = memchr (tmp , '=' , end - tmp );
3233
+ if (!tmp ) {
3234
+ size_t key_len = end - key ;
3235
+ fprintf (stderr , "Unexpected EOF after ZEND_MM_DEBUG parameter '%.*s', expected '='\n" ,
3236
+ (int )key_len , key );
3237
+ return ;
3238
+ }
3239
+
3240
+ size_t key_len = tmp - key ;
3241
+ char * value = tmp + 1 ;
3242
+
3243
+ if (key_len == strlen ("poison_alloc" )
3244
+ && !memcmp (key , "poison_alloc" , key_len )) {
3245
+
3246
+ heap -> debug .poison_alloc = true;
3247
+ heap -> debug .poison_alloc_value = (uint8_t ) ZEND_STRTOUL (value , & tmp , 0 );
3248
+
3249
+ } else if (key_len == strlen ("poison_free" )
3250
+ && !memcmp (key , "poison_free" , key_len )) {
3251
+
3252
+ heap -> debug .poison_free = true;
3253
+ heap -> debug .poison_free_value = (uint8_t ) ZEND_STRTOUL (value , & tmp , 0 );
3254
+
3255
+ } else if (key_len == strlen ("padding" )
3256
+ && !memcmp (key , "padding" , key_len )) {
3257
+
3258
+ uint8_t padding = ZEND_STRTOUL (value , & tmp , 0 );
3259
+ if (ZEND_MM_ALIGNED_SIZE (padding ) != padding ) {
3260
+ fprintf (stderr , "ZEND_MM_DEBUG padding must be a multiple of %u, %u given\n" ,
3261
+ (unsigned int )ZEND_MM_ALIGNMENT ,
3262
+ (unsigned int )padding );
3263
+ return ;
3264
+ }
3265
+ heap -> debug .padding = padding ;
3266
+
3267
+ } else if (key_len == strlen ("check_freelists_on_shutdown" )
3268
+ && !memcmp (key , "check_freelists_on_shutdown" , key_len )) {
3269
+
3270
+ heap -> debug .check_freelists_on_shutdown = (bool ) ZEND_STRTOUL (value , & tmp , 0 );
3271
+
3272
+ } else {
3273
+ fprintf (stderr , "Unknown ZEND_MM_DEBUG parameter: '%.*s'\n" ,
3274
+ (int )key_len , key );
3275
+ return ;
3276
+ }
3277
+
3278
+ if (tmp == end ) {
3279
+ break ;
3280
+ }
3281
+ if (* tmp != ',' ) {
3282
+ fprintf (stderr , "Unexpected '%c' after value of ZEND_MM_DEBUG parameter '%.*s', expected ','\n" ,
3283
+ * tmp , (int )key_len , key );
3284
+ return ;
3285
+ }
3286
+ tmp ++ ;
3287
+ }
3288
+
3289
+ zend_mm_set_custom_handlers_ex (heap , poison_malloc , poison_free ,
3290
+ poison_realloc , poison_gc , poison_shutdown );
3291
+ }
3292
+
3024
3293
static void alloc_globals_ctor (zend_alloc_globals * alloc_globals )
3025
3294
{
3026
3295
char * tmp ;
@@ -3057,6 +3326,14 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals)
3057
3326
zend_mm_use_huge_pages = true;
3058
3327
}
3059
3328
alloc_globals -> mm_heap = zend_mm_init ();
3329
+
3330
+ #if ZEND_MM_CUSTOM
3331
+ ZEND_ASSERT (!alloc_globals -> mm_heap -> tracked_allocs );
3332
+ tmp = getenv ("ZEND_MM_DEBUG" );
3333
+ if (tmp ) {
3334
+ poison_enable (alloc_globals -> mm_heap , tmp );
3335
+ }
3336
+ #endif
3060
3337
}
3061
3338
3062
3339
#ifdef ZTS
0 commit comments