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
4 changes: 4 additions & 0 deletions ext/standard/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ PHPAPI void php_fstat(php_stream *stream, zval *return_value);
PHPAPI void php_flock_common(php_stream *stream, zend_long operation, uint32_t operation_arg_num,
zval *wouldblock, zval *return_value);

/* Escapes NUL and backslash bytes so a host containing them cannot truncate or
* collide the persistent stream hash key. */
zend_string *php_stream_escape_persistent_key(const char *host, size_t hostlen);

#define PHP_CSV_NO_ESCAPE EOF
#define PHP_CSV_ESCAPE_ERROR -500

Expand Down
5 changes: 3 additions & 2 deletions ext/standard/fsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
}

if (persistent) {
php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host,
host_len, port);
zend_string *escaped = php_stream_escape_persistent_key(host, host_len);
spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, ZSTR_VAL(escaped), port);
zend_string_release_ex(escaped, false);
}

if (port > 0) {
Expand Down
25 changes: 24 additions & 1 deletion ext/standard/streamsfuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ typedef unsigned __int64 php_timeout_ull;

static php_stream_context *decode_context_param(zval *contextresource);

zend_string *php_stream_escape_persistent_key(const char *host, size_t hostlen)
{
zend_string *escaped = zend_string_safe_alloc(hostlen, 2, 0, 0);
char *ptr = ZSTR_VAL(escaped);
for (size_t i = 0; i < hostlen; i++) {
if (host[i] == '\0') {
*ptr++ = '\\';
*ptr++ = '0';
} else if (host[i] == '\\') {
*ptr++ = '\\';
*ptr++ = '\\';
} else {
*ptr++ = host[i];
}
}
*ptr = '\0';
ZSTR_LEN(escaped) = ptr - ZSTR_VAL(escaped);

return escaped;
}

/* Streams based network functions */

#ifdef HAVE_SOCKETPAIR
Expand Down Expand Up @@ -130,7 +151,9 @@ PHP_FUNCTION(stream_socket_client)
context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);

if (flags & PHP_STREAM_CLIENT_PERSISTENT) {
spprintf(&hashkey, 0, "stream_socket_client__%s", ZSTR_VAL(host));
zend_string *escaped = php_stream_escape_persistent_key(ZSTR_VAL(host), ZSTR_LEN(host));
spprintf(&hashkey, 0, "stream_socket_client__%s", ZSTR_VAL(escaped));
zend_string_release_ex(escaped, false);
}

/* prepare the timeout value for use */
Expand Down
45 changes: 45 additions & 0 deletions ext/standard/tests/streams/gh22617.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
--TEST--
GH-22617: Persistent abstract unix domain sockets resolved to wrong resource
--CREDITS--
Roysten
--SKIPIF--
<?php
if (PHP_OS_FAMILY !== "Linux") die("skip abstract unix domain sockets are Linux-only");
if (!function_exists("pfsockopen")) die("skip pfsockopen() not available");
?>
--FILE--
<?php
// Abstract sockets: the address is prefixed with a null byte.
$name1 = "gh22617_" . getmypid() . "_1";
$name2 = "gh22617_" . getmypid() . "_2";

$server1 = stream_socket_server("unix://\0$name1", $errno, $errstr);
$server2 = stream_socket_server("unix://\0$name2", $errno, $errstr);
var_dump($server1 !== false, $server2 !== false);

// Connect to the first abstract socket.
$socket1 = pfsockopen("unix://\0$name1", 0, $errno1, $errstr1);
var_dump($socket1 !== false);
var_dump(get_resource_type($socket1));

// Connect to the second abstract socket.
$socket2 = pfsockopen("unix://\0$name2", 0, $errno2, $errstr2);
var_dump($socket2 !== false);
var_dump(get_resource_type($socket2));

// The two distinct abstract sockets must resolve to distinct resources.
var_dump((int) $socket1 !== (int) $socket2);

fclose($socket1);
fclose($socket2);
fclose($server1);
fclose($server2);
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
string(17) "persistent stream"
bool(true)
string(17) "persistent stream"
bool(true)
Loading