Skip to content
Merged
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 NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ PHP NEWS
. Fixed three Windows-only proc_open() defects: an uninitialized
PROCESS_INFORMATION, an indeterminate comspec pointer after a failed
lookup, and an unchecked CreateFileA() failure. (Ilia Alshanetsky)
. Fix persistent stream context lifetime during shutdown (Levi Morrison)

- XSL:
. Fixed bug GH-23730 (use-after-free when XSLTProcessor::importStylesheet()
Expand Down
5 changes: 3 additions & 2 deletions ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ zend_module_entry basic_functions_module = { /* {{{ */
PHP_RSHUTDOWN(basic), /* request shutdown */
PHP_MINFO(basic), /* extension info */
PHP_STANDARD_VERSION, /* extension version */
STANDARD_MODULE_PROPERTIES
NO_MODULE_GLOBALS,
ZEND_MODULE_POST_ZEND_DEACTIVATE_N(streams),
STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */

Expand Down Expand Up @@ -463,7 +465,6 @@ PHP_RSHUTDOWN_FUNCTION(basic) /* {{{ */
#endif
BASIC_RSHUTDOWN_SUBMODULE(assert)
BASIC_RSHUTDOWN_SUBMODULE(url_scanner_ex)
BASIC_RSHUTDOWN_SUBMODULE(streams)
#ifdef PHP_WIN32
BASIC_RSHUTDOWN_SUBMODULE(win32_core_globals)
#endif
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
--TEST--
Persistent stream contexts created during resource shutdown are detached
--FILE--
<?php

if (($argv[1] ?? null) !== 'child') {
// Wait for the trigger's final status because the crash occurs after stdout closes.
$process = proc_open(
[PHP_BINARY, '-n', __FILE__, 'child'],
[
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
],
$pipes,
);
fclose($pipes[0]);
stream_get_contents($pipes[1]);
fclose($pipes[1]);
stream_get_contents($pipes[2]);
fclose($pipes[2]);
var_dump(proc_close($process));
return;
}

final class LateContextWrapper
{
public $context;
private string $address;

public function stream_open($path, $mode, $options, &$opened_path): bool
{
$this->address = substr($path, strlen('late-context://'));
return true;
}

public function stream_close(): void
{
$context = stream_context_create([
'socket' => ['tcp_nodelay' => true],
]);
stream_socket_client(
$this->address,
$errno,
$error,
1,
STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT,
$context,
);
}
}

$server = stream_socket_server('tcp://127.0.0.1:0', $errno, $error);
if (!$server) {
die("server failed: $error ($errno)\n");
}

stream_wrapper_register('late-context', LateContextWrapper::class);
$trigger = fopen('late-context://tcp://' . stream_socket_get_name($server, false), 'r');

?>
--EXPECT--
int(0)
2 changes: 1 addition & 1 deletion main/php_streams.h
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ END_EXTERN_C()
int php_init_stream_wrappers(int module_number);
void php_shutdown_stream_wrappers(int module_number);
void php_shutdown_stream_hashes(void);
PHP_RSHUTDOWN_FUNCTION(streams);
ZEND_MODULE_POST_ZEND_DEACTIVATE_D(streams);

BEGIN_EXTERN_C()
PHPAPI zend_result php_register_url_stream_wrapper(const char *protocol, const php_stream_wrapper *wrapper);
Expand Down
9 changes: 3 additions & 6 deletions main/streams/streams.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,14 @@ static int forget_persistent_resource_id_numbers(zval *el)
fprintf(stderr, "forget_persistent: %s:%p\n", stream->ops->label, stream);
#endif

/* Request resources have been destroyed; clear their stale pointers. */
stream->res = NULL;

if (stream->ctx) {
zend_list_delete(stream->ctx);
stream->ctx = NULL;
}
stream->ctx = NULL;

return 0;
}

PHP_RSHUTDOWN_FUNCTION(streams)
ZEND_MODULE_POST_ZEND_DEACTIVATE_D(streams)
{
zval *el;

Expand Down
Loading