1
0
mirror of https://github.com/php/php-src.git synced 2026-03-26 01:02:25 +01:00

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.
This commit is contained in:
Niels Dossche
2024-12-21 19:05:18 +01:00
parent 3eb79e146f
commit b666dc9788
4 changed files with 92 additions and 0 deletions

4
NEWS
View File

@@ -46,6 +46,10 @@ PHP NEWS
. Fixed bug GH-17330 (SNMP::setSecurity segfault on closed session).
(David Carlier)
- SPL:
. Fixed bug GH-15833 (Segmentation fault (access null pointer) in
ext/spl/spl_array.c). (nielsdos)
02 Jan 2025, PHP 8.4.3
- BcMath:

View File

@@ -40,6 +40,7 @@ PHPAPI zend_class_entry *spl_ce_ArrayObject;
typedef struct _spl_array_object {
zval array;
HashTable *sentinel_array;
uint32_t ht_iter;
int ar_flags;
unsigned char nApplyCount;
@@ -74,6 +75,19 @@ static inline HashTable **spl_array_get_hash_table_ptr(spl_array_object* intern)
return &Z_ARRVAL(intern->array);
} else {
zend_object *obj = Z_OBJ(intern->array);
/* Since we're directly playing with the properties table, we shall initialize the lazy object directly.
* If we don't, it's possible to continue working with the wrong object in case we're using a proxy. */
if (UNEXPECTED(zend_lazy_object_must_init(obj))) {
obj = zend_lazy_object_init(obj);
if (UNEXPECTED(!obj)) {
if (!intern->sentinel_array) {
intern->sentinel_array = zend_new_array(0);
}
return &intern->sentinel_array;
}
}
/* should no longer be lazy */
ZEND_ASSERT(!zend_lazy_object_must_init(obj));
/* rebuild properties */
zend_std_get_properties_ex(obj);
if (GC_REFCOUNT(obj->properties) > 1) {
@@ -129,6 +143,10 @@ static void spl_array_object_free_storage(zend_object *object)
zend_hash_iterator_del(intern->ht_iter);
}
if (UNEXPECTED(intern->sentinel_array)) {
zend_array_release(intern->sentinel_array);
}
zend_object_std_dtor(&intern->std);
zval_ptr_dtor(&intern->array);
@@ -489,6 +507,9 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
uint32_t refcount = 0;
if (!offset || Z_TYPE_P(offset) == IS_NULL) {
ht = spl_array_get_hash_table(intern);
if (UNEXPECTED(ht == intern->sentinel_array)) {
return;
}
refcount = spl_array_set_refcount(intern->is_child, ht, 1);
zend_hash_next_index_insert(ht, value);
@@ -505,6 +526,10 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
}
ht = spl_array_get_hash_table(intern);
if (UNEXPECTED(ht == intern->sentinel_array)) {
spl_hash_key_release(&key);
return;
}
refcount = spl_array_set_refcount(intern->is_child, ht, 1);
if (key.key) {
zend_hash_update_ind(ht, key.key, value);

View File

@@ -0,0 +1,22 @@
--TEST--
GH-15833 (Segmentation fault (access null pointer) in ext/spl/spl_array.c)
--CREDITS--
YuanchengJiang
--FILE--
<?php
class C {
public int $a = 1;
}
$reflector = new ReflectionClass(C::class);
$obj = $reflector->newLazyProxy(function ($obj) {
$obj = new C();
return $obj;
});
$recursiveArrayIterator = new RecursiveArrayIterator($obj);
var_dump($recursiveArrayIterator->current());
$recursiveArrayIterator->next();
var_dump($recursiveArrayIterator->current());
?>
--EXPECT--
int(1)
NULL

View File

@@ -0,0 +1,41 @@
--TEST--
GH-15833 (Segmentation fault (access null pointer) in ext/spl/spl_array.c)
--CREDITS--
YuanchengJiang
--FILE--
<?php
class C {
public int $a = 1;
}
$reflector = new ReflectionClass(C::class);
$obj = $reflector->newLazyProxy(function ($obj) {
static $counter = 0;
throw new Error('nope ' . ($counter++));
});
$recursiveArrayIterator = new RecursiveArrayIterator($obj);
try {
var_dump($recursiveArrayIterator->current());
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump($recursiveArrayIterator->current());
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$recursiveArrayIterator->next();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump($recursiveArrayIterator->current());
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
nope 0
nope 1
nope 2
nope 3