[php-src] php-8.3.32: Fix NULL deref when enabling TLS fails and the peer name needs to be reset

Nora Dossche via Jakub Zelenka <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Nora Dossche (ndossche)
Committer: Jakub Zelenka (bukka)
Date: 2026-07-01T10:49:39+02:00

Commit: https://github.com/php/php-src/commit/84f63e07aadb4ef84630b025ab7c72d579eee864
Raw diff: https://github.com/php/php-src/commit/84f63e07aadb4ef84630b025ab7c72d579eee864.diff

Fix NULL deref when enabling TLS fails and the peer name needs to be reset

The code tries to read the context on NULL when
`php_stream_xport_crypto_setup` fails because by then `stream` is reset
to NULL.
This is also UB, so can cause miscompiles.

```
==1217==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000090 (pc 0x55d829ed3acf bp 0x7fff045f5770 sp 0x7fff045f4df0 T0)
==1217==The signal is caused by a READ memory access.
==1217==Hint: address points to the zero page.
    #0 0x55d829ed3acf in php_stream_url_wrap_http_ex /work/php-src/ext/standard/http_fopen_wrapper.c:580
    #1 0x55d829ed857e in php_stream_url_wrap_http /work/php-src/ext/standard/http_fopen_wrapper.c:1204
    #2 0x55d82a15073d in _php_stream_open_wrapper_ex /work/php-src/main/streams/streams.c:2270
    #3 0x55d829e78fa6 in zif_file_get_contents /work/php-src/ext/standard/file.c:409
    #4 0x55d829bbfe39 in zif_phar_file_get_contents /work/php-src/ext/phar/func_interceptors.c:226
    #5 0x55d82a0b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
    #6 0x55d82a3e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
    #7 0x55d82a540995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
    #8 0x55d82a5558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
    #9 0x55d82a6ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
    #10 0x55d82a0ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
    #11 0x55d82a0ecccb in php_execute_script /work/php-src/main/main.c:2685
    #12 0x55d82a6bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
    #13 0x55d82a6c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
    #14 0x7f9e770491c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #15 0x7f9e7704928a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #16 0x55d829209b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```

This is a backport of:
 * 7782b8876bc0f61b0e8bcce262e22896e423b22e
 * f9519ccbb9975b2afae95a418fae65ad93823dc6
 * afded3dffc432a86955e6b8018146abb3d03779b

Co-authored-by: Ilija Tovilo <[email protected]>

Changed paths:
  A  ext/openssl/tests/gh21031.phpt
  A  ext/openssl/tests/sni_server_cs_expired.pem
  M  NEWS
  M  ext/standard/http_fopen_wrapper.c


Diff:

diff --git a/NEWS b/NEWS
index 9a9d86351752..9e0f80a15ccc 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@ PHP                                                                        NEWS
 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 ?? ??? ????, PHP 8.3.32
 
+- Streams:
+  . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL
+    and a proxy set). (CVE-2026-12184) (ndossche)
+
 - OpenSSL:
   . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in
     openssl_encrypt with AES-WRAP-PAD). (David Carlier)
