Skip to content

Commit 81038fa

Browse files
Fix GH-19548: Add defensive copy-on-write for hooks inheritance when CE is part of opcache shared memory
1 parent 4a1789c commit 81038fa

File tree

3 files changed

+98
-0
lines changed

3 files changed

+98
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
GH-19548: SEGV do_inherit_property zend_inheritance.c with opcache.protect_memory=1
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
opcache.protect_memory=1
8+
--FILE--
9+
<?php
10+
11+
interface I {
12+
public mixed $i { get; }
13+
}
14+
class P {
15+
public mixed $i;
16+
}
17+
18+
class C extends P implements I {}
19+
20+
echo "Test passed - no segmentation fault\n";
21+
22+
?>
23+
--EXPECT--
24+
Test passed - no segmentation fault
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
GH-19548: SEGV do_inherit_property zend_inheritance.c with opcache.protect_memory=1 (multiple properties)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
opcache.protect_memory=1
8+
--FILE--
9+
<?php
10+
11+
interface I1 {
12+
public mixed $a { get; }
13+
public mixed $b { get; }
14+
}
15+
class P1 {
16+
public mixed $a;
17+
public mixed $b;
18+
}
19+
class C1 extends P1 implements I1 {}
20+
21+
interface I2 {
22+
public mixed $prop { get; }
23+
}
24+
class P2 {
25+
public mixed $prop;
26+
}
27+
class Q2 extends P2 {}
28+
class C2 extends Q2 implements I2 {}
29+
30+
echo "Multiple property test passed - no segmentation fault\n";
31+
32+
?>
33+
--EXPECT--
34+
Multiple property test passed - no segmentation fault

Zend/zend_inheritance.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,16 @@ static void inherit_property_hook(
13821382
if (child
13831383
&& (child->common.fn_flags & ZEND_ACC_OVERRIDE)
13841384
&& property_has_operation(parent_info, kind)) {
1385+
/* Duplicate child function if it comes from opcache-protected memory */
1386+
if (child->common.scope != ce) {
1387+
child = zend_duplicate_function(child, ce);
1388+
if (!child_info->hooks) {
1389+
ce->num_hooked_props++;
1390+
child_info->hooks = zend_arena_alloc(&CG(arena), ZEND_PROPERTY_HOOK_STRUCT_SIZE);
1391+
memset(child_info->hooks, 0, ZEND_PROPERTY_HOOK_STRUCT_SIZE);
1392+
}
1393+
child_info->hooks[kind] = child;
1394+
}
13851395
child->common.fn_flags &= ~ZEND_ACC_OVERRIDE;
13861396
}
13871397

@@ -1406,6 +1416,17 @@ static void inherit_property_hook(
14061416
return;
14071417
}
14081418

1419+
/* Duplicate child function if it comes from opcache-protected memory */
1420+
if (child->common.scope != ce) {
1421+
child = zend_duplicate_function(child, ce);
1422+
if (!child_info->hooks) {
1423+
ce->num_hooked_props++;
1424+
child_info->hooks = zend_arena_alloc(&CG(arena), ZEND_PROPERTY_HOOK_STRUCT_SIZE);
1425+
memset(child_info->hooks, 0, ZEND_PROPERTY_HOOK_STRUCT_SIZE);
1426+
}
1427+
child_info->hooks[kind] = child;
1428+
}
1429+
14091430
child->common.prototype = parent->common.prototype ? parent->common.prototype : parent;
14101431

14111432
uint32_t parent_flags = parent->common.fn_flags;
@@ -1543,6 +1564,25 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
15431564
child_info->flags &= ~ZEND_ACC_VIRTUAL;
15441565
}
15451566

1567+
/* Create a copy if this property comes from opcache-protected memory */
1568+
if (child_info->ce != ce) {
1569+
zend_property_info *new_prop_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info));
1570+
memcpy(new_prop_info, child_info, sizeof(zend_property_info));
1571+
new_prop_info->ce = ce;
1572+
/* Deep copy the type information */
1573+
zend_type_copy_ctor(&new_prop_info->type, /* use_arena */ true, /* persistent */ false);
1574+
if (new_prop_info->hooks) {
1575+
new_prop_info->hooks = zend_arena_alloc(&CG(arena), ZEND_PROPERTY_HOOK_STRUCT_SIZE);
1576+
memcpy(new_prop_info->hooks, child_info->hooks, ZEND_PROPERTY_HOOK_STRUCT_SIZE);
1577+
}
1578+
/* Re-fetch and update the child entry in the hash table */
1579+
child = zend_hash_find_known_hash(&ce->properties_info, key);
1580+
if (EXPECTED(child)) {
1581+
Z_PTR_P(child) = new_prop_info;
1582+
child_info = new_prop_info;
1583+
}
1584+
}
1585+
15461586
if (parent_info->hooks || child_info->hooks) {
15471587
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
15481588
inherit_property_hook(ce, parent_info, child_info, i);

0 commit comments

Comments
 (0)