Skip to content

Commit 6b795f6

Browse files
committed
Fix GH-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. Closes GH-18586.
1 parent 43915b3 commit 6b795f6

File tree

6 files changed

+93
-17
lines changed

6 files changed

+93
-17
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ PHP NEWS
4141
(Arnaud)
4242
. Fixed bug GH-18567 (Preloading with internal class alias triggers assertion
4343
failure). (nielsdos)
44+
. Fixed bug GH-18534 (FPM exit code 70 with enabled opcache and hooked
45+
properties in traits). (nielsdos)
4446

4547
- SPL:
4648
. Fixed bug GH-18421 (Integer overflow with large numbers in LimitIterator).

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
@@ -4227,36 +4227,75 @@ static void preload_remove_empty_includes(void)
42274227

42284228
static void preload_register_trait_methods(zend_class_entry *ce) {
42294229
zend_op_array *op_array;
4230+
zend_property_info *info;
4231+
42304232
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
42314233
if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
42324234
ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
42334235
zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
42344236
}
42354237
} ZEND_HASH_FOREACH_END();
4238+
4239+
if (ce->num_hooked_props > 0) {
4240+
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, info) {
4241+
if (info->hooks) {
4242+
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4243+
if (info->hooks[i]) {
4244+
op_array = &info->hooks[i]->op_array;
4245+
if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4246+
ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
4247+
zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
4248+
}
4249+
}
4250+
}
4251+
}
4252+
} ZEND_HASH_FOREACH_END();
4253+
}
4254+
}
4255+
4256+
static void preload_fix_trait_op_array(zend_op_array *op_array)
4257+
{
4258+
if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4259+
return;
4260+
}
4261+
4262+
zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->refcount);
4263+
ZEND_ASSERT(orig_op_array && "Must be in xlat table");
4264+
4265+
zend_string *function_name = op_array->function_name;
4266+
zend_class_entry *scope = op_array->scope;
4267+
uint32_t fn_flags = op_array->fn_flags;
4268+
zend_function *prototype = op_array->prototype;
4269+
HashTable *ht = op_array->static_variables;
4270+
*op_array = *orig_op_array;
4271+
op_array->function_name = function_name;
4272+
op_array->scope = scope;
4273+
op_array->fn_flags = fn_flags;
4274+
op_array->prototype = prototype;
4275+
op_array->static_variables = ht;
42364276
}
42374277

42384278
static void preload_fix_trait_methods(zend_class_entry *ce)
42394279
{
42404280
zend_op_array *op_array;
42414281

42424282
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4243-
if (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) {
4244-
zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->refcount);
4245-
ZEND_ASSERT(orig_op_array && "Must be in xlat table");
4246-
4247-
zend_string *function_name = op_array->function_name;
4248-
zend_class_entry *scope = op_array->scope;
4249-
uint32_t fn_flags = op_array->fn_flags;
4250-
zend_function *prototype = op_array->prototype;
4251-
HashTable *ht = op_array->static_variables;
4252-
*op_array = *orig_op_array;
4253-
op_array->function_name = function_name;
4254-
op_array->scope = scope;
4255-
op_array->fn_flags = fn_flags;
4256-
op_array->prototype = prototype;
4257-
op_array->static_variables = ht;
4258-
}
4283+
preload_fix_trait_op_array(op_array);
42594284
} ZEND_HASH_FOREACH_END();
4285+
4286+
if (ce->num_hooked_props > 0) {
4287+
zend_property_info *info;
4288+
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, info) {
4289+
if (info->hooks) {
4290+
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4291+
if (info->hooks[i]) {
4292+
op_array = &info->hooks[i]->op_array;
4293+
preload_fix_trait_op_array(op_array);
4294+
}
4295+
}
4296+
}
4297+
} ZEND_HASH_FOREACH_END();
4298+
}
42604299
}
42614300

42624301
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)