Commit 4a4ac3bcb08 for php.net
commit 4a4ac3bcb0893d27aa20da321ef562ac5bbf1431
Author: Kamil Tekiela <tekiela246@gmail.com>
Date: Mon Sep 21 17:20:55 2026 +0100
Fix GH-22854 (Assertion failure at link_errno_read in ext/mysqli/mysqli_prop.c) (#20117)
diff --git a/NEWS b/NEWS
index 54b6a5d174e..639a784a5a9 100644
--- a/NEWS
+++ b/NEWS
@@ -38,6 +38,10 @@ PHP NEWS
. Fixed bug GH-23106 (mb_strpos() reads past the end of a haystack ending in
a truncated UTF-8 sequence). (Lazizbek Ergashev)
+- MySQLi:
+ . Fix GH-22854: Fixed failed assertion when accessing mysqli property after
+ failed reconnection. (Kamil Tekiela)
+
- Opcache:
. Fixed OSS-Fuzz #546798343 (Heap-buffer-overflow in optimizer with
FCCs and inlining). (ndossche)
diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c
index 6f2aa50117a..bec7047571e 100644
--- a/ext/mysqli/mysqli_nonapi.c
+++ b/ext/mysqli/mysqli_nonapi.c
@@ -134,6 +134,8 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, bool is_real_connect, b
{
/* already connected, we should close the connection */
php_mysqli_close(mysql, MYSQLI_CLOSE_IMPLICIT, mysqli_resource->status);
+ /* the link is not connected anymore, even if the new connection fails */
+ mysqli_resource->status = MYSQLI_STATUS_INITIALIZED;
}
if (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)) {
diff --git a/ext/mysqli/mysqli_prop.c b/ext/mysqli/mysqli_prop.c
index 6d86a6ad4ca..a6165ab668c 100644
--- a/ext/mysqli/mysqli_prop.c
+++ b/ext/mysqli/mysqli_prop.c
@@ -43,6 +43,12 @@ if (!obj->ptr || !(MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr) { \
} else { \
CHECK_STATUS(statusval, quiet);\
p = (MYSQL *)((MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr)->mysql;\
+ if (!p) { \
+ if (!quiet) { \
+ zend_throw_error(NULL, "%s object is not fully initialized", ZSTR_VAL(obj->zo.ce->name)); \
+ } \
+ return FAILURE; \
+ } \
}
#define MYSQLI_GET_RESULT(statusval) \
diff --git a/ext/mysqli/tests/gh22854.phpt b/ext/mysqli/tests/gh22854.phpt
new file mode 100644
index 00000000000..25714a8a962
--- /dev/null
+++ b/ext/mysqli/tests/gh22854.phpt
@@ -0,0 +1,83 @@
+--TEST--
+GH-22854 (Assertion failure at link_errno_read in ext/mysqli/mysqli_prop.c)
+--EXTENSIONS--
+mysqli
+--SKIPIF--
+<?php
+require_once 'skipifconnectfailure.inc';
+?>
+--FILE--
+<?php
+include 'connect.inc';
+
+mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
+
+function read_properties(mysqli $mysqli, array $properties): void {
+ foreach ($properties as $property) {
+ try {
+ $mysqli->$property;
+ echo "$property: no error\n";
+ } catch (Error $e) {
+ echo "$property: " . $e->getMessage() . "\n";
+ }
+ }
+
+ // var_dump() reads every property in quiet mode
+ ob_start();
+ var_dump($mysqli);
+ ob_end_clean();
+ echo "var_dump() done\n";
+}
+
+$links = mysqli_get_links_stats()['total'];
+
+echo "connect()\n";
+$mysqli = new mysqli();
+$mysqli->connect($host, $user, $passwd, $db, $port, $socket);
+echo 'Success... ' . $mysqli->host_info . "\n";
+try {
+ $mysqli->connect($host, $user, $passwd, $db.'wrong', $port, $socket);
+} catch (mysqli_sql_exception $e) {
+ echo "Error: " . $e->getMessage() . "\n";
+}
+read_properties($mysqli, ['host_info', 'affected_rows', 'error_list', 'errno', 'error']);
+unset($mysqli);
+
+echo "\nreal_connect()\n";
+$mysqli = new mysqli();
+$mysqli->connect($host, $user, $passwd, $db, $port, $socket);
+echo 'Success... ' . $mysqli->host_info . "\n";
+try {
+ $mysqli->real_connect($host, $user, $passwd, $db.'wrong', $port, $socket);
+} catch (mysqli_sql_exception $e) {
+ echo "Error: " . $e->getMessage() . "\n";
+}
+read_properties($mysqli, ['host_info', 'server_info', 'affected_rows', 'error_list']);
+unset($mysqli);
+
+// A failed reconnect must leave the link count balanced (it used to be decremented twice)
+var_dump(mysqli_get_links_stats()['total'] === $links);
+
+print "done!";
+?>
+--EXPECTF--
+connect()
+Success... %s via %s
+Error: Unknown database '%s'
+host_info: Property access is not allowed yet
+affected_rows: Property access is not allowed yet
+error_list: Property access is not allowed yet
+errno: mysqli object is not fully initialized
+error: mysqli object is not fully initialized
+var_dump() done
+
+real_connect()
+Success... %s via %s
+Error: Unknown database '%s'
+host_info: Property access is not allowed yet
+server_info: Property access is not allowed yet
+affected_rows: Property access is not allowed yet
+error_list: Property access is not allowed yet
+var_dump() done
+bool(true)
+done!