Skip to content
Open
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 @@ -105,6 +105,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,62 @@
--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;

public function stream_open($path, $mode, $options, &$opened_path): bool
{
return true;
}

public function stream_close(): void
{
$context = stream_context_create([
'socket' => ['tcp_nodelay' => true],
]);
$GLOBALS['late_persistent_client'] = stream_socket_client(
$GLOBALS['late_context_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");
}

$GLOBALS['late_context_address'] = 'tcp://' . stream_socket_get_name($server, false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$GLOBALS['late_context_address'] = 'tcp://' . stream_socket_get_name($server, false);
$late_context_address = 'tcp://' . stream_socket_get_name($server, false);

Not sure what $GLOBALS does here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just making it available so that the other user can use it. I guess it could use global $late_context_address instead?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's top-level code - that's the global scope already.

stream_wrapper_register('late-context', LateContextWrapper::class);
$trigger = fopen('late-context://trigger', '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