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
This commit is contained in:
Ilija Tovilo 2023-08-08 11:33:51 +02:00
parent 2196e2299f
commit ee000ea186
No known key found for this signature in database
GPG Key ID: A4F5D403F118200A
7 changed files with 66 additions and 9 deletions

2
NEWS
View File

@ -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)

15
Zend/tests/new_oom.inc Normal file
View File

@ -0,0 +1,15 @@
<?php
$mb_used = (int) ceil(memory_get_usage() / (1024 ** 2));
ini_set('memory_limit', ($mb_used + 1) . 'M');
$class = $argv[1];
$objects = [];
try {
while (true) {
$rc = new ReflectionClass($class);
$objects[] = $rc->newInstanceWithoutConstructor();
}
} catch (Throwable) {
}

24
Zend/tests/new_oom.phpt Normal file
View File

@ -0,0 +1,24 @@
--TEST--
Test OOM on new of each class
--SKIPIF--
<?php
if (getenv("SKIP_SLOW_TESTS")) die('skip slow test');
?>
--FILE--
<?php
$file = __DIR__ . '/new_oom.inc';
$php = PHP_BINARY;
foreach (get_declared_classes() as $class) {
$output = shell_exec("$php $file $class 2>&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===

View File

@ -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;

View File

@ -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);
}
/* }}} */

View File

@ -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) {

View File

@ -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);