summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2021-01-05 12:43:19 +0100
committerNikita Popov <nikita.ppv@gmail.com>2021-01-05 12:44:17 +0100
commitd29d3a4bf69f0122b40ebafa376e2f34b20002f6 (patch)
tree67954220630edf2eba523fee572109d94134868e
parent0067c3ced17ff69516854865e607887a9702cd0c (diff)
downloadphp-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.c26
-rw-r--r--ext/standard/tests/serialize/SplObjectStorage_object_reference.phpt33
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) {
+ }
+}