Skip to content

Commit e585034

Browse files
authored
Fix GH-21699: callable resolution must fail if error handler threw during self/parent/static deprecations (#21712)
When resolving string callables using self::, parent::, or static::, zend_is_callable_check_class() emits E_DEPRECATED. If the user error handler throws, EG(exception) is set but the function could still return true, leading to trampoline allocation and a failed assertion in shutdown_executor(). Return false from zend_is_callable_check_class() when EG(exception) is set after handling.
1 parent 1a428e5 commit e585034

File tree

6 files changed

+106
-1
lines changed

6 files changed

+106
-1
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ PHP NEWS
88
. Fixed bug GH-21478 (Forward property operations to real instance for
99
initialized lazy proxies). (iliaal)
1010
. Fixed bug GH-21605 (Missing addref for Countable::count()). (ilutov)
11+
. Fixed bug GH-21699 (Assertion failure in shutdown_executor when resolving
12+
self::/parent::/static:: callables if the error handler throws).
1113

1214
- Curl:
1315
. Add support for brotli and zstd on Windows. (Shivam Mathur)

Zend/tests/gh16799.phpt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ Test::test();
1515
--EXPECTF--
1616
Fatal error: Uncaught Exception: Use of "static" in callables is deprecated in %s:%d
1717
Stack trace:
18-
#0 %s(%d): {closure:%s:%d}(8192, 'Use of "static"...', %s, %d)
18+
#0 %s(%d): {closure:%s}(8192, 'Use of "static"%s', '%s', %d)
1919
#1 %s(%d): Test::test()
2020
#2 {main}
21+
22+
Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d
23+
Stack trace:
24+
#0 %s(%d): Test::test()
25+
#1 {main}
2126
thrown in %s on line %d

Zend/tests/gh_21699.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
GH-21699: Assertion failure in shutdown_executor when error handler throws during self:: callable resolution
3+
--FILE--
4+
<?php
5+
set_error_handler(function () {
6+
throw new Exception;
7+
});
8+
class bar {
9+
public static function __callstatic($fusion, $b)
10+
{
11+
}
12+
public function test()
13+
{
14+
call_user_func('self::y');
15+
}
16+
}
17+
$x = new bar;
18+
$x->test();
19+
?>
20+
--EXPECTF--
21+
Fatal error: Uncaught Exception in %s:%d
22+
Stack trace:
23+
#0 %s(%d): {closure:%s}(%d, 'Use of "self" i%s', '%s', %d)
24+
#1 %s(%d): bar->test()
25+
#2 {main}
26+
27+
Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d
28+
Stack trace:
29+
#0 %s(%d): bar->test()
30+
#1 {main}
31+
thrown in %s on line %d

Zend/tests/gh_21699_parent.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
GH-21699 (parent::): no shutdown_executor trampoline assertion when error handler throws during parent:: callable resolution
3+
--FILE--
4+
<?php
5+
set_error_handler(function () {
6+
throw new Exception;
7+
});
8+
class Base {
9+
public static function __callStatic($name, $args)
10+
{
11+
}
12+
}
13+
class Child extends Base {
14+
public function test()
15+
{
16+
call_user_func('parent::missing');
17+
}
18+
}
19+
(new Child)->test();
20+
?>
21+
--EXPECTF--
22+
Fatal error: Uncaught Exception in %s:%d
23+
Stack trace:
24+
#0 %s(%d): {closure:%s}(%d, 'Use of "parent"%s', '%s', %d)
25+
#1 %s(%d): Child->test()
26+
#2 {main}
27+
28+
Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d
29+
Stack trace:
30+
#0 %s(%d): Child->test()
31+
#1 {main}
32+
thrown in %s on line %d

Zend/tests/gh_21699_static.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
GH-21699 (static::): no shutdown_executor trampoline assertion when error handler throws during static:: callable resolution
3+
--FILE--
4+
<?php
5+
set_error_handler(function () {
6+
throw new Exception;
7+
});
8+
class bar {
9+
public static function __callstatic($fusion, $b)
10+
{
11+
}
12+
public function test()
13+
{
14+
call_user_func('static::y');
15+
}
16+
}
17+
$x = new bar;
18+
$x->test();
19+
?>
20+
--EXPECTF--
21+
Fatal error: Uncaught Exception in %s:%d
22+
Stack trace:
23+
#0 %s(%d): {closure:%s}(%d, 'Use of "static"%s', '%s', %d)
24+
#1 %s(%d): bar->test()
25+
#2 {main}
26+
27+
Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d
28+
Stack trace:
29+
#0 %s(%d): bar->test()
30+
#1 {main}
31+
thrown in %s on line %d

Zend/zend_API.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3849,6 +3849,10 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
38493849
if (error) zend_spprintf(error, 0, "class \"%.*s\" not found", (int)name_len, ZSTR_VAL(name));
38503850
}
38513851
ZSTR_ALLOCA_FREE(lcname, use_heap);
3852+
/* User error handlers may throw from deprecations above; do not report callable as valid. */
3853+
if (UNEXPECTED(EG(exception))) {
3854+
return false;
3855+
}
38523856
return ret;
38533857
}
38543858
/* }}} */

0 commit comments

Comments
 (0)