diff --git a/ext/openssl/tests/gh21031.phpt b/ext/openssl/tests/gh21031.phpt
new file mode 100644
index 000000000000..25bdb385a791
--- /dev/null
+++ b/ext/openssl/tests/gh21031.phpt
@@ -0,0 +1,55 @@
+--TEST--
+GH-21031 (Fix NULL deref when enabling TLS fails and the peer name needs to be reset)
+--EXTENSIONS--
+openssl
+--SKIPIF--
+<?php
+if (!function_exists("proc_open")) die("skip no proc_open");
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+    die("skip not reliable on Windows due to proxy wait limitation");
+}
+?>
+--FILE--
+<?php
+
+$serverCode = <<<'CODE'
+    $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
+    $ctx = stream_context_create(['ssl' => [
+        'SNI_server_certs' => [
+            "cs.php.net" => __DIR__ . "/sni_server_cs_expired.pem",
+        ]
+    ]]);
+
+    $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $serverFlags, $ctx);
+    phpt_notify_server_start($server);
+
+    $conn = stream_socket_accept($server, 10);
+    if ($conn) {
+        fclose($conn);
+    }
+
+    phpt_wait();
+CODE;
+
+$clientCode = <<<'CODE'
+    $clientCtx = stream_context_create([
+        'ssl' => [
+            'cafile' => __DIR__ . '/sni_server_ca.pem',
+            'verify_peer' => true,
+            'verify_peer_name' => true,
+        ],
+        "http" => [
+            "proxy" => "tcp://{{ ADDR }}"
+        ],
+    ]);
+
+    var_dump(@file_get_contents("https://cs.php.net/", false, $clientCtx));
+
+    phpt_notify();
+CODE;
+
+include 'ServerClientTestCase.inc';
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
+?>
+--EXPECT--
+bool(false)
diff --git a/ext/openssl/tests/sni_server_cs_expired.pem b/ext/openssl/tests/sni_server_cs_expired.pem
new file mode 100644
index 000000000000..9f5a201b26d1
--- /dev/null
+++ b/ext/openssl/tests/sni_server_cs_expired.pem
@@ -0,0 +1,57 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAvy5NhzktzEdsHTbGB6vqYANms5rn1zXFmTJrGlWCwoIsNmTf
+ahvZkrC1cCXTZ7fbPB8XQbpAtz2ZSU7OcwBW9B8okYUPo9zi/ptwcrgsQsN0hrcD
+8MBRUccevwime5fLvg8E9RJ/68y9y3BnRcVWYO2sAK9juTfidNjETU3Bb05oXv8D
+SD/6onXQu4uXDgsQ3cRXeld9UB0xazmQXyyiIqXc/cpTAnaEVYzn28aj7NlUbzNq
+511UXMXY44x9EcXWpPVZ7heNcJNzY5DCNzmtXKrt9yiMpWQcPXEzsESVxAMqib9u
+TFOlvVX17LIPxBG656PjTD9J1h6kBbMCUxzs7wIDAQABAoIBAQC85lBeY0X4ST3v
+I7bJz7kWQ2YP4uhfAdeLhoDDFWjNLffniwYhfwEc6xNri0R2f/jUT9gX7qORKwEx
+qPdeNCC2t67LElGg1FlJv2Z9Q7MgCKYzkdQH5s6y4e9kTHTLO/JpiceZKz1QTQ3f
+XOH9032E6nIAf0wmr6xHTgOwajrN8VI5BuPEMVmEwIw3AtYeqVuPCNKyGR4HUVkC
+2bAydnGngbRJRnNzmKcWJancxpHDGBSFqPyuXMFC7Jgo3ZmyCbGp99vuXVk/sW9x
+5aj94M9nRE0guk05ivH2/JZao2uLYkIgjFWlhNxKdWgWRk8DEuN4djC8mKS9YH1q
+crYRToMhAoGBAOspUTtKP54mpZmyhxuDqj02JaJRzNTskPHsiF1UhtXuw7uT+ryV
+ekUFLNXoFmn9mbx1WVaUvGH4qjilvQOxz7u++lz0ApqJEfyM3jc/cC40Y5zcuGSu
+Etbg+SyDoytlgMCIydJyrS7NNALSo5p5oG6XY2f8yd/DCAmo8LzypaHRAoGBANAf
+R1SlBMc/bOsi6GrJxcBVSCFMiKYiO5woL5aUKa9yM+UQuQ/6xbQ7Q+sOlt0FH3xo
+AJ2L60qTdjyXVtjOdtXs5ZC4l+C6AfnCx6yLr+fNc4SOYXEfqS4LZylgwKd9KyVB
+asspIW9Idbgebmi6vPyt9LDkIp0h1VuFGjkvQJK/AoGBAI4pbS0dprXyARyYW6sb
+fpgAmuG099IkrT9DUfCx/81myTclr2fAKal+BmvOIXaz0/OlMXvw8K19iVIzh7+r
+B70lJ+93p/dKM/BsLI5TsHqOO0YB/QsIXOVAHgJ2FfdPJnW+e9vYba+kZ/Po6PSi
+4ITaykJ8BIJcQgis89QWEGFxAoGBAJhQO+jzuDKF9ZWEf6ofrw0anOZZ16wWY5/e
+PS2rk3JmVxpuibHrKqPDt+ogTELHDAsFJmYmz3VNxHuFmrajK49Wh4/JuMVr/CQo
+6+8YcA1qa/94IFIlBLDBAafjujsZvOjQHnM+z8xcsGKmStF00Pjv6qNG4xoyd646
+FD4DmfOLAoGAWXehpopZKXE9gRAni881ucK6WqxPPBoofbozi09D0MmfarIVaSkv
+jNVVHBfLWd7IEXTjiipPBeUqq6Jc3pscN1Vp4rrl8jTmVTdazEv0LuzpdUFqmNo2
+M+xw17uz9D9Q32/aW1Lar0PdIaL/wGEDEyzEBFwrGppcENLilPz8gzU=
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIFIjCCAwqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix
+EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w
+ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjEyWhcNMjYwNDAy
+MTgzNjEyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE
+CgwHUEhQLm5ldDETMBEGA1UEAwwKY3MucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAL8uTYc5LcxHbB02xger6mADZrOa59c1xZkyaxpVgsKC
+LDZk32ob2ZKwtXAl02e32zwfF0G6QLc9mUlOznMAVvQfKJGFD6Pc4v6bcHK4LELD
+dIa3A/DAUVHHHr8IpnuXy74PBPUSf+vMvctwZ0XFVmDtrACvY7k34nTYxE1NwW9O
+aF7/A0g/+qJ10LuLlw4LEN3EV3pXfVAdMWs5kF8soiKl3P3KUwJ2hFWM59vGo+zZ
+VG8zauddVFzF2OOMfRHF1qT1We4XjXCTc2OQwjc5rVyq7fcojKVkHD1xM7BElcQD
+Kom/bkxTpb1V9eyyD8QRuuej40w/SdYepAWzAlMc7O8CAwEAAaOCAQkwggEFMAkG
+A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu
+U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFHPfd8dK
+Lz1R0Ck4WV1B9AWXd5DSMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa
+oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH
+UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG
+A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQB6WSIHEyDXLZxH
+hZjqSNQOA7Wc9Z2FCAiD29xYkGTL8WuPVGGP1mu4B92ytj+PMWwqSReDa7eTGLE7
+O7ozw9l+c+gNmHFNikSsGjlV2E8CToQOFMny+jAQYMSXf8UbTp9xDfgG02t/71hv
+SLWqdeHMLcR0xi0nBQH0vDOkwUbuWYqFa3jejHieGhykHM6CkIk6lqnyOEO+ooIF
+ZsLprrg1ss/mXCPI6niP0hze55ERKdxI7Rk8sZ4pVkf2SUWqZrUS0aJ+Ymmwi6Xd
+2V7izq5N30PkJS8MtqII4FAjRBIkwPh0sy8PmW/DzkYU+lYQnDfYLKDFKcj8xJK/
+o8oZUBsQltrSj0KlM9QuqxCTCBCy1nXZ9WHOhq+jdLiTc1Oi60uEHcUMrLK8aYc4
+HqIvZS6C2iwMI0d1OP3VxmAbMQ9yqRi+FbLYavJ3H40jrU9SYqdxa0BrTaz8MJNE
+6AEwgQDPChczSghvHME+Fs4mtGCY3TesbNZKVahQRjaFIhMZIZ4RP4CRc0bJOBG+
+8Me4+KHNsD2ki5b03wAN6C1P2QrMzI+gH9fXLZYp761ciDAsX6YIzrhHHYLxYpJH
+BkQKKs8dCQWE5IzgVrdlvC3Z1/l9om66wHqqx7nKnPfYs/Sfnwe9MpCD6xJrXiTm
+WS7NM6fbQpO9APNr7o0ZOjbbWFzlNw==
+-----END CERTIFICATE-----
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
index 040ee4eabf78..00f7b4dfa80e 100644
--- a/ext/standard/http_fopen_wrapper.c
+++ b/ext/standard/http_fopen_wrapper.c
@@ -546,6 +546,10 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
 		smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1);
 
 		if (php_stream_write(stream, ZSTR_VAL(header.s), ZSTR_LEN(header.s)) != ZSTR_LEN(header.s)) {
+			if (reset_ssl_peer_name) {
+				php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name");
+			}
+
 			php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy");
 			php_stream_close(stream);
 			stream = NULL;
@@ -567,16 +571,18 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
 
 		/* enable SSL transport layer */
 		if (stream) {
+			php_stream_context *old_context = PHP_STREAM_CONTEXT(stream);
+
 			if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 ||
 			    php_stream_xport_crypto_enable(stream, 1) < 0) {
 				php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy");
 				php_stream_close(stream);
 				stream = NULL;
 			}
-		}
 
-		if (reset_ssl_peer_name) {
-			php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name");
+			if (reset_ssl_peer_name) {
+				php_stream_context_unset_option(old_context, "ssl", "peer_name");
+			}
 		}
 	}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.