@@ -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 (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
+
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,200 @@ 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
+ 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
+
3024
3240
static void alloc_globals_ctor (zend_alloc_globals * alloc_globals )
3025
3241
{
3026
3242
char * tmp ;
@@ -3057,6 +3273,14 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals)
3057
3273
zend_mm_use_huge_pages = true;
3058
3274
}
3059
3275
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
3060
3284
}
3061
3285
3062
3286
#ifdef ZTS
0 commit comments