Skip to content

Commit f0fcb59

Browse files
committed
Fix phpGH-18534: FPM exit code 70 with enabled opcache and hooked properties in traits
The trait handling for property hooks in preloading did not exist, we add a check to skip trait clones and we add the necessary code to update the op arrays.
1 parent 68abc19 commit f0fcb59

File tree

5 files changed

+91
-17
lines changed

5 files changed

+91
-17
lines changed

Zend/Optimizer/zend_optimizer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1579,7 +1579,7 @@ void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void
15791579
if (property->ce == ce && property->hooks) {
15801580
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
15811581
zend_function *hook = hooks[i];
1582-
if (hook && hook->common.scope == ce) {
1582+
if (hook && hook->common.scope == ce && !(hooks[i]->op_array.fn_flags & ZEND_ACC_TRAIT_CLONE)) {
15831583
zend_foreach_op_array_helper(&hooks[i]->op_array, func, context);
15841584
}
15851585
}

Zend/zend_inheritance.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2976,6 +2976,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
29762976
}
29772977
}
29782978
ce->ce_flags |= ZEND_ACC_USE_GUARDS;
2979+
ce->num_hooked_props++;
29792980
}
29802981
} ZEND_HASH_FOREACH_END();
29812982
}

ext/opcache/ZendAccelerator.c

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4202,36 +4202,75 @@ static void preload_remove_empty_includes(void)
42024202

42034203
static void preload_register_trait_methods(zend_class_entry *ce) {
42044204
zend_op_array *op_array;
4205+
zend_property_info *info;
4206+
42054207
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
42064208
if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
42074209
ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
42084210
zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
42094211
}
42104212
} ZEND_HASH_FOREACH_END();
4213+
4214+
if (ce->num_hooked_props > 0) {
4215+
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, info) {
4216+
if (info->hooks) {
4217+
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4218+
if (info->hooks[i]) {
4219+
op_array = &info->hooks[i]->op_array;
4220+
if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4221+
ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
4222+
zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
4223+
}
4224+
}
4225+
}
4226+
}
4227+
} ZEND_HASH_FOREACH_END();
4228+
}
4229+
}
4230+
4231+
static void preload_fix_trait_op_array(zend_op_array *op_array)
4232+
{
4233+
if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4234+
return;
4235+
}
4236+
4237+
zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->refcount);
4238+
ZEND_ASSERT(orig_op_array && "Must be in xlat table");
4239+
4240+
zend_string *function_name = op_array->function_name;
4241+
zend_class_entry *scope = op_array->scope;
4242+
uint32_t fn_flags = op_array->fn_flags;
4243+
zend_function *prototype = op_array->prototype;
4244+
HashTable *ht = op_array->static_variables;
4245+
*op_array = *orig_op_array;
4246+
op_array->function_name = function_name;
4247+
op_array->scope = scope;
4248+
op_array->fn_flags = fn_flags;
4249+
op_array->prototype = prototype;
4250+
op_array->static_variables = ht;
42114251
}
42124252

42134253
static void preload_fix_trait_methods(zend_class_entry *ce)
42144254
{
42154255
zend_op_array *op_array;
42164256

42174257
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4218-
if (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) {
4219-
zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->refcount);
4220-
ZEND_ASSERT(orig_op_array && "Must be in xlat table");
4221-
4222-
zend_string *function_name = op_array->function_name;
4223-
zend_class_entry *scope = op_array->scope;
4224-
uint32_t fn_flags = op_array->fn_flags;
4225-
zend_function *prototype = op_array->prototype;
4226-
HashTable *ht = op_array->static_variables;
4227-
*op_array = *orig_op_array;
4228-
op_array->function_name = function_name;
4229-
op_array->scope = scope;
4230-
op_array->fn_flags = fn_flags;
4231-
op_array->prototype = prototype;
4232-
op_array->static_variables = ht;
4233-
}
4258+
preload_fix_trait_op_array(op_array);
42344259
} ZEND_HASH_FOREACH_END();
4260+
4261+
if (ce->num_hooked_props > 0) {
4262+
zend_property_info *info;
4263+
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, info) {
4264+
if (info->hooks) {
4265+
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4266+
if (info->hooks[i]) {
4267+
op_array = &info->hooks[i]->op_array;
4268+
preload_fix_trait_op_array(op_array);
4269+
}
4270+
}
4271+
}
4272+
} ZEND_HASH_FOREACH_END();
4273+
}
42354274
}
42364275

42374276
static void preload_optimize(zend_persistent_script *script)

ext/opcache/tests/gh18534.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
GH-18534 (FPM exit code 70 with enabled opcache and hooked properties in traits)
3+
--EXTENSIONS--
4+
opcache
5+
--SKIPIF--
6+
<?php if (PHP_OS_FAMILY === 'Windows') die('skip preloading does not work on Windows'); ?>
7+
--INI--
8+
opcache.enable=1
9+
opcache.enable_cli=1
10+
opcache.preload={PWD}/gh18534_preload.inc
11+
--FILE--
12+
<?php
13+
14+
require_once __DIR__ . '/gh18534_preload.inc';
15+
16+
$test = new DummyModel;
17+
var_dump($test->dummyProperty2);
18+
19+
?>
20+
--EXPECT--
21+
NULL

ext/opcache/tests/gh18534_preload.inc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
trait DummyTrait
4+
{
5+
public ?string $dummyProperty2 {
6+
get => null;
7+
}
8+
}
9+
10+
class DummyModel
11+
{
12+
use DummyTrait;
13+
}

0 commit comments

Comments
 (0)