@@ -393,14 +393,28 @@ uintptr_t *ddog_test_php_prof_function_run_time_cache(zend_function const *func)
393393#if CFG_STACK_WALKING_TESTS || defined(CFG_TEST )
394394static int (* og_snprintf )(char * , size_t , const char * , ...);
395395
396- // Manually create a zend_string without using zend_string_init(), since we
397- // do not link against PHP at test time
398- static zend_string * test_zend_string_create (const char * str , size_t len ) {
399- // zend_string has a flexible array member val[1], so we allocate
400- // sizeof(zend_string) - 1 (for the val[1]) + len + 1 (for null terminator)
401- zend_string * zs = calloc (1 , sizeof (zend_string ) + len );
396+ static ZEND_COLD ZEND_NORETURN void out_of_memory (void ) {
397+ fprintf (stderr , "Out of memory\n" );
398+ exit (1 );
399+ }
400+
401+ static zend_string * test_zend_string_alloc (size_t len ) {
402+ // We don't need to handle large strings.
403+ if (len > INT32_MAX ) {
404+ out_of_memory ();
405+ }
406+
407+ // zend_string logically has a flexible array member val, but it uses the
408+ // so-called "struct hack" instead. So we calculate the number of bytes
409+ // needed to get to `val`, then add the `len` plus 1 (for a null byte).
410+ uint32_t alloc_len = offsetof(zend_string , val ) + len + 1 ;
411+
412+ // Need to round up to 8 bytes on 64-bit platforms, won't overflow because
413+ // of above large string check.
414+ uint32_t rounded = (alloc_len + UINT32_C (7 )) & ~UINT32_C (7 );
415+ zend_string * zs = calloc (rounded , 1 );
402416 if (!zs ) {
403- return NULL ;
417+ out_of_memory () ;
404418 }
405419
406420 // Initialize the refcounted header
@@ -409,22 +423,35 @@ static zend_string *test_zend_string_create(const char *str, size_t len) {
409423#else
410424 GC_SET_REFCOUNT (zs , 1 );
411425#endif
412- #if PHP_VERSION_ID < 70499
413- GC_TYPE_INFO (zs ) = IS_STRING ;
414- #else
415- GC_TYPE_INFO (zs ) = GC_STRING ;
426+
427+ #if PHP_VERSION_ID < 70200
428+ #undef GC_FLAGS_SHIFT
429+ #define GC_FLAGS_SHIFT 8
430+ #endif
431+
432+ #if PHP_VERSION_ID < 80000
433+ #undef GC_STRING
434+ #define GC_STRING IS_STRING
416435#endif
417436
437+ // IS_STR_PERSISTENT means it's allocated with malloc, not emalloc.
438+ GC_TYPE_INFO (zs ) = GC_STRING | (IS_STR_PERSISTENT << GC_FLAGS_SHIFT );
439+
418440 zs -> h = 0 ;
419441 zs -> len = len ;
420- if (len > 0 ) {
421- memcpy (ZSTR_VAL (zs ), str , len );
422- }
423442 ZSTR_VAL (zs )[len ] = '\0' ;
424443
425444 return zs ;
426445}
427446
447+ // Manually create a zend_string without using zend_string_init(), since we
448+ // do not link against PHP at test time
449+ static zend_string * test_zend_string_create (const char * str , size_t len ) {
450+ zend_string * zstr = test_zend_string_alloc (len );
451+ memcpy (ZSTR_VAL (zstr ), str , len );
452+ return zstr ;
453+ }
454+
428455static zend_execute_data * create_fake_frame (int depth ) {
429456 zend_execute_data * execute_data = calloc (1 , sizeof (zend_execute_data ));
430457 zend_op_array * op_array = calloc (1 , sizeof (zend_function ));
@@ -493,19 +520,9 @@ zend_function *ddog_php_test_create_fake_zend_function_with_name_len(size_t len)
493520 op_array -> type = ZEND_USER_FUNCTION ;
494521
495522 if (len > 0 ) {
496- char * buffer = malloc (len + 1 );
497- if (!buffer ) {
498- free (op_array );
499- return NULL ;
500- }
501- memset (buffer , 'x' , len );
502- buffer [len ] = '\0' ;
503- op_array -> function_name = test_zend_string_create (buffer , len );
504- free (buffer );
505- if (!op_array -> function_name ) {
506- free (op_array );
507- return NULL ;
508- }
523+ zend_string * zstr = test_zend_string_alloc (len );
524+ memset (ZSTR_VAL (zstr ), 'x' , len );
525+ op_array -> function_name = zstr ;
509526 }
510527
511528 return (zend_function * )op_array ;
0 commit comments