From d354d83efc8baf2b29aca60a4494e9c075ca0ab7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 31 Oct 2025 15:52:54 +0000 Subject: [PATCH 1/4] Fix GH-20286: use-after-destroy during userland stream_close(). --- Zend/zend_execute_API.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index e2503d6d3045d..de464010a74cd 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1077,6 +1077,14 @@ ZEND_API zend_result zend_call_method_if_exists( zend_object *object, zend_string *method_name, zval *retval, uint32_t param_count, zval *params) { + if (UNEXPECTED(!EG(active))) { +#if ZEND_DEBUG + ZEND_UNREACHABLE(); +#endif + ZVAL_UNDEF(retval); + return FAILURE; + } + zend_fcall_info fci; fci.size = sizeof(zend_fcall_info); fci.object = object; From f4280b1a5cbd5d9a433b941521b6ef43f870a13b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 31 Oct 2025 21:48:49 +0000 Subject: [PATCH 2/4] fix segfault on streams destruction --- ext/standard/tests/streams/gh20286.phpt | 54 +++++++++++++++++++++++++ main/streams/userspace.c | 4 ++ 2 files changed, 58 insertions(+) create mode 100644 ext/standard/tests/streams/gh20286.phpt diff --git a/ext/standard/tests/streams/gh20286.phpt b/ext/standard/tests/streams/gh20286.phpt new file mode 100644 index 0000000000000..ce370d5bac221 --- /dev/null +++ b/ext/standard/tests/streams/gh20286.phpt @@ -0,0 +1,54 @@ +--TEST-- +GH-20286 use after destroy on userland stream_close +--CREDITS-- +vi3tL0u1s +--FILE-- + +--EXPECTF-- + +Deprecated: Creation of dynamic property lib::$context is deprecated in %s on line %d + +Warning: include(): lib::stream_set_option is not implemented! in %s on line %d + +Warning: include(): lib::stream_stat is not implemented! in %s on line %d + +Deprecated: Creation of dynamic property lib::$context is deprecated in %s on line %d + +Warning: include(): lib::stream_set_option is not implemented! in %s on line %d + +Warning: include(): lib::stream_stat is not implemented! in %s on line %d + +Fatal error: Cannot redeclare a() (previously declared in %s:%d) %s on line %d + +Deprecated: Creation of dynamic property lib::$context is deprecated in %s on line %d + +Warning: include(): lib::stream_set_option is not implemented! in %s on line %d + +Warning: include(): lib::stream_stat is not implemented! in %s on line %d + +Fatal error: Cannot redeclare a() (previously declared in %s:%d) %s on line %d + +Deprecated: Creation of dynamic property lib::$context is deprecated in %s on line %d + +Warning: include(): lib::stream_set_option is not implemented! in %s on line %d + +Warning: include(): lib::stream_stat is not implemented! in %s on line %d + +Fatal error: Cannot redeclare a() (previously declared in %s:%d) %s on line %d diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 8d15172ef1319..89984277611dc 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -695,6 +695,10 @@ static int php_userstreamop_close(php_stream *stream, int close_handle) assert(us != NULL); + if (UNEXPECTED(stream->wrapper->wops != &user_stream_wops)) { + stream->wrapper->wops = &user_stream_wops; + } + ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1); call_method_if_exists(&us->object, &func_name, &retval, 0, NULL); From eb466e1cbbdef53db97a1641d015d65418795445 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 31 Oct 2025 22:06:07 +0000 Subject: [PATCH 3/4] fix test --- ext/standard/tests/streams/gh20286.phpt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/standard/tests/streams/gh20286.phpt b/ext/standard/tests/streams/gh20286.phpt index ce370d5bac221..dd6e83267000a 100644 --- a/ext/standard/tests/streams/gh20286.phpt +++ b/ext/standard/tests/streams/gh20286.phpt @@ -2,6 +2,10 @@ GH-20286 use after destroy on userland stream_close --CREDITS-- vi3tL0u1s +--SKIPIF-- + --FILE-- Date: Sat, 1 Nov 2025 06:03:18 +0000 Subject: [PATCH 4/4] test feedback --- ext/standard/tests/streams/gh20286.phpt | 60 +++++++++---------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/ext/standard/tests/streams/gh20286.phpt b/ext/standard/tests/streams/gh20286.phpt index dd6e83267000a..aa281fe3e4034 100644 --- a/ext/standard/tests/streams/gh20286.phpt +++ b/ext/standard/tests/streams/gh20286.phpt @@ -9,50 +9,32 @@ if (PHP_DEBUG) die('skip requires release build'); --FILE-- --EXPECTF-- -Deprecated: Creation of dynamic property lib::$context is deprecated in %s on line %d - -Warning: include(): lib::stream_set_option is not implemented! in %s on line %d - -Warning: include(): lib::stream_stat is not implemented! in %s on line %d - -Deprecated: Creation of dynamic property lib::$context is deprecated in %s on line %d - -Warning: include(): lib::stream_set_option is not implemented! in %s on line %d - -Warning: include(): lib::stream_stat is not implemented! in %s on line %d - -Fatal error: Cannot redeclare a() (previously declared in %s:%d) %s on line %d - -Deprecated: Creation of dynamic property lib::$context is deprecated in %s on line %d - -Warning: include(): lib::stream_set_option is not implemented! in %s on line %d - -Warning: include(): lib::stream_stat is not implemented! in %s on line %d - -Fatal error: Cannot redeclare a() (previously declared in %s:%d) %s on line %d - -Deprecated: Creation of dynamic property lib::$context is deprecated in %s on line %d - -Warning: include(): lib::stream_set_option is not implemented! in %s on line %d +Fatal error: Cannot redeclare a() (previously declared in %s:%d) in %s on line %d -Warning: include(): lib::stream_stat is not implemented! in %s on line %d +Fatal error: Cannot redeclare a() (previously declared in %s:%d) in %s on line %d -Fatal error: Cannot redeclare a() (previously declared in %s:%d) %s on line %d +Fatal error: Cannot redeclare a() (previously declared in %s:%d) in %s on line %d