Commit 9e5cf96095c for php.net

commit 9e5cf96095ca54543c68787092cc28fcc4bc534d
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Sat Aug 29 08:00:20 2026 -0400

    [mysqlnd] Fix OK packet message length buffer over-read

    The OK packet message-length varint is read after the last bounds check,
    so a length-encoded integer at the end of a packet can advance p past
    header.size and even past the end of the 4096-byte command buffer. The
    old MIN(net_len, buf_len - (p - begin)) clamp then underflows and passes
    an unclamped attacker-controlled length to mnd_pestrndup(), reading heap
    memory beyond both the packet and its allocation. Reject a message length
    that extends past the payload, matching php_mysqlnd_auth_response_read()
    from GHSA-h35g-vwh6-m678; an audit found no further readers using the
    vulnerable buf_len clamp.

    Closes GH-23497

diff --git a/ext/mysqli/tests/fake_server.inc b/ext/mysqli/tests/fake_server.inc
index dad8bc52ddd..4056b9fb78c 100644
--- a/ext/mysqli/tests/fake_server.inc
+++ b/ext/mysqli/tests/fake_server.inc
@@ -721,6 +721,19 @@ function my_mysqli_test_auth_response_message_over_read(my_mysqli_fake_server_co
     $conn->read();
 }

+function my_mysqli_test_ok_packet_message_over_read(my_mysqli_fake_server_conn $conn): void
+{
+    $p = new my_mysqli_fake_packet();
+    $p->full = "08000001" . "00" . "00" . "00" . "0200" . "0000" . "fa";
+
+    $conn->send_server_greetings();
+    $conn->read_packets(1);
+    $conn->send_server_ok();
+    $conn->read_packets(1);
+    $conn->send($p->to_bytes(), "Malicious OK Packet [message length past the packet size]");
+    $conn->read();
+}
+
 function my_mysqli_test_stmt_response_row_over_read_string(my_mysqli_fake_server_conn $conn): void
 {
     $rh = $conn->packet_generator->server_stmt_execute_items_response();
diff --git a/ext/mysqli/tests/mysqlnd_ok_packet_message_over_read.phpt b/ext/mysqli/tests/mysqlnd_ok_packet_message_over_read.phpt
new file mode 100644
index 00000000000..8364251e8df
--- /dev/null
+++ b/ext/mysqli/tests/mysqlnd_ok_packet_message_over_read.phpt
@@ -0,0 +1,40 @@
+--TEST--
+mysqlnd OK packet message length buffer over-read
+--EXTENSIONS--
+mysqli
+--FILE--
+<?php
+require_once 'fake_server.inc';
+
+$servername = "127.0.0.1";
+$username = "root";
+$password = "";
+
+$process = run_fake_server_in_background('ok_packet_message_over_read');
+$process->wait();
+
+try {
+    $conn = new mysqli( $servername, $username, $password, "", $process->getPort());
+    var_dump($conn->select_db("test"));
+} catch (Exception $e) {
+    echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+$process->terminate();
+
+print "done!";
+?>
+--EXPECTF--
+[*] Server started on 127.0.0.1:%d
+[*] Connection established
+[*] Sending - Server Greeting: %s
+[*] Received: %s
+[*] Sending - Server OK: %s
+[*] Received: %s
+[*] Sending - Malicious OK Packet [message length past the packet size]: %s
+
+Warning: mysqli::select_db(): OK packet message length is past the packet size in %s on line %d
+
+Warning: mysqli::select_db(): Error while reading INIT_DB's response packet. PID=%d in %s on line %d
+mysqli_sql_exception: Malformed packet
+done!
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index 64c2c796961..80b4b37591a 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -878,7 +878,12 @@ php_mysqlnd_ok_read(MYSQLND_CONN_DATA * conn, void * _packet)

 	/* There is a message */
 	if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
-		packet->message_len = MIN(net_len, buf_len - (p - begin));
+		if ((p - buf) > packet->header.size || packet->header.size - (p - buf) < net_len) {
+			DBG_ERR_FMT("OK packet message length is past the packet size");
+			php_error_docref(NULL, E_WARNING, "OK packet message length is past the packet size");
+			DBG_RETURN(FAIL);
+		}
+		packet->message_len = net_len;
 		packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
 	} else {
 		packet->message = NULL;