diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2021-01-05 12:43:19 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2021-01-05 12:44:17 +0100 |
commit | d29d3a4bf69f0122b40ebafa376e2f34b20002f6 (patch) | |
tree | 67954220630edf2eba523fee572109d94134868e | |
parent | 0067c3ced17ff69516854865e607887a9702cd0c (diff) | |
download | php-git-d29d3a4bf69f0122b40ebafa376e2f34b20002f6.tar.gz |
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.
-rw-r--r-- | ext/spl/spl_observer.c | 26 | ||||
-rw-r--r-- | ext/standard/tests/serialize/SplObjectStorage_object_reference.phpt | 33 |
2 files changed, 41 insertions, 18 deletions
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-- +<?php + +$inner = 'x:i:1;O:8:"stdClass":0:{};m:a:0:{}'; +$inner_len = strlen($inner); +$str = <<<STR +a:2:{i:0;C:16:"SPlObjectStorage":{$inner_len}:{{$inner}}i:1;R:4;} +STR; +var_dump(unserialize($str)); + +?> +--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) { + } +} |