diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 0acbc98611966..db7f4fff9bb04 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -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); diff --git a/ext/spl/tests/gh15833_1.phpt b/ext/spl/tests/gh15833_1.phpt new file mode 100644 index 0000000000000..d658a770e80f5 --- /dev/null +++ b/ext/spl/tests/gh15833_1.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-15833 (Segmentation fault (access null pointer) in ext/spl/spl_array.c) +--CREDITS-- +YuanchengJiang +--FILE-- +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 diff --git a/ext/spl/tests/gh15833_2.phpt b/ext/spl/tests/gh15833_2.phpt new file mode 100644 index 0000000000000..8f30921741fe3 --- /dev/null +++ b/ext/spl/tests/gh15833_2.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-15833 (Segmentation fault (access null pointer) in ext/spl/spl_array.c) +--CREDITS-- +YuanchengJiang +--FILE-- +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