From d29d3a4bf69f0122b40ebafa376e2f34b20002f6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 5 Jan 2021 12:43:19 +0100 Subject: Fix use-after-scope in SplObjectStorage::unserialize() Introduced by the recent switch to a zend_object. Unserialize the object into a tmp_var to avoid leaving behind a stack reference. Fixes oss-fuzz #29271. --- ext/spl/spl_observer.c | 26 ++++++----------- .../SplObjectStorage_object_reference.phpt | 33 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 ext/standard/tests/serialize/SplObjectStorage_object_reference.phpt diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index c83e8bf3a9..a32a84e45a 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -682,7 +682,6 @@ PHP_METHOD(SplObjectStorage, unserialize) size_t buf_len; const unsigned char *p, *s; php_unserialize_data_t var_hash; - zval entry, inf; zval *pcount, *pmembers; spl_SplObjectStorageElement *element; zend_long count; @@ -715,13 +714,12 @@ PHP_METHOD(SplObjectStorage, unserialize) goto outexcept; } - ZVAL_UNDEF(&entry); - ZVAL_UNDEF(&inf); - while (count-- > 0) { spl_SplObjectStorageElement *pelement; zend_hash_key key; - zval obj; + zval *entry = var_tmp_var(&var_hash); + zval inf; + ZVAL_UNDEF(&inf); if (*p != ';') { goto outexcept; @@ -731,46 +729,38 @@ PHP_METHOD(SplObjectStorage, unserialize) goto outexcept; } /* store reference to allow cross-references between different elements */ - if (!php_var_unserialize(&entry, &p, s + buf_len, &var_hash)) { - zval_ptr_dtor(&entry); + if (!php_var_unserialize(entry, &p, s + buf_len, &var_hash)) { goto outexcept; } if (*p == ',') { /* new version has inf */ ++p; if (!php_var_unserialize(&inf, &p, s + buf_len, &var_hash)) { - zval_ptr_dtor(&entry); zval_ptr_dtor(&inf); goto outexcept; } } - if (Z_TYPE(entry) != IS_OBJECT) { - zval_ptr_dtor(&entry); + if (Z_TYPE_P(entry) != IS_OBJECT) { zval_ptr_dtor(&inf); goto outexcept; } - if (spl_object_storage_get_hash(&key, intern, Z_OBJ(entry)) == FAILURE) { - zval_ptr_dtor(&entry); + if (spl_object_storage_get_hash(&key, intern, Z_OBJ_P(entry)) == FAILURE) { zval_ptr_dtor(&inf); goto outexcept; } pelement = spl_object_storage_get(intern, &key); spl_object_storage_free_hash(intern, &key); if (pelement) { + zval obj; if (!Z_ISUNDEF(pelement->inf)) { var_push_dtor(&var_hash, &pelement->inf); } ZVAL_OBJ(&obj, pelement->obj); var_push_dtor(&var_hash, &obj); } - element = spl_object_storage_attach(intern, Z_OBJ(entry), Z_ISUNDEF(inf)?NULL:&inf); - ZVAL_OBJ(&obj, element->obj); - var_replace(&var_hash, &entry, &obj); + element = spl_object_storage_attach(intern, Z_OBJ_P(entry), Z_ISUNDEF(inf)?NULL:&inf); var_replace(&var_hash, &inf, &element->inf); - zval_ptr_dtor(&entry); - ZVAL_UNDEF(&entry); zval_ptr_dtor(&inf); - ZVAL_UNDEF(&inf); } if (*p != ';') { diff --git a/ext/standard/tests/serialize/SplObjectStorage_object_reference.phpt b/ext/standard/tests/serialize/SplObjectStorage_object_reference.phpt new file mode 100644 index 0000000000..e09ba1ae38 --- /dev/null +++ b/ext/standard/tests/serialize/SplObjectStorage_object_reference.phpt @@ -0,0 +1,33 @@ +--TEST-- +Reference to SplObjectStorage key (not supported) +--FILE-- + +--EXPECTF-- +array(2) { + [0]=> + object(SplObjectStorage)#1 (1) { + ["storage":"SplObjectStorage":private]=> + array(1) { + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#2 (0) { + } + ["inf"]=> + NULL + } + } + } + [1]=> + object(stdClass)#2 (0) { + } +} -- cgit v1.2.1