[PHP-CVS] [php-src] master: Merge branch 'PHP-8.5'

[email protected] (David Carlier)
Newsgroups php.cvs
Message-ID <[email protected]>
Author: David Carlier (devnexen)
Date: 2026-08-24T17:00:00+01:00

Commit: https://github.com/php/php-src/commit/74d49165e48c502578fafcf286bb3eaa7e30f7e3
Raw diff: https://github.com/php/php-src/commit/74d49165e48c502578fafcf286bb3eaa7e30f7e3.diff

Merge branch 'PHP-8.5'

* PHP-8.5:
  sapi/cli: check php_cli_server_client_send_through() return value

Changed paths:
  A  sapi/cli/tests/gh23425.phpt
  M  sapi/cli/php_cli_server.c


Diff:

diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c
index 007718e37b9c..5df12648ca84 100644
--- a/sapi/cli/php_cli_server.c
+++ b/sapi/cli/php_cli_server.c
@@ -175,6 +175,7 @@ typedef struct php_cli_server_client {
 	php_http_parser parser;
 	bool request_read;
 	bool too_large_post;
+	bool headers_written;
 	zend_string *current_header_name;
 	zend_string *current_header_value;
 	enum { HEADER_NONE=0, HEADER_FIELD, HEADER_VALUE } last_header_element;
@@ -544,7 +545,7 @@ static int sapi_cli_server_send_headers(sapi_headers_struct *sapi_headers) /* {{
 	sapi_header_struct *h;
 	zend_llist_position pos;
 
-	if (client == NULL || SG(request_info).no_headers) {
+	if (client == NULL || SG(request_info).no_headers || client->headers_written) {
 		return SAPI_HEADER_SENT_SUCCESSFULLY;
 	}
 
@@ -567,10 +568,12 @@ static int sapi_cli_server_send_headers(sapi_headers_struct *sapi_headers) /* {{
 	}
 	smart_str_appendl(&buffer, "\r\n", 2);
 
-	php_cli_server_client_send_through(client, ZSTR_VAL(buffer.s), ZSTR_LEN(buffer.s));
+	size_t buffer_len = ZSTR_LEN(buffer.s);
+	bool sent = php_cli_server_client_send_through(client, ZSTR_VAL(buffer.s), buffer_len) == buffer_len;
 
+	client->headers_written = true;
 	smart_str_free(&buffer);
-	return SAPI_HEADER_SENT_SUCCESSFULLY;
+	return sent ? SAPI_HEADER_SENT_SUCCESSFULLY : SAPI_HEADER_SEND_FAILED;
 }
 /* }}} */
 
@@ -1927,11 +1930,11 @@ static size_t php_cli_server_client_send_through(php_cli_server_client *client,
 				} else {
 					/* error or timeout */
 					php_handle_aborted_connection();
-					return nbytes_left;
+					return str_len - nbytes_left;
 				}
 			} else {
 				php_handle_aborted_connection();
-				return nbytes_left;
+				return str_len - nbytes_left;
 			}
 		}
 		nbytes_left -= nbytes_sent;
@@ -1981,6 +1984,7 @@ static void php_cli_server_client_ctor(php_cli_server_client *client, php_cli_se
 	php_http_parser_init(&client->parser, PHP_HTTP_REQUEST);
 	client->request_read = false;
 	client->too_large_post = false;
+	client->headers_written = false;
 
 	client->last_header_element = HEADER_NONE;
 	client->current_header_name = NULL;
diff --git a/sapi/cli/tests/gh23425.phpt b/sapi/cli/tests/gh23425.phpt
new file mode 100644
index 000000000000..98ed07859174
--- /dev/null
+++ b/sapi/cli/tests/gh23425.phpt
@@ -0,0 +1,40 @@
+--TEST--
+GH-23425 (sapi_cli_server_send_headers() does not check the return value of php_cli_server_client_send_through())
+--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";
+
+$info = php_cli_server_start(<<<'PHP'
+ignore_user_abort(true);
+usleep(300000);
+header('X-Test: 1');
+echo 'x';
+file_put_contents(__DIR__ . '/result.txt', headers_sent() ? 'sent' : 'not-sent');
+PHP);
+
+// Connect the usual way, then drop to the socket extension only to force a
+// hard reset (SO_LINGER=0) instead of a graceful close, so the server's
+// header write fails deterministically while the script is still running
+// (ignore_user_abort(true)).
+$stream = stream_socket_client("tcp://" . PHP_CLI_SERVER_ADDRESS);
+$sock = socket_import_stream($stream);
+socket_write($sock, "GET /index.php HTTP/1.1\r\nHost: " . PHP_CLI_SERVER_HOSTNAME . "\r\nConnection: close\r\n\r\n");
+socket_set_option($sock, SOL_SOCKET, SO_LINGER, ['l_onoff' => 1, 'l_linger' => 0]);
+socket_close($sock);
+
+$result_file = $info->docRoot . '/result.txt';
+for ($i = 0; $i < 40 && !file_exists($result_file); $i++) {
+    usleep(50000);
+}
+
+echo file_get_contents($result_file), "\n";
+?>
+--EXPECT--
+not-sent
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.