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
@@ -200,6 +203,7 @@ typedef struct _zend_mm_free_slot zend_mm_free_slot;
200203typedef struct _zend_mm_chunk zend_mm_chunk ;
201204typedef struct _zend_mm_huge_list zend_mm_huge_list ;
202205
206+
203207static bool zend_mm_use_huge_pages = false;
204208
205209/*
@@ -245,6 +249,7 @@ struct _zend_mm_heap {
245249 size_t size ; /* current memory usage */
246250 size_t peak ; /* peak memory usage */
247251#endif
252+ uintptr_t key ; /* free slot shadow ptr key */
248253 zend_mm_free_slot * free_slot [ZEND_MM_BINS ]; /* free lists for small sizes */
249254#if ZEND_MM_STAT || ZEND_MM_LIMIT
250255 size_t real_size ; /* current size of allocated pages */
@@ -275,6 +280,8 @@ struct _zend_mm_heap {
275280 } custom_heap ;
276281 HashTable * tracked_allocs ;
277282#endif
283+ pid_t pid ;
284+ php_random_status_state_xoshiro256starstar random_state ;
278285};
279286
280287struct _zend_mm_chunk {
@@ -318,19 +325,37 @@ struct _zend_mm_huge_list {
318325#define ZEND_MM_PAGE_ADDR (chunk , page_num ) \
319326 ((void*)(((zend_mm_page*)(chunk)) + (page_num)))
320327
321- #define _BIN_DATA_SIZE (num , size , elements , pages , x , y ) size,
328+ #define _BIN_DATA_SIZE (num , size , elements , pages , x , y ) \
329+ /* Need two words for free slot pointer and shadow */ \
330+ MAX(size, sizeof(zend_mm_free_slot*)*2)
331+ #define _BIN_DATA_SIZE_C (num , size , elements , pages , x , y ) \
332+ _BIN_DATA_SIZE(num, size, elements, pages, x, y),
322333static const uint32_t bin_data_size [] = {
323- ZEND_MM_BINS_INFO (_BIN_DATA_SIZE , x , y )
334+ ZEND_MM_BINS_INFO (_BIN_DATA_SIZE_C , x , y )
324335};
325336
326- #define _BIN_DATA_ELEMENTS (num , size , elements , pages , x , y ) elements,
337+ #define _BIN_DATA_ELEMENTS (num , size , elements , pages , x , y ) \
338+ /* Adjusting size requires adjusting elements */ \
339+ (elements / (_BIN_DATA_SIZE(num, size, elements, pages, x, y) / size))
340+ #define _BIN_DATA_ELEMENTS_C (num , size , elements , pages , x , y ) \
341+ _BIN_DATA_ELEMENTS(num, size, elements, pages, x, y),
327342static const uint32_t bin_elements [] = {
328- ZEND_MM_BINS_INFO (_BIN_DATA_ELEMENTS , x , y )
343+ ZEND_MM_BINS_INFO (_BIN_DATA_ELEMENTS_C , x , y )
329344};
330345
331- #define _BIN_DATA_PAGES (num , size , elements , pages , x , y ) pages,
346+ #define _BIN_DATA_PAGES (num , size , elements , pages , x , y ) pages
347+ #define _BIN_DATA_PAGES_C (num , size , elements , pages , x , y ) \
348+ _BIN_DATA_PAGES(num, size, elements, pages, x, y),
332349static const uint32_t bin_pages [] = {
333- ZEND_MM_BINS_INFO (_BIN_DATA_PAGES , x , y )
350+ ZEND_MM_BINS_INFO (_BIN_DATA_PAGES_C , x , y )
351+ };
352+
353+ #define _BIN_SHADOW_OFFSET (num , size , elements , pages , x , y ) \
354+ (_BIN_DATA_SIZE(num, size, elements, pages, x, y) - sizeof(zend_mm_free_slot*))
355+ #define _BIN_SHADOW_OFFSET_C (num , size , elements , pages , x , y ) \
356+ _BIN_SHADOW_OFFSET(num, size, elements, pages, x, y),
357+ static const uint32_t bin_shadow_offset [] = {
358+ ZEND_MM_BINS_INFO (_BIN_SHADOW_OFFSET_C , x , y )
334359};
335360
336361#if ZEND_DEBUG
@@ -1248,6 +1273,45 @@ static zend_always_inline int zend_mm_small_size_to_bin(size_t size)
12481273
12491274#define ZEND_MM_SMALL_SIZE_TO_BIN (size ) zend_mm_small_size_to_bin(size)
12501275
1276+ static zend_always_inline zend_mm_free_slot * zend_mm_encode_free_slot (zend_mm_heap * heap , zend_mm_free_slot * slot )
1277+ {
1278+ return (zend_mm_free_slot * )((uintptr_t )slot ^ heap -> key );
1279+ }
1280+
1281+ static zend_always_inline zend_mm_free_slot * zend_mm_decode_free_slot (zend_mm_heap * heap , zend_mm_free_slot * slot )
1282+ {
1283+ return (zend_mm_free_slot * )((uintptr_t )slot ^ heap -> key );
1284+ }
1285+
1286+ 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 )
1287+ {
1288+ slot -> next_free_slot = next ;
1289+ * (zend_mm_free_slot * * )((char * )slot + bin_shadow_offset [bin_num ]) = zend_mm_encode_free_slot (heap , next );
1290+ }
1291+
1292+ 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 )
1293+ {
1294+ dest -> next_free_slot = from -> next_free_slot ;
1295+ * (zend_mm_free_slot * * )((char * )dest + bin_shadow_offset [bin_num ]) = * (zend_mm_free_slot * * )((char * )from + bin_shadow_offset [bin_num ]);
1296+ }
1297+
1298+ static ZEND_COLD ZEND_NORETURN void zend_mm_free_slot_corrupted (void )
1299+ {
1300+ zend_mm_panic ("zend_mm_heap corrupted" );
1301+ }
1302+
1303+ 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 )
1304+ {
1305+ zend_mm_free_slot * next = slot -> next_free_slot ;
1306+ zend_mm_free_slot * shadow = * (zend_mm_free_slot * * )((char * )slot + bin_shadow_offset [bin_num ]);
1307+ if (EXPECTED (next != NULL )) {
1308+ if (UNEXPECTED (next != zend_mm_decode_free_slot (heap , shadow ))) {
1309+ zend_mm_free_slot_corrupted ();
1310+ }
1311+ }
1312+ return (zend_mm_free_slot * )next ;
1313+ }
1314+
12511315static 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 )
12521316{
12531317 zend_mm_chunk * chunk ;
@@ -1281,7 +1345,7 @@ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint
12811345 end = (zend_mm_free_slot * )((char * )bin + (bin_data_size [bin_num ] * (bin_elements [bin_num ] - 1 )));
12821346 heap -> free_slot [bin_num ] = p = (zend_mm_free_slot * )((char * )bin + bin_data_size [bin_num ]);
12831347 do {
1284- p -> next_free_slot = (zend_mm_free_slot * )((char * )p + bin_data_size [bin_num ]);
1348+ zend_mm_set_next_free_slot ( heap , bin_num , p , (zend_mm_free_slot * )((char * )p + bin_data_size [bin_num ]) );
12851349#if ZEND_DEBUG
12861350 do {
12871351 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 +1381,7 @@ static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_
13171381
13181382 if (EXPECTED (heap -> free_slot [bin_num ] != NULL )) {
13191383 zend_mm_free_slot * p = heap -> free_slot [bin_num ];
1320- heap -> free_slot [bin_num ] = p -> next_free_slot ;
1384+ heap -> free_slot [bin_num ] = zend_mm_check_next_free_slot ( heap , bin_num , p ) ;
13211385 return p ;
13221386 } else {
13231387 return zend_mm_alloc_small_slow (heap , bin_num ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC );
@@ -1340,7 +1404,7 @@ static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr,
13401404#endif
13411405
13421406 p = (zend_mm_free_slot * )ptr ;
1343- p -> next_free_slot = heap -> free_slot [bin_num ];
1407+ zend_mm_set_next_free_slot ( heap , bin_num , p , heap -> free_slot [bin_num ]) ;
13441408 heap -> free_slot [bin_num ] = p ;
13451409}
13461410
@@ -1904,6 +1968,27 @@ static void zend_mm_free_huge(zend_mm_heap *heap, void *ptr ZEND_FILE_LINE_DC ZE
19041968/* Initialization */
19051969/******************/
19061970
1971+ static zend_result zend_mm_refresh_key (zend_mm_heap * heap )
1972+ {
1973+ php_random_result result = php_random_algo_xoshiro256starstar .generate (& heap -> random_state );
1974+ ZEND_ASSERT (result .size == sizeof (uint64_t ));
1975+ heap -> key = (uintptr_t ) result .result ;
1976+ return SUCCESS ;
1977+ }
1978+
1979+ static zend_result zend_mm_init_key (zend_mm_heap * heap )
1980+ {
1981+ uint64_t seed [4 ];
1982+ if (php_random_bytes (& seed , sizeof (seed ), false) != SUCCESS ) {
1983+ return FAILURE ;
1984+ }
1985+
1986+ php_random_xoshiro256starstar_seed256 (& heap -> random_state ,
1987+ seed [0 ], seed [1 ], seed [2 ], seed [3 ]);
1988+
1989+ return zend_mm_refresh_key (heap );
1990+ }
1991+
19071992static zend_mm_heap * zend_mm_init (void )
19081993{
19091994 zend_mm_chunk * chunk = (zend_mm_chunk * )zend_mm_chunk_alloc_int (ZEND_MM_CHUNK_SIZE , ZEND_MM_CHUNK_SIZE );
@@ -1940,6 +2025,12 @@ static zend_mm_heap *zend_mm_init(void)
19402025 heap -> size = 0 ;
19412026 heap -> peak = 0 ;
19422027#endif
2028+ if (zend_mm_init_key (heap ) != SUCCESS ) {
2029+ #if ZEND_MM_ERROR
2030+ fprintf (stderr , "Can't initialize heap\n" );
2031+ #endif
2032+ return NULL ;
2033+ }
19432034#if ZEND_MM_LIMIT
19442035 heap -> limit = (size_t )Z_L (-1 ) >> 1 ;
19452036 heap -> overflow = 0 ;
@@ -1951,12 +2042,13 @@ static zend_mm_heap *zend_mm_init(void)
19512042 heap -> storage = NULL ;
19522043#endif
19532044 heap -> huge_list = NULL ;
2045+ heap -> pid = getpid ();
19542046 return heap ;
19552047}
19562048
19572049ZEND_API size_t zend_mm_gc (zend_mm_heap * heap )
19582050{
1959- zend_mm_free_slot * p , * * q ;
2051+ zend_mm_free_slot * p , * q ;
19602052 zend_mm_chunk * chunk ;
19612053 size_t page_offset ;
19622054 int page_num ;
@@ -1994,15 +2086,15 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
19942086 has_free_pages = true;
19952087 }
19962088 chunk -> map [page_num ] = ZEND_MM_SRUN_EX (i , free_counter );
1997- p = p -> next_free_slot ;
2089+ p = zend_mm_check_next_free_slot ( heap , i , p ) ;
19982090 }
19992091
20002092 if (!has_free_pages ) {
20012093 continue ;
20022094 }
20032095
2004- q = & heap -> free_slot [i ];
2005- p = * q ;
2096+ q = ( zend_mm_free_slot * ) & heap -> free_slot [i ];
2097+ p = q -> next_free_slot ;
20062098 while (p != NULL ) {
20072099 chunk = (zend_mm_chunk * )ZEND_MM_ALIGNED_BASE (p , ZEND_MM_CHUNK_SIZE );
20082100 ZEND_MM_CHECK (chunk -> heap == heap , "zend_mm_heap corrupted" );
@@ -2020,11 +2112,19 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
20202112 ZEND_ASSERT (ZEND_MM_SRUN_BIN_NUM (info ) == i );
20212113 if (ZEND_MM_SRUN_FREE_COUNTER (info ) == bin_elements [i ]) {
20222114 /* remove from cache */
2023- p = p -> next_free_slot ;
2024- * q = p ;
2115+ if (q == (zend_mm_free_slot * )& heap -> free_slot [i ]) {
2116+ q -> next_free_slot = zend_mm_check_next_free_slot (heap , i , p );
2117+ } else {
2118+ zend_mm_copy_next_free_slot ((zend_mm_free_slot * )q , i , p );
2119+ }
2120+ p = zend_mm_check_next_free_slot (heap , i , p );
20252121 } else {
2026- q = & p -> next_free_slot ;
2027- p = * q ;
2122+ q = p ;
2123+ if (q == (zend_mm_free_slot * )& heap -> free_slot [i ]) {
2124+ p = q -> next_free_slot ;
2125+ } else {
2126+ p = zend_mm_check_next_free_slot (heap , i , q );
2127+ }
20282128 }
20292129 }
20302130 }
@@ -2376,6 +2476,18 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
23762476 memset (p -> free_map , 0 , sizeof (p -> free_map ) + sizeof (p -> map ));
23772477 p -> free_map [0 ] = (1L << ZEND_MM_FIRST_PAGE ) - 1 ;
23782478 p -> map [0 ] = ZEND_MM_LRUN (ZEND_MM_FIRST_PAGE );
2479+
2480+ pid_t pid = getpid ();
2481+ if (heap -> pid != pid ) {
2482+ if (zend_mm_init_key (heap ) != SUCCESS ) {
2483+ zend_mm_panic ("Can't initialize heap" );
2484+ }
2485+ heap -> pid = pid ;
2486+ } else {
2487+ if (zend_mm_refresh_key (heap ) != SUCCESS ) {
2488+ zend_mm_panic ("Can't initialize heap" );
2489+ }
2490+ }
23792491 }
23802492}
23812493
@@ -3052,6 +3164,12 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
30523164 heap -> size = 0 ;
30533165 heap -> peak = 0 ;
30543166#endif
3167+ if (zend_mm_init_key (heap ) != SUCCESS ) {
3168+ #if ZEND_MM_ERROR
3169+ fprintf (stderr , "Can't initialize heap\n" );
3170+ #endif
3171+ return NULL ;
3172+ }
30553173#if ZEND_MM_LIMIT
30563174 heap -> limit = (size_t )Z_L (-1 ) >> 1 ;
30573175 heap -> overflow = 0 ;
@@ -3076,6 +3194,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
30763194 memcpy (storage -> data , data , data_size );
30773195 }
30783196 heap -> storage = storage ;
3197+ heap -> pid = getpid ();
30793198 return heap ;
30803199#else
30813200 return NULL ;
0 commit comments