Skip to content

Commit 7782b88

Browse files
committed
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 php#9 0x55d82a6ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980 php#10 0x55d82a0ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645 php#11 0x55d82a0ecccb in php_execute_script /work/php-src/main/main.c:2685 php#12 0x55d82a6bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951 php#13 0x55d82a6c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362 php#14 0x7f9e770491c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e) php#15 0x7f9e7704928a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e) php#16 0x55d829209b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c) ``` Closes phpGH-21468. Closes phpGH-21031.
1 parent 9c08243 commit 7782b88

File tree

4 files changed

+152
-3
lines changed

4 files changed

+152
-3
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ PHP NEWS
2424
. Fixed bug GH-21499 (RecursiveArrayIterator getChildren UAF after parent
2525
free). (Girgias)
2626

27+
- Streams:
28+
. Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL
29+
and a proxy set). (ndossche)
30+
2731
09 Apr 2026, PHP 8.4.20
2832

2933
- Bz2:

ext/openssl/tests/gh21031.phpt

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
--TEST--
2+
GH-21031 (Fix NULL deref when enabling TLS fails and the peer name needs to be reset)
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("proc_open")) die("skip no proc_open");
8+
if (substr(PHP_OS, 0, 3) == 'WIN') {
9+
die("skip not reliable on Windows due to proxy wait limitation");
10+
}
11+
?>
12+
--FILE--
13+
<?php
14+
15+
$serverCode = <<<'CODE'
16+
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
17+
$ctx = stream_context_create(['ssl' => [
18+
'SNI_server_certs' => [
19+
"cs.php.net" => __DIR__ . "/sni_server_cs_expired.pem",
20+
]
21+
]]);
22+
23+
$server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $serverFlags, $ctx);
24+
phpt_notify_server_start($server);
25+
26+
$conn = stream_socket_accept($server, 3);
27+
fclose($conn);
28+
29+
phpt_wait();
30+
CODE;
31+
32+
$proxyCode = <<<'CODE'
33+
$flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
34+
$server = stream_socket_server("tcp://127.0.0.1:0", $errornum, $errorstr, $flags);
35+
phpt_notify_server_start($server);
36+
37+
$upstream = stream_socket_client("tcp://{{ ADDR }}", $errornum, $errorstr, 30, STREAM_CLIENT_CONNECT);
38+
stream_set_blocking($upstream, false);
39+
40+
$conn = stream_socket_accept($server);
41+
stream_set_blocking($conn, true);
42+
43+
// reading CONNECT request headers
44+
while (($line = fgets($conn)) !== false) {
45+
if (rtrim($line) === '') break; // empty line means end of headers
46+
}
47+
48+
// successful CONNECT response
49+
fwrite($conn, "HTTP/1.0 200 Connection established\r\n\r\n");
50+
51+
fclose($conn);
52+
fclose($upstream);
53+
phpt_wait();
54+
CODE;
55+
56+
$clientCode = <<<'CODE'
57+
$clientCtx = stream_context_create([
58+
'ssl' => [
59+
'cafile' => __DIR__ . '/sni_server_ca.pem',
60+
'verify_peer' => true,
61+
'verify_peer_name' => true,
62+
],
63+
"http" => [
64+
"proxy" => "tcp://{{ ADDR }}"
65+
],
66+
]);
67+
68+
var_dump(file_get_contents("https://cs.php.net/", false, $clientCtx));
69+
70+
phpt_notify('proxy');
71+
phpt_notify('server');
72+
CODE;
73+
74+
include 'ServerClientTestCase.inc';
75+
ServerClientTestCase::getInstance()->run($clientCode, [
76+
'server' => $serverCode,
77+
'proxy' => $proxyCode,
78+
]);
79+
?>
80+
--EXPECTF--
81+
Warning: file_get_contents(https://cs.php.net/): Failed to open stream: Cannot connect to HTTPS server through proxy in %s
82+
bool(false)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEpQIBAAKCAQEAvy5NhzktzEdsHTbGB6vqYANms5rn1zXFmTJrGlWCwoIsNmTf
3+
ahvZkrC1cCXTZ7fbPB8XQbpAtz2ZSU7OcwBW9B8okYUPo9zi/ptwcrgsQsN0hrcD
4+
8MBRUccevwime5fLvg8E9RJ/68y9y3BnRcVWYO2sAK9juTfidNjETU3Bb05oXv8D
5+
SD/6onXQu4uXDgsQ3cRXeld9UB0xazmQXyyiIqXc/cpTAnaEVYzn28aj7NlUbzNq
6+
511UXMXY44x9EcXWpPVZ7heNcJNzY5DCNzmtXKrt9yiMpWQcPXEzsESVxAMqib9u
7+
TFOlvVX17LIPxBG656PjTD9J1h6kBbMCUxzs7wIDAQABAoIBAQC85lBeY0X4ST3v
8+
I7bJz7kWQ2YP4uhfAdeLhoDDFWjNLffniwYhfwEc6xNri0R2f/jUT9gX7qORKwEx
9+
qPdeNCC2t67LElGg1FlJv2Z9Q7MgCKYzkdQH5s6y4e9kTHTLO/JpiceZKz1QTQ3f
10+
XOH9032E6nIAf0wmr6xHTgOwajrN8VI5BuPEMVmEwIw3AtYeqVuPCNKyGR4HUVkC
11+
2bAydnGngbRJRnNzmKcWJancxpHDGBSFqPyuXMFC7Jgo3ZmyCbGp99vuXVk/sW9x
12+
5aj94M9nRE0guk05ivH2/JZao2uLYkIgjFWlhNxKdWgWRk8DEuN4djC8mKS9YH1q
13+
crYRToMhAoGBAOspUTtKP54mpZmyhxuDqj02JaJRzNTskPHsiF1UhtXuw7uT+ryV
14+
ekUFLNXoFmn9mbx1WVaUvGH4qjilvQOxz7u++lz0ApqJEfyM3jc/cC40Y5zcuGSu
15+
Etbg+SyDoytlgMCIydJyrS7NNALSo5p5oG6XY2f8yd/DCAmo8LzypaHRAoGBANAf
16+
R1SlBMc/bOsi6GrJxcBVSCFMiKYiO5woL5aUKa9yM+UQuQ/6xbQ7Q+sOlt0FH3xo
17+
AJ2L60qTdjyXVtjOdtXs5ZC4l+C6AfnCx6yLr+fNc4SOYXEfqS4LZylgwKd9KyVB
18+
asspIW9Idbgebmi6vPyt9LDkIp0h1VuFGjkvQJK/AoGBAI4pbS0dprXyARyYW6sb
19+
fpgAmuG099IkrT9DUfCx/81myTclr2fAKal+BmvOIXaz0/OlMXvw8K19iVIzh7+r
20+
B70lJ+93p/dKM/BsLI5TsHqOO0YB/QsIXOVAHgJ2FfdPJnW+e9vYba+kZ/Po6PSi
21+
4ITaykJ8BIJcQgis89QWEGFxAoGBAJhQO+jzuDKF9ZWEf6ofrw0anOZZ16wWY5/e
22+
PS2rk3JmVxpuibHrKqPDt+ogTELHDAsFJmYmz3VNxHuFmrajK49Wh4/JuMVr/CQo
23+
6+8YcA1qa/94IFIlBLDBAafjujsZvOjQHnM+z8xcsGKmStF00Pjv6qNG4xoyd646
24+
FD4DmfOLAoGAWXehpopZKXE9gRAni881ucK6WqxPPBoofbozi09D0MmfarIVaSkv
25+
jNVVHBfLWd7IEXTjiipPBeUqq6Jc3pscN1Vp4rrl8jTmVTdazEv0LuzpdUFqmNo2
26+
M+xw17uz9D9Q32/aW1Lar0PdIaL/wGEDEyzEBFwrGppcENLilPz8gzU=
27+
-----END RSA PRIVATE KEY-----
28+
-----BEGIN CERTIFICATE-----
29+
MIIFIjCCAwqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix
30+
EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w
31+
ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjEyWhcNMjYwNDAy
32+
MTgzNjEyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE
33+
CgwHUEhQLm5ldDETMBEGA1UEAwwKY3MucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB
34+
BQADggEPADCCAQoCggEBAL8uTYc5LcxHbB02xger6mADZrOa59c1xZkyaxpVgsKC
35+
LDZk32ob2ZKwtXAl02e32zwfF0G6QLc9mUlOznMAVvQfKJGFD6Pc4v6bcHK4LELD
36+
dIa3A/DAUVHHHr8IpnuXy74PBPUSf+vMvctwZ0XFVmDtrACvY7k34nTYxE1NwW9O
37+
aF7/A0g/+qJ10LuLlw4LEN3EV3pXfVAdMWs5kF8soiKl3P3KUwJ2hFWM59vGo+zZ
38+
VG8zauddVFzF2OOMfRHF1qT1We4XjXCTc2OQwjc5rVyq7fcojKVkHD1xM7BElcQD
39+
Kom/bkxTpb1V9eyyD8QRuuej40w/SdYepAWzAlMc7O8CAwEAAaOCAQkwggEFMAkG
40+
A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu
41+
U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFHPfd8dK
42+
Lz1R0Ck4WV1B9AWXd5DSMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa
43+
oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH
44+
UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG
45+
A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQB6WSIHEyDXLZxH
46+
hZjqSNQOA7Wc9Z2FCAiD29xYkGTL8WuPVGGP1mu4B92ytj+PMWwqSReDa7eTGLE7
47+
O7ozw9l+c+gNmHFNikSsGjlV2E8CToQOFMny+jAQYMSXf8UbTp9xDfgG02t/71hv
48+
SLWqdeHMLcR0xi0nBQH0vDOkwUbuWYqFa3jejHieGhykHM6CkIk6lqnyOEO+ooIF
49+
ZsLprrg1ss/mXCPI6niP0hze55ERKdxI7Rk8sZ4pVkf2SUWqZrUS0aJ+Ymmwi6Xd
50+
2V7izq5N30PkJS8MtqII4FAjRBIkwPh0sy8PmW/DzkYU+lYQnDfYLKDFKcj8xJK/
51+
o8oZUBsQltrSj0KlM9QuqxCTCBCy1nXZ9WHOhq+jdLiTc1Oi60uEHcUMrLK8aYc4
52+
HqIvZS6C2iwMI0d1OP3VxmAbMQ9yqRi+FbLYavJ3H40jrU9SYqdxa0BrTaz8MJNE
53+
6AEwgQDPChczSghvHME+Fs4mtGCY3TesbNZKVahQRjaFIhMZIZ4RP4CRc0bJOBG+
54+
8Me4+KHNsD2ki5b03wAN6C1P2QrMzI+gH9fXLZYp761ciDAsX6YIzrhHHYLxYpJH
55+
BkQKKs8dCQWE5IzgVrdlvC3Z1/l9om66wHqqx7nKnPfYs/Sfnwe9MpCD6xJrXiTm
56+
WS7NM6fbQpO9APNr7o0ZOjbbWFzlNw==
57+
-----END CERTIFICATE-----

ext/standard/http_fopen_wrapper.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,10 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
540540
smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1);
541541

542542
if (php_stream_write(stream, ZSTR_VAL(header.s), ZSTR_LEN(header.s)) != ZSTR_LEN(header.s)) {
543+
if (reset_ssl_peer_name) {
544+
php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name");
545+
}
546+
543547
php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy");
544548
php_stream_close(stream);
545549
stream = NULL;
@@ -561,16 +565,18 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
561565

562566
/* enable SSL transport layer */
563567
if (stream) {
568+
php_stream_context *old_context = PHP_STREAM_CONTEXT(stream);
569+
564570
if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 ||
565571
php_stream_xport_crypto_enable(stream, 1) < 0) {
566572
php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy");
567573
php_stream_close(stream);
568574
stream = NULL;
569575
}
570-
}
571576

572-
if (reset_ssl_peer_name) {
573-
php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name");
577+
if (reset_ssl_peer_name) {
578+
php_stream_context_unset_option(old_context, "ssl", "peer_name");
579+
}
574580
}
575581
}
576582

0 commit comments

Comments
 (0)