Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion ext/spl/spl_observer.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,16 @@ static zend_result spl_object_storage_get_hash(zend_hash_key *key, spl_SplObject
ZVAL_OBJ(&param, obj);
ZVAL_UNDEF(&rv);
spl_object_storage_get_hash_depth++;
zend_call_method_with_1_params(&intern->std, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, &param);
bool bailout = false;
zend_try {
zend_call_method_with_1_params(&intern->std, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, &param);
} zend_catch {
bailout = true;
} zend_end_try();
spl_object_storage_get_hash_depth--;
if (UNEXPECTED(bailout)) {
zend_bailout();
}
if (UNEXPECTED(Z_ISUNDEF(rv))) {
/* An exception has occurred */
return FAILURE;
Expand Down
42 changes: 42 additions & 0 deletions sapi/cli/tests/spl_object_storage_gethash_bailout.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
SplObjectStorage getHash() depth counter is reset after a bailout in a user getHash()
--SKIPIF--
<?php
include "skipif.inc";
?>
--INI--
allow_url_fopen=1
--FILE--
<?php
include "php_cli_server.inc";

$code = <<<'PHP'
if ($_SERVER["REQUEST_URI"] === "/poison") {
class Poison extends SplObjectStorage {
public function getHash($o): string {
ini_set("memory_limit", "2M");
str_repeat("a", 100 * 1024 * 1024);
return "x";
}
}
(new Poison())->offsetSet(new stdClass());
echo "poison";
} else {
$s = new SplObjectStorage();
$s->offsetSet(new stdClass());
echo "check-ok count=", count($s);
}
PHP;

php_cli_server_start($code, 'router.php');

$base = 'http://' . PHP_CLI_SERVER_ADDRESS;
// Request 1 bails out (OOM) inside the overridden getHash() mid-offsetSet.
@file_get_contents($base . '/poison');
// A later request on the same worker must not be poisoned by a stuck counter.
echo @file_get_contents($base . '/check'), "\n";
echo @file_get_contents($base . '/check'), "\n";
?>
--EXPECT--
check-ok count=1
check-ok count=1
Loading