From ee000ea186fcc312411a42df3832ca32057b0ad9 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 8 Aug 2023 11:33:51 +0200 Subject: [PATCH] Fix uouv on oom on object allocation We may OOM during object initialization. In this case, free_obj needs to guard against NULL values. There may be more cases where this is an issue, these were the ones I was able to discover via script. Fixes GH-11734 --- NEWS | 2 ++ Zend/tests/new_oom.inc | 15 +++++++++++++++ Zend/tests/new_oom.phpt | 24 ++++++++++++++++++++++++ ext/pdo/pdo_dbh.c | 6 ++++++ ext/spl/spl_dllist.c | 11 ++++++----- ext/spl/spl_heap.c | 5 +++++ ext/xsl/php_xsl.c | 12 ++++++++---- 7 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/new_oom.inc create mode 100644 Zend/tests/new_oom.phpt diff --git a/NEWS b/NEWS index 1d1eedd249a..2018d967812 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ PHP NEWS - Core: . Fixed strerror_r detection at configuration time. (Kévin Dunglas) + . Fixed segfault during freeing of some incompletely initialized objects due + to OOM error (PDO, SPL, XSL). (ilutov) - DOM: . adoptNode now respects the strict error checking property. (nielsdos) diff --git a/Zend/tests/new_oom.inc b/Zend/tests/new_oom.inc new file mode 100644 index 00000000000..fa62764310a --- /dev/null +++ b/Zend/tests/new_oom.inc @@ -0,0 +1,15 @@ +newInstanceWithoutConstructor(); + } +} catch (Throwable) { +} diff --git a/Zend/tests/new_oom.phpt b/Zend/tests/new_oom.phpt new file mode 100644 index 00000000000..1e9b0593e6b --- /dev/null +++ b/Zend/tests/new_oom.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test OOM on new of each class +--SKIPIF-- + +--FILE-- +&1"); + if ($output && preg_match('(^\nFatal error: Allowed memory size of [0-9]+ bytes exhausted[^\r\n]* \(tried to allocate [0-9]+ bytes\) in [^\r\n]+ on line [0-9]+$)', $output) !== 1) { + echo "Class $class failed\n"; + echo $output, "\n"; + } +} + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 833afa0d0f5..ce9c7967e72 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1414,6 +1414,12 @@ static void dbh_free(pdo_dbh_t *dbh, bool free_persistent) static void pdo_dbh_free_storage(zend_object *std) { pdo_dbh_t *dbh = php_pdo_dbh_fetch_inner(std); + + /* dbh might be null if we OOMed during object initialization. */ + if (!dbh) { + return; + } + if (dbh->driver_data && dbh->methods && dbh->methods->rollback && pdo_is_in_transaction(dbh)) { dbh->methods->rollback(dbh); dbh->in_txn = false; diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 74dc7731fd1..74dc3b8c99f 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -295,12 +295,13 @@ static void spl_dllist_object_free_storage(zend_object *object) /* {{{ */ zend_object_std_dtor(&intern->std); - while (intern->llist->count > 0) { - spl_ptr_llist_pop(intern->llist, &tmp); - zval_ptr_dtor(&tmp); + if (intern->llist) { + while (intern->llist->count > 0) { + spl_ptr_llist_pop(intern->llist, &tmp); + zval_ptr_dtor(&tmp); + } + spl_ptr_llist_destroy(intern->llist); } - - spl_ptr_llist_destroy(intern->llist); SPL_LLIST_CHECK_DELREF(intern->traverse_pointer); } /* }}} */ diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 4f242d3a3c3..5d617414251 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -372,6 +372,11 @@ static spl_ptr_heap *spl_ptr_heap_clone(spl_ptr_heap *from) { /* {{{ */ /* }}} */ static void spl_ptr_heap_destroy(spl_ptr_heap *heap) { /* {{{ */ + /* Heap might be null if we OOMed during object initialization. */ + if (!heap) { + return; + } + int i; for (i = 0; i < heap->count; ++i) { diff --git a/ext/xsl/php_xsl.c b/ext/xsl/php_xsl.c index 82e782d18f9..5ecb7fd9b0b 100644 --- a/ext/xsl/php_xsl.c +++ b/ext/xsl/php_xsl.c @@ -59,11 +59,15 @@ void xsl_objects_free_storage(zend_object *object) zend_object_std_dtor(&intern->std); - zend_hash_destroy(intern->parameter); - FREE_HASHTABLE(intern->parameter); + if (intern->parameter) { + zend_hash_destroy(intern->parameter); + FREE_HASHTABLE(intern->parameter); + } - zend_hash_destroy(intern->registered_phpfunctions); - FREE_HASHTABLE(intern->registered_phpfunctions); + if (intern->registered_phpfunctions) { + zend_hash_destroy(intern->registered_phpfunctions); + FREE_HASHTABLE(intern->registered_phpfunctions); + } if (intern->node_list) { zend_hash_destroy(intern->node_list);