Commit 83c59b80530 for php
commit 83c59b80530d51f16d8af852c92e13e3dd49fb0d
Author: David Carlier <devnexen@gmail.com>
Date: Tue Sep 22 11:05:25 2026 +0100
sapi/cli: Fix crash in the CLI server when a client is reset before being accepted
accept() can return a socket without filling in the peer address when the
client has already reset the connection. The address buffer was left
uninitialized and php_network_populate_name_from_sockaddr() does not set the
output string for an unknown family, so zend_string_dup() was handed a NULL.
Zero the buffer and fall back to "-" when the address is unknown.
Close GH-23840
diff --git a/NEWS b/NEWS
index ba4baf65461..f61ceb97aa0 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ PHP NEWS
request activation). (matyhtf)
. Fixed bug GH-23764 (Built-in server leaks a file descriptor on every HEAD
request for a static file). (jakubskopal)
+ . Fixed crash in the built-in server when a client is reset before being
+ accepted. (David Carlier)
- Core
. Fix GH-21999: GC inconsistency with lazy object, var_dump(), and object
diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c
index dcd3d4a7e04..1b33ceaf70b 100644
--- a/sapi/cli/php_cli_server.c
+++ b/sapi/cli/php_cli_server.c
@@ -1970,9 +1970,13 @@ static void php_cli_server_client_ctor(php_cli_server_client *client, php_cli_se
// Create a new php_network_populate_name_from_sockaddr_ex() API with a persistent flag?
zend_string *tmp_addr = NULL;
php_network_populate_name_from_sockaddr(addr, addr_len, &tmp_addr, NULL, 0);
- client->addr_str = zend_string_dup(tmp_addr, /* persistent */ true);
+ if (EXPECTED(tmp_addr != NULL)) {
+ client->addr_str = zend_string_dup(tmp_addr, /* persistent */ true);
+ zend_string_release_ex(tmp_addr, /* persistent */ false);
+ } else {
+ client->addr_str = zend_string_init(ZEND_STRL("-"), /* persistent */ true);
+ }
GC_MAKE_PERSISTENT_LOCAL(client->addr_str);
- zend_string_release_ex(tmp_addr, /* persistent */ false);
php_http_parser_init(&client->parser, PHP_HTTP_REQUEST);
client->request_read = false;
@@ -2717,7 +2721,7 @@ static zend_result php_cli_server_do_event_for_each_fd_callback(void *_params, p
php_cli_server_client *client = NULL;
php_socket_t client_sock;
socklen_t socklen = server->socklen;
- struct sockaddr *sa = pemalloc(server->socklen, 1);
+ struct sockaddr *sa = pecalloc(1, server->socklen, 1);
client_sock = accept(server->server_sock, sa, &socklen);
if (!ZEND_VALID_SOCKET(client_sock)) {
pefree(sa, 1);
diff --git a/sapi/cli/tests/php_cli_server_reset_before_accept.phpt b/sapi/cli/tests/php_cli_server_reset_before_accept.phpt
new file mode 100644
index 00000000000..b8f31ba5a63
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_reset_before_accept.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Connection reset before it is accepted does not take the server down
+--EXTENSIONS--
+sockets
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (PHP_OS_FAMILY === "Windows") die("skip SO_LINGER reset behaviour differs on Windows");
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start();
+
+$fp = php_cli_server_connect();
+socket_set_option(socket_import_stream($fp), SOL_SOCKET, SO_LINGER, ['l_onoff' => 1, 'l_linger' => 0]);
+stream_socket_shutdown($fp, STREAM_SHUT_RD);
+fclose($fp);
+
+$fp = php_cli_server_connect();
+fwrite($fp, "GET / HTTP/1.1\r\nConnection: close\r\n\r\n");
+echo fgets($fp);
+fclose($fp);
+?>
+--EXPECT--
+HTTP/1.1 200 OK