@@ -197,6 +197,44 @@ typedef zend_mm_bitset zend_mm_page_map[ZEND_MM_PAGE_MAP_LEN]; /* 64B */
197197
198198#define ZEND_MM_BINS 30
199199
200+ #if defined(_MSC_VER )
201+ # if UINTPTR_MAX == UINT64_MAX
202+ # define BSWAPPTR (u ) _byteswap_uint64(u)
203+ # else
204+ # define BSWAPPTR (u ) _byteswap_ulong(u)
205+ # endif
206+ #else
207+ # if UINTPTR_MAX == UINT64_MAX
208+ # if __has_builtin (__builtin_bswap64 )
209+ # define BSWAPPTR (u ) __builtin_bswap64(u)
210+ # else
211+ zend_always_inline uintptr_t BSWAPPTR (uintptr_t u )
212+ {
213+ return (((u & 0xff00000000000000ULL ) >> 56 )
214+ | ((u & 0x00ff000000000000ULL ) >> 40 )
215+ | ((u & 0x0000ff0000000000ULL ) >> 24 )
216+ | ((u & 0x000000ff00000000ULL ) >> 8 )
217+ | ((u & 0x00000000ff000000ULL ) << 8 )
218+ | ((u & 0x0000000000ff0000ULL ) << 24 )
219+ | ((u & 0x000000000000ff00ULL ) << 40 )
220+ | ((u & 0x00000000000000ffULL ) << 56 ));
221+ }
222+ # endif /* __has_builtin(__builtin_bswap64) */
223+ # else /* UINTPTR_MAX == UINT64_MAX */
224+ # if __has_builtin (__builtin_bswap32 )
225+ # define BSWAPPTR (u ) __builtin_bswap32(u)
226+ # else
227+ zend_always_inline uintptr_t BSWAPPTR (uintptr_t u )
228+ {
229+ return (((u & 0xff000000 ) >> 24 )
230+ | ((u & 0x00ff0000 ) >> 8 )
231+ | ((u & 0x0000ff00 ) << 8 )
232+ | ((u & 0x000000ff ) << 24 ));
233+ }
234+ # endif /* __has_builtin(__builtin_bswap32) */
235+ # endif /* UINTPTR_MAX == UINT64_MAX */
236+ #endif /* defined(_MSC_VER) */
237+
200238typedef struct _zend_mm_page zend_mm_page ;
201239typedef struct _zend_mm_bin zend_mm_bin ;
202240typedef struct _zend_mm_free_slot zend_mm_free_slot ;
@@ -248,7 +286,7 @@ struct _zend_mm_heap {
248286 size_t size ; /* current memory usage */
249287 size_t peak ; /* peak memory usage */
250288#endif
251- uintptr_t key ; /* free slot shadow ptr key */
289+ uintptr_t shadow_key ; /* free slot shadow ptr xor key */
252290 zend_mm_free_slot * free_slot [ZEND_MM_BINS ]; /* free lists for small sizes */
253291#if ZEND_MM_STAT || ZEND_MM_LIMIT
254292 size_t real_size ; /* current size of allocated pages */
@@ -349,14 +387,6 @@ static const uint32_t bin_pages[] = {
349387 ZEND_MM_BINS_INFO (_BIN_DATA_PAGES_C , x , y )
350388};
351389
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 )
358- };
359-
360390#if ZEND_DEBUG
361391ZEND_COLD void zend_debug_alloc_output (char * format , ...)
362392{
@@ -1272,40 +1302,58 @@ static zend_always_inline int zend_mm_small_size_to_bin(size_t size)
12721302
12731303#define ZEND_MM_SMALL_SIZE_TO_BIN (size ) zend_mm_small_size_to_bin(size)
12741304
1305+ /* We keep track of free slots by organizing them in a linked list, with the
1306+ * first word of every free slot being a pointer to the next one.
1307+ *
1308+ * In order to frustrate corruptions, we check the consistency of these pointers
1309+ * before dereference by comparing them with a shadow.
1310+ *
1311+ * The shadow is a copy of the pointer, stored at the end of the slot. It is
1312+ * XOR'ed with a random key, and converted to big-endian so that smaller
1313+ * corruptions affect the most significant bytes, which has a high chance of
1314+ * resulting in an invalid address instead of pointing to an adjacent slot.
1315+ */
1316+
1317+ #define ZEND_MM_FREE_SLOT_PTR_SHADOW (free_slot , bin_num ) \
1318+ *((zend_mm_free_slot**)((char*)(free_slot) + bin_data_size[(bin_num)] - sizeof(zend_mm_free_slot*)))
1319+
12751320static zend_always_inline zend_mm_free_slot * zend_mm_encode_free_slot (zend_mm_heap * heap , zend_mm_free_slot * slot )
12761321{
1277- return (zend_mm_free_slot * )((uintptr_t )slot ^ heap -> key );
1322+ #if WORDS_BIGENDIAN
1323+ return (zend_mm_free_slot * )(((uintptr_t )slot ) ^ heap -> shadow_key );
1324+ #else
1325+ return (zend_mm_free_slot * )(BSWAPPTR ((uintptr_t )slot ) ^ heap -> shadow_key );
1326+ #endif
12781327}
12791328
12801329static zend_always_inline zend_mm_free_slot * zend_mm_decode_free_slot (zend_mm_heap * heap , zend_mm_free_slot * slot )
12811330{
1282- return (zend_mm_free_slot * )((uintptr_t )slot ^ heap -> key );
1331+ #if WORDS_BIGENDIAN
1332+ return (zend_mm_free_slot * )((uintptr_t )slot ^ heap -> shadow_key ));
1333+ #else
1334+ return (zend_mm_free_slot * )(BSWAPPTR ((uintptr_t )slot ^ heap -> shadow_key ));
1335+ #endif
12831336}
12841337
12851338static 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 )
12861339{
12871340 slot -> next_free_slot = next ;
1288- * ( zend_mm_free_slot * * )(( char * ) slot + bin_shadow_offset [ bin_num ] ) = zend_mm_encode_free_slot (heap , next );
1341+ ZEND_MM_FREE_SLOT_PTR_SHADOW ( slot , bin_num ) = zend_mm_encode_free_slot (heap , next );
12891342}
12901343
12911344static zend_always_inline void zend_mm_copy_next_free_slot (zend_mm_free_slot * dest , uint32_t bin_num , zend_mm_free_slot * from )
12921345{
12931346 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" );
1347+ ZEND_MM_FREE_SLOT_PTR_SHADOW (dest , bin_num ) = ZEND_MM_FREE_SLOT_PTR_SHADOW (from , bin_num );
13001348}
13011349
13021350static 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 )
13031351{
13041352 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 ] );
1353+ zend_mm_free_slot * shadow = ZEND_MM_FREE_SLOT_PTR_SHADOW ( slot , bin_num );
13061354 if (EXPECTED (next != NULL )) {
13071355 if (UNEXPECTED (next != zend_mm_decode_free_slot (heap , shadow ))) {
1308- zend_mm_free_slot_corrupted ( );
1356+ zend_mm_panic ( "zend_mm_heap corrupted" );
13091357 }
13101358 }
13111359 return (zend_mm_free_slot * )next ;
@@ -1971,15 +2019,17 @@ static zend_result zend_mm_refresh_key(zend_mm_heap *heap)
19712019{
19722020 php_random_result result = php_random_algo_xoshiro256starstar .generate (& heap -> random_state );
19732021 ZEND_ASSERT (result .size == sizeof (uint64_t ));
1974- heap -> key = (uintptr_t ) result .result ;
2022+ heap -> shadow_key = (uintptr_t ) result .result ;
19752023 return SUCCESS ;
19762024}
19772025
19782026static zend_result zend_mm_init_key (zend_mm_heap * heap )
19792027{
19802028 uint64_t seed [4 ];
1981- if (php_random_bytes (& seed , sizeof (seed ), false) != SUCCESS ) {
1982- return FAILURE ;
2029+ if (php_random_bytes_silent (& seed , sizeof (seed )) != SUCCESS ) {
2030+ for (int i = 0 ; i < sizeof (seed )/sizeof (seed [0 ]); i ++ ) {
2031+ seed [i ] = php_random_generate_fallback_seed ();
2032+ }
19832033 }
19842034
19852035 php_random_xoshiro256starstar_seed256 (& heap -> random_state ,
0 commit comments