[php-src] Issue #24609: mysqlnd: OK packets without info message rejected on x32 builds since GHSA-r6x9-5r99-36j7 (size_t truncation)
| From: | themofleur | Date: | Fri, 23 Oct 2026 16:14:34 +0000 |
| Subject: | [php-src] Issue #24609: mysqlnd: OK packets without info message rejected on x32 builds since GHSA-r6x9-5r99-36j7 (size_t truncation) | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253135@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24609
Author: themofleur
### Description
Since the fix for GHSA-r6x9-5r99-36j7, every statement answered by an OK packet without an info
message (SET NAMES, SET, DO, single-row INSERT or DELETE, TRUNCATE...) fails on x32 builds (ILP32
ABI on x86_64). x86_64 builds of the same versions are not affected. On our hosting, all CLI cron
jobs of seven sites have been failing since the CLI binary moved to PHP 8.3.36 (x32 build). The
hosting provider reproduced the error and confirmed that it only happens with the x32 build.
The following code:
```php
<?php
mysqli_report(MYSQLI_REPORT_OFF);
$link = mysqli_connect('127.0.0.1', 'user', 'password',
'test');
var_dump(mysqli_set_charset($link, 'utf8'));
```
Resulted in this output (x32 build):
```
Warning: mysqli_set_charset(): RSET_HEADER packet additional data length is past 4294967294 bytes
the packet size in %s on line 4
Warning: mysqli_set_charset(): Error reading result set's header in %s on line 4
bool(false)
```
Every following query on the same connection then fails with "Commands out of sync; you
can't run this command now".
But I expected this output instead:
```
bool(true)
```
Root cause: in ext/mysqlnd/mysqlnd_wireprotocol.c, php_mysqlnd_rset_header_read() declares
size_t len; (line 1130 on master) and stores in it the zend_ulong returned by
php_mysqlnd_net_field_length(). On x32, __x86_64__ is defined, so zend_ulong is 64-bit
(Zend/zend_long.h) while size_t is 32-bit. For a minimal OK packet, php_mysqlnd_net_field_length()
is called with remaining_size == 0 and returns MYSQLND_INVALID_NET_FIELD_LENGTH ((zend_ulong) -2).
Stored in len, it becomes 0xFFFFFFFE, so the check if (len && len !=
MYSQLND_INVALID_NET_FIELD_LENGTH) (line 1203 on master) no longer recognizes it, and the
packet is rejected with an "over-read" of exactly 4294967294 bytes. The other callers of
php_mysqlnd_net_field_length() store the result in a zend_ulong and are not affected. i686 builds
should not be affected either, since zend_ulong and size_t are both 32-bit there.
Suggested fix (not compiled on my side, I cannot build for x32):
```diff
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ php_mysqlnd_rset_header_read
- size_t len;
+ zend_ulong len;
```
len is only used afterwards for values bounded by packet->header.size (LOAD LOCAL
file name and info message), so widening it is safe on all platforms.
A regression test could use ext/mysqli/tests/fake_server.inc to answer a COM_QUERY with the minimal
OK packet "\x00\x00\x00\x02\x00\x00\x00". I also have a standalone script (fake MySQL
server started by the script itself, no database needed) and can share it if useful.
### PHP Version
```plain
PHP 8.3.36 (x32 build). Same code in PHP 8.2.34+, 8.3.35+, 8.4.26+, 8.5.11+ and master.
```
### Operating System
Linux, x32 ABI (ILP32 on x86_64). x86_64 builds are not affected.