summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2021-02-15 14:52:38 +0100
committerNikita Popov <nikita.ppv@gmail.com>2021-02-15 14:58:38 +0100
commit7b7d99839c2e2886ecf159952552c9964bd80481 (patch)
tree23479c2d0817b804f665f02ebb265d4a6a915cc6
parent3646604203d80bc0f6a124aa2ac5c448229327ea (diff)
downloadphp-git-7b7d99839c2e2886ecf159952552c9964bd80481.tar.gz
Fix symtable cache being used while cleaning symtable
We need to first clean the symtable and then check whether a cache slot is available for it. Otherwise, it may happen that a destructor runs while cleaning the table and uses up all the remaining slots in the cache. This is particularly insidious because once we overflow the cache, the first pointer we modify is symtable_cache_ptr, making it hard to understand what happened after the fact. Fixes oss-fuzz #30815.
-rw-r--r--Zend/tests/symtable_cache_recursive_dtor.phpt19
-rw-r--r--Zend/zend_execute.c7
2 files changed, 23 insertions, 3 deletions
diff --git a/Zend/tests/symtable_cache_recursive_dtor.phpt b/Zend/tests/symtable_cache_recursive_dtor.phpt
new file mode 100644
index 0000000000..def0816a66
--- /dev/null
+++ b/Zend/tests/symtable_cache_recursive_dtor.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Symtable cache slots may be acquired while cleaning symtable
+--FILE--
+<?php
+class A {
+ // Must be larger than the symtable cache.
+ static $max = 40;
+ function __destruct() {
+ if (self::$max-- < 0) return;
+ $x = 'y';
+ $$x = new a;
+ }
+}
+new A;
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index b6839bb88b..f58b1131ba 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -3437,12 +3437,13 @@ ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_val
ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table) /* {{{ */
{
+ /* Clean before putting into the cache, since clean could call dtors,
+ * which could use the cached hash. Also do this before the check for
+ * available cache slots, as those may be used by a dtor as well. */
+ zend_symtable_clean(symbol_table);
if (EG(symtable_cache_ptr) >= EG(symtable_cache_limit)) {
zend_array_destroy(symbol_table);
} else {
- /* clean before putting into the cache, since clean
- could call dtors, which could use cached hash */
- zend_symtable_clean(symbol_table);
*(EG(symtable_cache_ptr)++) = symbol_table;
}
}