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
1 change: 1 addition & 0 deletions ext/spl/php_spl.c
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ PHP_MINIT_FUNCTION(spl)
PHP_RINIT_FUNCTION(spl) /* {{{ */
{
spl_autoload_extensions = NULL;
spl_object_storage_reset_get_hash_depth();
return SUCCESS;
} /* }}} */

Expand Down
5 changes: 5 additions & 0 deletions ext/spl/spl_observer.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ static zend_object_handlers spl_handler_MultipleIterator;

ZEND_TLS uint32_t spl_object_storage_get_hash_depth;

void spl_object_storage_reset_get_hash_depth(void)
{
spl_object_storage_get_hash_depth = 0;
}

typedef struct _spl_SplObjectStorage { /* {{{ */
HashTable storage;
zend_long index;
Expand Down
2 changes: 2 additions & 0 deletions ext/spl/spl_observer.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ extern PHPAPI zend_class_entry *spl_ce_MultipleIterator;

PHP_MINIT_FUNCTION(spl_observer);

void spl_object_storage_reset_get_hash_depth(void);

#endif /* SPL_OBSERVER_H */
44 changes: 44 additions & 0 deletions ext/spl/tests/spl_object_storage_gethash_bailout.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
--TEST--
SplObjectStorage getHash() depth counter is reset after a bailout in a user getHash()
--SKIPIF--
<?php
if (!file_exists(__DIR__ . "/../../../sapi/cli/tests/php_cli_server.inc")) {
die("skip sapi/cli/tests/php_cli_server.inc required but not found");
}
?>
--INI--
allow_url_fopen=1
--FILE--
<?php
include __DIR__ . "/../../../sapi/cli/tests/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