Skip to content

Commit 63decfd

Browse files
committed
fix: test_zend_string_create and extract test_zend_string_alloc
1 parent 1477bd5 commit 63decfd

File tree

1 file changed

+44
-27
lines changed

1 file changed

+44
-27
lines changed

profiling/src/php_ffi.c

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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)
394394
static 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+
428455
static 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

Comments
 (0)