diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index f897ab1350cc..64d5011975d2 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -94,8 +94,16 @@ static zend_result spl_object_storage_get_hash(zend_hash_key *key, spl_SplObject ZVAL_OBJ(¶m, 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, ¶m); + bool bailout = false; + zend_try { + zend_call_method_with_1_params(&intern->std, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, ¶m); + } 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; diff --git a/sapi/cli/tests/spl_object_storage_gethash_bailout.phpt b/sapi/cli/tests/spl_object_storage_gethash_bailout.phpt new file mode 100644 index 000000000000..e7dceb027128 --- /dev/null +++ b/sapi/cli/tests/spl_object_storage_gethash_bailout.phpt @@ -0,0 +1,42 @@ +--TEST-- +SplObjectStorage getHash() depth counter is reset after a bailout in a user getHash() +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +--FILE-- +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