Skip to content

Commit

Permalink
Work around preloaded enums being broken on early PHP 8.4 versions wi…
Browse files Browse the repository at this point in the history
…th observers (#3093)

See also php/php-src#17715.

Relatively simple fix: just initialize the cache slots on every request. Has a slight per request-overhead, but avoids crashes when SomePreloadedEnum::cases().

Signed-off-by: Bob Weinand <[email protected]>
  • Loading branch information
bwoebi authored Feb 20, 2025
1 parent c5148ba commit 3a910f7
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ $(BUILD_DIR)/run-tests.php: $(if $(ASSUME_COMPILED),, $(BUILD_DIR)/configure)
sed -i 's/\bdl(/(bool)(/' $(BUILD_DIR)/run-tests.php # this dl() stuff in run-tests.php is for --EXTENSIONS-- sections, which we don't use; just strip it away (see https://github.com/php/php-src/issues/15367)

$(BUILD_DIR)/Makefile: $(BUILD_DIR)/configure
$(Q) (cd $(BUILD_DIR); ./configure --$(if $(RUST_DEBUG_BUILD),enable,disable)-ddtrace-rust-debug $(if $(ASAN), --enable-ddtrace-sanitize) $(EXTRA_CONFIGURE_OPTIONS))
$(Q) (cd $(BUILD_DIR); $(if $(ASAN),CFLAGS="${CFLAGS} -DZEND_TRACK_ARENA_ALLOC") ./configure --$(if $(RUST_DEBUG_BUILD),enable,disable)-ddtrace-rust-debug $(if $(ASAN), --enable-ddtrace-sanitize) $(EXTRA_CONFIGURE_OPTIONS))

$(SO_FILE): $(if $(ASSUME_COMPILED),, $(ALL_OBJECT_FILES) $(BUILD_DIR)/compile_rust.sh)
$(if $(ASSUME_COMPILED),,$(Q) $(MAKE) -C $(BUILD_DIR) -j)
Expand Down
2 changes: 1 addition & 1 deletion ext/ddtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1622,7 +1622,7 @@ static void dd_initialize_request(void) {
static PHP_RINIT_FUNCTION(ddtrace) {
UNUSED(module_number, type);

#if PHP_VERSION_ID < 80000
#if PHP_VERSION_ID < 80000 || (PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500)
zai_interceptor_rinit();
#endif

Expand Down
9 changes: 8 additions & 1 deletion ext/live_debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ static void clean_ctx(struct eval_ctx *ctx) {
zend_arena *arena = ctx->arena;
do {
zend_arena *prev = arena->prev;
for (zval *cur = (zval *)((char *)arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))); cur < (zval *)arena->ptr; ++cur) {
#ifdef ZEND_TRACK_ARENA_ALLOC
for (zval **ptr = (zval **)arena->ptrs; ptr < (zval **)arena->ptr; ++ptr)
{
zval *cur = *ptr;
#else
for (zval *cur = (zval *)((char *)arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))); cur < (zval *)arena->ptr; ++cur)
{
#endif
zval_ptr_dtor(cur);
}
arena = prev;
Expand Down
48 changes: 48 additions & 0 deletions zend_abstract_interface/interceptor/php8/interceptor.c
Original file line number Diff line number Diff line change
Expand Up @@ -913,12 +913,20 @@ void zai_interceptor_execute_internal_with_handler(INTERNAL_FUNCTION_PARAMETERS,
}
#endif

#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
zend_internal_function **preloaded_enum_functions = NULL;
#endif

void zai_interceptor_setup_resolving_post_startup(void);

// extension handles are supposed to be frozen at post_startup time and observer extension handle allocation
// incidentally is right before the defacto freeze via zend_finalize_system_id
static zend_result (*prev_post_startup)(void);
zend_result zai_interceptor_post_startup(void) {
#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
uint32_t classes = zend_hash_num_elements(CG(class_table));
#endif

zend_result result = prev_post_startup ? prev_post_startup() : SUCCESS; // first run opcache post_startup, then ours

zai_hook_post_startup();
Expand All @@ -931,6 +939,34 @@ zend_result zai_interceptor_post_startup(void) {
zai_interceptor_avoid_compile_opt = true; // Reset it in case MINIT gets re-executed
#endif

#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
if (classes != zend_hash_num_elements(CG(class_table))) {
// We suppose some preloading happened here
zend_class_entry *ce;
int num = 0;
ZEND_HASH_FOREACH_PTR(CG(class_table), ce) {
if ((ce->ce_flags & ZEND_ACC_ENUM) && ce->type != ZEND_INTERNAL_CLASS) {
num += ce->enum_backing_type == IS_UNDEF ? 1 : 3;
}
} ZEND_HASH_FOREACH_END();
if (num) {
preloaded_enum_functions = pemalloc(sizeof(zend_function *) * (num + 1), 1);
int idx = 0;
ZEND_HASH_FOREACH_PTR(CG(class_table), ce) {
if ((ce->ce_flags & ZEND_ACC_ENUM) && ce->type != ZEND_INTERNAL_CLASS) {
zend_function *function;
ZEND_HASH_FOREACH_PTR(&ce->function_table, function) {
if (!ZEND_USER_CODE(function->type)) {
preloaded_enum_functions[idx++] = &function->internal_function;
}
} ZEND_HASH_FOREACH_END();
}
} ZEND_HASH_FOREACH_END();
preloaded_enum_functions[idx] = NULL;
}
}
#endif

return result;
}

Expand Down Expand Up @@ -1008,6 +1044,18 @@ void zai_interceptor_activate(void) {
#endif
}

#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
void zai_interceptor_rinit(void) {
if (preloaded_enum_functions) {
zend_internal_function **zif = preloaded_enum_functions;
size_t cache_size = zend_internal_run_time_cache_reserved_size();
do {
ZEND_MAP_PTR_SET((*zif)->run_time_cache, zend_arena_calloc(&CG(arena), 1, cache_size));
} while (*++zif);
}
}
#endif

void zai_interceptor_deactivate(void) {
zend_hash_destroy(&zai_hook_memory);
zend_hash_destroy(&zai_interceptor_implicit_generators);
Expand Down
3 changes: 3 additions & 0 deletions zend_abstract_interface/interceptor/php8/interceptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ void zai_interceptor_startup(void);
void zai_interceptor_activate(void);
void zai_interceptor_deactivate(void);
void zai_interceptor_shutdown(void);
#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500
void zai_interceptor_rinit(void);
#endif

#endif // ZAI_INTERCEPTOR_H

0 comments on commit 3a910f7

Please sign in to comment.