diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2021-02-15 14:52:38 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2021-02-15 14:58:38 +0100 |
commit | 7b7d99839c2e2886ecf159952552c9964bd80481 (patch) | |
tree | 23479c2d0817b804f665f02ebb265d4a6a915cc6 | |
parent | 3646604203d80bc0f6a124aa2ac5c448229327ea (diff) | |
download | php-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.phpt | 19 | ||||
-rw-r--r-- | Zend/zend_execute.c | 7 |
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; } } |