Merge branch 'fix-anonymous-socket-at-length-boundary' into PHP-8.2

This commit is contained in:
Derick Rethans 2024-06-13 14:03:46 +01:00
commit e0e9eb4053
No known key found for this signature in database
GPG Key ID: 910DEB46F53EA312
3 changed files with 51 additions and 6 deletions

View File

@ -1,5 +1,5 @@
--TEST--
Bug#60106 (stream_socket_server silently truncates long unix socket paths)
Bug #60106 (stream_socket_server silently truncates long unix socket paths)
--SKIPIF--
<?php
if( substr(PHP_OS, 0, 3) == "WIN" )
@ -11,9 +11,9 @@ error_reporting(E_ALL | E_NOTICE);
$socket_file = "/tmp/" . str_repeat("a", 512);
function get_truncated_socket_filename($errno, $errmsg, $file, $line) {
global $socket_file;
print_r ($errmsg);
echo $errmsg, "\n";
preg_match("#maximum allowed length of (\d+) bytes#", $errmsg, $matches);
$socket_file = substr($socket_file, 0, intval($matches[1]) - 1);
$socket_file = substr($socket_file, 0, intval($matches[1]));
}
set_error_handler("get_truncated_socket_filename", E_NOTICE);
stream_socket_server("unix://" . $socket_file);

View File

@ -0,0 +1,40 @@
--TEST--
Bug #60106 (stream_socket_server with abstract unix socket paths)
--SKIPIF--
<?php
if (PHP_OS != "Linux") die("skip Only for Linux systems");
?>
--FILE--
<?php
error_reporting(E_ALL | E_NOTICE);
/* This figures out the max length for normal sockets */
$socket_file = "/tmp/" . str_repeat("a", 512);
function get_truncated_socket_filename($errno, $errmsg, $file, $line) {
global $socket_file, $max_normal_length;
echo $errmsg, "\n";
preg_match("#maximum allowed length of (\d+) bytes#", $errmsg, $matches);
$max_normal_length = intval($matches[1]);
$socket_file = substr($socket_file, 0, $max_normal_length);
}
set_error_handler("get_truncated_socket_filename", E_NOTICE);
stream_socket_server("unix://" . $socket_file);
unlink($socket_file);
/* No we create an abstract one, prefixed with \0 so this should now work */
$abstract_socket = "\0" . $socket_file;
stream_socket_server("unix://" . $abstract_socket);
$old_max_length = $max_normal_length;
/* And now one longer, which should fail again */
$abstract_socket_long = "\0" . $abstract_socket . 'X';
stream_socket_server("unix://" . $abstract_socket_long);
echo "Allowed length is now one more: ", $max_normal_length == $old_max_length + 1 ? "yes" : "no", "\n";
?>
--EXPECTF--
stream_socket_server(): socket path exceeded the maximum allowed length of %d bytes and was truncated
stream_socket_server(): socket path exceeded the maximum allowed length of %d bytes and was truncated
Allowed length is now one more: yes

View File

@ -564,18 +564,23 @@ static inline int parse_unix_address(php_stream_xport_param *xparam, struct sock
memset(unix_addr, 0, sizeof(*unix_addr));
unix_addr->sun_family = AF_UNIX;
/* Abstract namespace does not need to be NUL-terminated, while path-based
* sockets should be. */
bool is_abstract_ns = xparam->inputs.namelen > 0 && xparam->inputs.name[0] == '\0';
unsigned long max_length = is_abstract_ns ? sizeof(unix_addr->sun_path) : sizeof(unix_addr->sun_path) - 1;
/* we need to be binary safe on systems that support an abstract
* namespace */
if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
if (xparam->inputs.namelen > max_length) {
/* On linux, when the path begins with a NUL byte we are
* referring to an abstract namespace. In theory we should
* allow an extra byte below, since we don't need the NULL.
* BUT, to get into this branch of code, the name is too long,
* so we don't care. */
xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
xparam->inputs.namelen = max_length;
php_error_docref(NULL, E_NOTICE,
"socket path exceeded the maximum allowed length of %lu bytes "
"and was truncated", (unsigned long)sizeof(unix_addr->sun_path));
"and was truncated", max_length);
}
memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);