diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index 9e1a53c0ec2..67c68468f51 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -23,6 +23,28 @@ #include "php_network.h" #include "file.h" +static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, + const char *host, size_t host_len, zend_long port) +{ + char portbuf[32]; + int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port); + size_t total_len = prefix_len + host_len + portlen; + + char *result = emalloc(total_len + 1); + + if (prefix_len > 0) { + memcpy(result, prefix, prefix_len); + } + memcpy(result + prefix_len, host, host_len); + memcpy(result + prefix_len + host_len, portbuf, portlen); + + result[total_len] = '\0'; + + *message = result; + + return total_len; +} + /* {{{ php_fsockopen() */ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) @@ -62,11 +84,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (persistent) { - spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, host, port); + php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, + host_len, port); } if (port > 0) { - hostname_len = spprintf(&hostname, 0, "%s:" ZEND_LONG_FMT, host, port); + hostname_len = php_fsockopen_format_host_port(&hostname, "", 0, host, host_len, port); } else { hostname_len = host_len; hostname = host; diff --git a/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 00000000000..7556c3be94c --- /dev/null +++ b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,21 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in fsockopen() +--FILE-- + +--EXPECTF-- + +Warning: fsockopen(): Unable to connect to localhost:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 00000000000..52f9263c99a --- /dev/null +++ b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,26 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in stream_socket_client() +--FILE-- + +--EXPECTF-- + +Warning: stream_socket_client(): Unable to connect to tcp://localhost\0.example.com:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index b17eccf2eeb..38f11d149de 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -581,12 +581,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po char *colon; char *host = NULL; -#ifdef HAVE_IPV6 - char *p; + if (memchr(str, '\0', str_len)) { + *err = ZSTR_INIT_LITERAL("The hostname must not contain null bytes", 0); + return NULL; + } +#ifdef HAVE_IPV6 if (*(str) == '[' && str_len > 1) { /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ - p = memchr(str + 1, ']', str_len - 2); + char *p = memchr(str + 1, ']', str_len - 2); if (!p || *(p + 1) != ':') { if (get_err) { *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str);