summaryrefslogtreecommitdiffstats
path: root/php-cve-2025-1220.patch
diff options
context:
space:
mode:
Diffstat (limited to 'php-cve-2025-1220.patch')
-rw-r--r--php-cve-2025-1220.patch153
1 files changed, 153 insertions, 0 deletions
diff --git a/php-cve-2025-1220.patch b/php-cve-2025-1220.patch
new file mode 100644
index 0000000..dad45e0
--- /dev/null
+++ b/php-cve-2025-1220.patch
@@ -0,0 +1,153 @@
+From 36150278addd8686a9899559241296094bd57282 Mon Sep 17 00:00:00 2001
+From: Jakub Zelenka <bukka@php.net>
+Date: Thu, 10 Apr 2025 15:15:36 +0200
+Subject: [PATCH 2/4] Fix GHSA-3cr5-j632-f35r: Null byte in hostnames
+
+This fixes stream_socket_client() and fsockopen().
+
+Specifically it adds a check to parse_ip_address_ex and it also makes
+sure that the \0 is not ignored in fsockopen() hostname formatting.
+
+(cherry picked from commit cac8f7f1cf4939f55f06b68120040f057682d89c)
+---
+ ext/standard/fsock.c | 27 +++++++++++++++++--
+ .../tests/network/ghsa-3cr5-j632-f35r.phpt | 21 +++++++++++++++
+ .../tests/streams/ghsa-3cr5-j632-f35r.phpt | 26 ++++++++++++++++++
+ main/streams/xp_socket.c | 9 ++++---
+ 4 files changed, 78 insertions(+), 5 deletions(-)
+ create mode 100644 ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt
+ create mode 100644 ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt
+
+diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c
+index a9c3cb0bf5d..636dbb6e359 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--
++<?php
++
++$server = stream_socket_server("tcp://localhost:0");
++
++if (preg_match('/:(\d+)$/', stream_socket_get_name($server, false), $m)) {
++ $client = fsockopen("localhost\0.example.com", intval($m[1]));
++ var_dump($client);
++ if ($client) {
++ fclose($client);
++ }
++}
++fclose($server);
++
++?>
++--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--
++<?php
++
++$server = stream_socket_server("tcp://localhost:0");
++$socket_name = stream_socket_get_name($server, false);
++
++if (preg_match('/:(\d+)$/', $socket_name, $m)) {
++ $port = $m[1];
++ $client = stream_socket_client("tcp://localhost\0.example.com:$port");
++ var_dump($client);
++ if ($client) {
++ fclose($client);
++ }
++} else {
++ echo "Could not extract port from socket name: $socket_name\n";
++}
++
++fclose($server);
++
++?>
++--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 68df3366340..47f1bdd4b1d 100644
+--- a/main/streams/xp_socket.c
++++ b/main/streams/xp_socket.c
+@@ -580,12 +580,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 = strpprintf(0, "The hostname must not contain null bytes");
++ 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);
+--
+2.50.0
+