5858#include "zend_multiply.h"
5959#include "zend_bitset.h"
6060#include "zend_mmap.h"
61+ #include "zend_portability.h"
62+ #include "ext/random/php_random_csprng.h"
63+ #include "ext/random/php_random.h"
6164#include <signal.h>
6265
6366#ifdef HAVE_UNISTD_H
@@ -245,6 +248,7 @@ struct _zend_mm_heap {
245248 size_t size ; /* current memory usage */
246249 size_t peak ; /* peak memory usage */
247250#endif
251+ uintptr_t key ; /* free slot shadow ptr key */
248252 zend_mm_free_slot * free_slot [ZEND_MM_BINS ]; /* free lists for small sizes */
249253#if ZEND_MM_STAT || ZEND_MM_LIMIT
250254 size_t real_size ; /* current size of allocated pages */
@@ -275,6 +279,8 @@ struct _zend_mm_heap {
275279 } custom_heap ;
276280 HashTable * tracked_allocs ;
277281#endif
282+ pid_t pid ;
283+ php_random_status_state_xoshiro256starstar random_state ;
278284};
279285
280286struct _zend_mm_chunk {
@@ -318,19 +324,37 @@ struct _zend_mm_huge_list {
318324#define ZEND_MM_PAGE_ADDR (chunk , page_num ) \
319325 ((void*)(((zend_mm_page*)(chunk)) + (page_num)))
320326
321- #define _BIN_DATA_SIZE (num , size , elements , pages , x , y ) size,
327+ #define _BIN_DATA_SIZE (num , size , elements , pages , x , y ) \
328+ /* Need two words for free slot pointer and shadow */ \
329+ MAX(size, sizeof(zend_mm_free_slot*)*2)
330+ #define _BIN_DATA_SIZE_C (num , size , elements , pages , x , y ) \
331+ _BIN_DATA_SIZE(num, size, elements, pages, x, y),
322332static const uint32_t bin_data_size [] = {
323- ZEND_MM_BINS_INFO (_BIN_DATA_SIZE , x , y )
333+ ZEND_MM_BINS_INFO (_BIN_DATA_SIZE_C , x , y )
324334};
325335
326- #define _BIN_DATA_ELEMENTS (num , size , elements , pages , x , y ) elements,
336+ #define _BIN_DATA_ELEMENTS (num , size , elements , pages , x , y ) \
337+ /* Adjusting size requires adjusting elements */ \
338+ (elements / (_BIN_DATA_SIZE(num, size, elements, pages, x, y) / size))
339+ #define _BIN_DATA_ELEMENTS_C (num , size , elements , pages , x , y ) \
340+ _BIN_DATA_ELEMENTS(num, size, elements, pages, x, y),
327341static const uint32_t bin_elements [] = {
328- ZEND_MM_BINS_INFO (_BIN_DATA_ELEMENTS , x , y )
342+ ZEND_MM_BINS_INFO (_BIN_DATA_ELEMENTS_C , x , y )
329343};
330344
331- #define _BIN_DATA_PAGES (num , size , elements , pages , x , y ) pages,
345+ #define _BIN_DATA_PAGES (num , size , elements , pages , x , y ) pages
346+ #define _BIN_DATA_PAGES_C (num , size , elements , pages , x , y ) \
347+ _BIN_DATA_PAGES(num, size, elements, pages, x, y),
332348static const uint32_t bin_pages [] = {
333- ZEND_MM_BINS_INFO (_BIN_DATA_PAGES , x , y )
349+ ZEND_MM_BINS_INFO (_BIN_DATA_PAGES_C , x , y )
350+ };
351+
352+ #define _BIN_SHADOW_OFFSET (num , size , elements , pages , x , y ) \
353+ (_BIN_DATA_SIZE(num, size, elements, pages, x, y) - sizeof(zend_mm_free_slot*))
354+ #define _BIN_SHADOW_OFFSET_C (num , size , elements , pages , x , y ) \
355+ _BIN_SHADOW_OFFSET(num, size, elements, pages, x, y),
356+ static const uint32_t bin_shadow_offset [] = {
357+ ZEND_MM_BINS_INFO (_BIN_SHADOW_OFFSET_C , x , y )
334358};
335359
336360#if ZEND_DEBUG
@@ -1248,6 +1272,45 @@ static zend_always_inline int zend_mm_small_size_to_bin(size_t size)
12481272
12491273#define ZEND_MM_SMALL_SIZE_TO_BIN (size ) zend_mm_small_size_to_bin(size)
12501274
1275+ static zend_always_inline zend_mm_free_slot * zend_mm_encode_free_slot (zend_mm_heap * heap , zend_mm_free_slot * slot )
1276+ {
1277+ return (zend_mm_free_slot * )((uintptr_t )slot ^ heap -> key );
1278+ }
1279+
1280+ static zend_always_inline zend_mm_free_slot * zend_mm_decode_free_slot (zend_mm_heap * heap , zend_mm_free_slot * slot )
1281+ {
1282+ return (zend_mm_free_slot * )((uintptr_t )slot ^ heap -> key );
1283+ }
1284+
1285+ static zend_always_inline void zend_mm_set_next_free_slot (zend_mm_heap * heap , uint32_t bin_num , zend_mm_free_slot * slot , zend_mm_free_slot * next )
1286+ {
1287+ slot -> next_free_slot = next ;
1288+ * (zend_mm_free_slot * * )((char * )slot + bin_shadow_offset [bin_num ]) = zend_mm_encode_free_slot (heap , next );
1289+ }
1290+
1291+ static zend_always_inline void zend_mm_copy_next_free_slot (zend_mm_free_slot * dest , uint32_t bin_num , zend_mm_free_slot * from )
1292+ {
1293+ dest -> next_free_slot = from -> next_free_slot ;
1294+ * (zend_mm_free_slot * * )((char * )dest + bin_shadow_offset [bin_num ]) = * (zend_mm_free_slot * * )((char * )from + bin_shadow_offset [bin_num ]);
1295+ }
1296+
1297+ static ZEND_COLD ZEND_NORETURN void zend_mm_free_slot_corrupted (void )
1298+ {
1299+ zend_mm_panic ("zend_mm_heap corrupted" );
1300+ }
1301+
1302+ static zend_always_inline zend_mm_free_slot * zend_mm_check_next_free_slot (zend_mm_heap * heap , uint32_t bin_num , zend_mm_free_slot * slot )
1303+ {
1304+ zend_mm_free_slot * next = slot -> next_free_slot ;
1305+ zend_mm_free_slot * shadow = * (zend_mm_free_slot * * )((char * )slot + bin_shadow_offset [bin_num ]);
1306+ if (EXPECTED (next != NULL )) {
1307+ if (UNEXPECTED (next != zend_mm_decode_free_slot (heap , shadow ))) {
1308+ zend_mm_free_slot_corrupted ();
1309+ }
1310+ }
1311+ return (zend_mm_free_slot * )next ;
1312+ }
1313+
12511314static 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 )
12521315{
12531316 zend_mm_chunk * chunk ;
@@ -1281,7 +1344,7 @@ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint
12811344 end = (zend_mm_free_slot * )((char * )bin + (bin_data_size [bin_num ] * (bin_elements [bin_num ] - 1 )));
12821345 heap -> free_slot [bin_num ] = p = (zend_mm_free_slot * )((char * )bin + bin_data_size [bin_num ]);
12831346 do {
1284- p -> next_free_slot = (zend_mm_free_slot * )((char * )p + bin_data_size [bin_num ]);
1347+ zend_mm_set_next_free_slot ( heap , bin_num , p , (zend_mm_free_slot * )((char * )p + bin_data_size [bin_num ]) );
12851348#if ZEND_DEBUG
12861349 do {
12871350 zend_mm_debug_info * dbg = (zend_mm_debug_info * )((char * )p + bin_data_size [bin_num ] - ZEND_MM_ALIGNED_SIZE (sizeof (zend_mm_debug_info )));
@@ -1317,7 +1380,7 @@ static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_
13171380
13181381 if (EXPECTED (heap -> free_slot [bin_num ] != NULL )) {
13191382 zend_mm_free_slot * p = heap -> free_slot [bin_num ];
1320- heap -> free_slot [bin_num ] = p -> next_free_slot ;
1383+ heap -> free_slot [bin_num ] = zend_mm_check_next_free_slot ( heap , bin_num , p ) ;
13211384 return p ;
13221385 } else {
13231386 return zend_mm_alloc_small_slow (heap , bin_num ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
@@ -1340,7 +1403,7 @@ static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr,
13401403#endif
13411404
13421405 p = (zend_mm_free_slot * )ptr ;
1343- p -> next_free_slot = heap -> free_slot [bin_num ];
1406+ zend_mm_set_next_free_slot ( heap , bin_num , p , heap -> free_slot [bin_num ]) ;
13441407 heap -> free_slot [bin_num ] = p ;
13451408}
13461409
@@ -1904,6 +1967,27 @@ static void zend_mm_free_huge(zend_mm_heap *heap, void *ptr ZEND_FILE_LINE_DC ZE
19041967/* Initialization */
19051968/******************/
19061969
1970+ static zend_result zend_mm_refresh_key (zend_mm_heap * heap )
1971+ {
1972+ php_random_result result = php_random_algo_xoshiro256starstar .generate (& heap -> random_state );
1973+ ZEND_ASSERT (result .size == sizeof (uint64_t ));
1974+ heap -> key = (uintptr_t ) result .result ;
1975+ return SUCCESS ;
1976+ }
1977+
1978+ static zend_result zend_mm_init_key (zend_mm_heap * heap )
1979+ {
1980+ uint64_t seed [4 ];
1981+ if (php_random_bytes (& seed , sizeof (seed ), false) != SUCCESS ) {
1982+ return FAILURE ;
1983+ }
1984+
1985+ php_random_xoshiro256starstar_seed256 (& heap -> random_state ,
1986+ seed [0 ], seed [1 ], seed [2 ], seed [3 ]);
1987+
1988+ return zend_mm_refresh_key (heap );
1989+ }
1990+
19071991static zend_mm_heap * zend_mm_init (void )
19081992{
19091993 zend_mm_chunk * chunk = (zend_mm_chunk * )zend_mm_chunk_alloc_int (ZEND_MM_CHUNK_SIZE , ZEND_MM_CHUNK_SIZE );
@@ -1940,6 +2024,12 @@ static zend_mm_heap *zend_mm_init(void)
19402024 heap -> size = 0 ;
19412025 heap -> peak = 0 ;
19422026#endif
2027+ if (zend_mm_init_key (heap ) != SUCCESS ) {
2028+ #if ZEND_MM_ERROR
2029+ fprintf (stderr , "Can't initialize heap\n" );
2030+ #endif
2031+ return NULL ;
2032+ }
19432033#if ZEND_MM_LIMIT
19442034 heap -> limit = (size_t )Z_L (-1 ) >> 1 ;
19452035 heap -> overflow = 0 ;
@@ -1951,12 +2041,13 @@ static zend_mm_heap *zend_mm_init(void)
19512041 heap -> storage = NULL ;
19522042#endif
19532043 heap -> huge_list = NULL ;
2044+ heap -> pid = getpid ();
19542045 return heap ;
19552046}
19562047
19572048ZEND_API size_t zend_mm_gc (zend_mm_heap * heap )
19582049{
1959- zend_mm_free_slot * p , * * q ;
2050+ zend_mm_free_slot * p , * q ;
19602051 zend_mm_chunk * chunk ;
19612052 size_t page_offset ;
19622053 int page_num ;
@@ -1994,15 +2085,15 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
19942085 has_free_pages = true;
19952086 }
19962087 chunk -> map [page_num ] = ZEND_MM_SRUN_EX (i , free_counter );
1997- p = p -> next_free_slot ;
2088+ p = zend_mm_check_next_free_slot ( heap , i , p ) ;
19982089 }
19992090
20002091 if (!has_free_pages ) {
20012092 continue ;
20022093 }
20032094
2004- q = & heap -> free_slot [i ];
2005- p = * q ;
2095+ q = ( zend_mm_free_slot * ) & heap -> free_slot [i ];
2096+ p = q -> next_free_slot ;
20062097 while (p != NULL ) {
20072098 chunk = (zend_mm_chunk * )ZEND_MM_ALIGNED_BASE (p , ZEND_MM_CHUNK_SIZE );
20082099 ZEND_MM_CHECK (chunk -> heap == heap , "zend_mm_heap corrupted" );
@@ -2020,11 +2111,19 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
20202111 ZEND_ASSERT (ZEND_MM_SRUN_BIN_NUM (info ) == i );
20212112 if (ZEND_MM_SRUN_FREE_COUNTER (info ) == bin_elements [i ]) {
20222113 /* remove from cache */
2023- p = p -> next_free_slot ;
2024- * q = p ;
2114+ if (q == (zend_mm_free_slot * )& heap -> free_slot [i ]) {
2115+ q -> next_free_slot = zend_mm_check_next_free_slot (heap , i , p );
2116+ } else {
2117+ zend_mm_copy_next_free_slot ((zend_mm_free_slot * )q , i , p );
2118+ }
2119+ p = zend_mm_check_next_free_slot (heap , i , p );
20252120 } else {
2026- q = & p -> next_free_slot ;
2027- p = * q ;
2121+ q = p ;
2122+ if (q == (zend_mm_free_slot * )& heap -> free_slot [i ]) {
2123+ p = q -> next_free_slot ;
2124+ } else {
2125+ p = zend_mm_check_next_free_slot (heap , i , q );
2126+ }
20282127 }
20292128 }
20302129 }
@@ -2376,6 +2475,18 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
23762475 memset (p -> free_map , 0 , sizeof (p -> free_map ) + sizeof (p -> map ));
23772476 p -> free_map [0 ] = (1L << ZEND_MM_FIRST_PAGE ) - 1 ;
23782477 p -> map [0 ] = ZEND_MM_LRUN (ZEND_MM_FIRST_PAGE );
2478+
2479+ pid_t pid = getpid ();
2480+ if (heap -> pid != pid ) {
2481+ if (zend_mm_init_key (heap ) != SUCCESS ) {
2482+ zend_mm_panic ("Can't initialize heap" );
2483+ }
2484+ heap -> pid = pid ;
2485+ } else {
2486+ if (zend_mm_refresh_key (heap ) != SUCCESS ) {
2487+ zend_mm_panic ("Can't initialize heap" );
2488+ }
2489+ }
23792490 }
23802491}
23812492
@@ -3052,6 +3163,12 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
30523163 heap -> size = 0 ;
30533164 heap -> peak = 0 ;
30543165#endif
3166+ if (zend_mm_init_key (heap ) != SUCCESS ) {
3167+ #if ZEND_MM_ERROR
3168+ fprintf (stderr , "Can't initialize heap\n" );
3169+ #endif
3170+ return NULL ;
3171+ }
30553172#if ZEND_MM_LIMIT
30563173 heap -> limit = (size_t )Z_L (-1 ) >> 1 ;
30573174 heap -> overflow = 0 ;
@@ -3076,6 +3193,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
30763193 memcpy (storage -> data , data , data_size );
30773194 }
30783195 heap -> storage = storage ;
3196+ heap -> pid = getpid ();
30793197 return heap ;
30803198#else
30813199 return NULL ;
0 commit comments