Skip to content

Commit b666dc9

Browse files
committed
Fix GH-15833: Segmentation fault (access null pointer) in ext/spl/spl_array.c
We're accessing the object properties table directly in spl, but we're not accounting for lazy objects. Upon accessing we should trigger the initialization as spl is doing direct manipulations on the object property table and expects a real object. Closes GH-17235.
1 parent 3eb79e1 commit b666dc9

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ PHP NEWS
4646
. Fixed bug GH-17330 (SNMP::setSecurity segfault on closed session).
4747
(David Carlier)
4848

49+
- SPL:
50+
. Fixed bug GH-15833 (Segmentation fault (access null pointer) in
51+
ext/spl/spl_array.c). (nielsdos)
52+
4953
02 Jan 2025, PHP 8.4.3
5054

5155
- BcMath:

ext/spl/spl_array.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ PHPAPI zend_class_entry *spl_ce_ArrayObject;
4040

4141
typedef struct _spl_array_object {
4242
zval array;
43+
HashTable *sentinel_array;
4344
uint32_t ht_iter;
4445
int ar_flags;
4546
unsigned char nApplyCount;
@@ -74,6 +75,19 @@ static inline HashTable **spl_array_get_hash_table_ptr(spl_array_object* intern)
7475
return &Z_ARRVAL(intern->array);
7576
} else {
7677
zend_object *obj = Z_OBJ(intern->array);
78+
/* Since we're directly playing with the properties table, we shall initialize the lazy object directly.
79+
* If we don't, it's possible to continue working with the wrong object in case we're using a proxy. */
80+
if (UNEXPECTED(zend_lazy_object_must_init(obj))) {
81+
obj = zend_lazy_object_init(obj);
82+
if (UNEXPECTED(!obj)) {
83+
if (!intern->sentinel_array) {
84+
intern->sentinel_array = zend_new_array(0);
85+
}
86+
return &intern->sentinel_array;
87+
}
88+
}
89+
/* should no longer be lazy */
90+
ZEND_ASSERT(!zend_lazy_object_must_init(obj));
7791
/* rebuild properties */
7892
zend_std_get_properties_ex(obj);
7993
if (GC_REFCOUNT(obj->properties) > 1) {
@@ -129,6 +143,10 @@ static void spl_array_object_free_storage(zend_object *object)
129143
zend_hash_iterator_del(intern->ht_iter);
130144
}
131145

146+
if (UNEXPECTED(intern->sentinel_array)) {
147+
zend_array_release(intern->sentinel_array);
148+
}
149+
132150
zend_object_std_dtor(&intern->std);
133151

134152
zval_ptr_dtor(&intern->array);
@@ -489,6 +507,9 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
489507
uint32_t refcount = 0;
490508
if (!offset || Z_TYPE_P(offset) == IS_NULL) {
491509
ht = spl_array_get_hash_table(intern);
510+
if (UNEXPECTED(ht == intern->sentinel_array)) {
511+
return;
512+
}
492513
refcount = spl_array_set_refcount(intern->is_child, ht, 1);
493514
zend_hash_next_index_insert(ht, value);
494515

@@ -505,6 +526,10 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
505526
}
506527

507528
ht = spl_array_get_hash_table(intern);
529+
if (UNEXPECTED(ht == intern->sentinel_array)) {
530+
spl_hash_key_release(&key);
531+
return;
532+
}
508533
refcount = spl_array_set_refcount(intern->is_child, ht, 1);
509534
if (key.key) {
510535
zend_hash_update_ind(ht, key.key, value);

ext/spl/tests/gh15833_1.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
GH-15833 (Segmentation fault (access null pointer) in ext/spl/spl_array.c)
3+
--CREDITS--
4+
YuanchengJiang
5+
--FILE--
6+
<?php
7+
class C {
8+
public int $a = 1;
9+
}
10+
$reflector = new ReflectionClass(C::class);
11+
$obj = $reflector->newLazyProxy(function ($obj) {
12+
$obj = new C();
13+
return $obj;
14+
});
15+
$recursiveArrayIterator = new RecursiveArrayIterator($obj);
16+
var_dump($recursiveArrayIterator->current());
17+
$recursiveArrayIterator->next();
18+
var_dump($recursiveArrayIterator->current());
19+
?>
20+
--EXPECT--
21+
int(1)
22+
NULL

ext/spl/tests/gh15833_2.phpt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
GH-15833 (Segmentation fault (access null pointer) in ext/spl/spl_array.c)
3+
--CREDITS--
4+
YuanchengJiang
5+
--FILE--
6+
<?php
7+
class C {
8+
public int $a = 1;
9+
}
10+
$reflector = new ReflectionClass(C::class);
11+
$obj = $reflector->newLazyProxy(function ($obj) {
12+
static $counter = 0;
13+
throw new Error('nope ' . ($counter++));
14+
});
15+
$recursiveArrayIterator = new RecursiveArrayIterator($obj);
16+
try {
17+
var_dump($recursiveArrayIterator->current());
18+
} catch (Error $e) {
19+
echo $e->getMessage(), "\n";
20+
}
21+
try {
22+
var_dump($recursiveArrayIterator->current());
23+
} catch (Error $e) {
24+
echo $e->getMessage(), "\n";
25+
}
26+
try {
27+
$recursiveArrayIterator->next();
28+
} catch (Error $e) {
29+
echo $e->getMessage(), "\n";
30+
}
31+
try {
32+
var_dump($recursiveArrayIterator->current());
33+
} catch (Error $e) {
34+
echo $e->getMessage(), "\n";
35+
}
36+
?>
37+
--EXPECT--
38+
nope 0
39+
nope 1
40+
nope 2
41+
nope 3

0 commit comments

Comments
 (0)