summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/mysqli/tests/bug69899.phpt38
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c5
2 files changed, 41 insertions, 2 deletions
diff --git a/ext/mysqli/tests/bug69899.phpt b/ext/mysqli/tests/bug69899.phpt
new file mode 100644
index 0000000000..177b5e3dce
--- /dev/null
+++ b/ext/mysqli/tests/bug69899.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Bug #69899: Segfault on stmt close after free_result with mysqlnd.
+--DESCRIPTION--
+The segfault happens only if the database connection was already closed and
+free_result is called on a prepared statement followed by closing that
+statement. This is due to mysqlnd_stmt::free_result (mysqlnd_ps.c) which
+unconditionally sets the connection of the statement to ready, despite the fact
+that it might already be closed.
+--SKIPIF--
+<?php
+require_once __DIR__ . '/skipif.inc';
+require_once __DIR__ . '/skipifconnectfailure.inc';
+require_once __DIR__ . '/connect.inc';
+if (!$IS_MYSQLND) {
+ die('mysqlnd only');
+}
+?>
+--FILE--
+<?php
+
+require_once __DIR__ . '/connect.inc';
+
+mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
+
+$mysqli = new mysqli($host, $user, $passwd, $db, $port, $socket);
+$stmt = $mysqli->prepare('SELECT 1');
+
+var_dump(
+ $mysqli->close(),
+ $stmt->free_result(),
+ $stmt->close()
+);
+
+?>
+--EXPECT--
+bool(true)
+NULL
+bool(true)
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index 670ea99038..2631e4a797 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -2005,8 +2005,9 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s)
stmt->state = MYSQLND_STMT_PREPARED;
}
- /* Line is free! */
- CONN_SET_STATE(stmt->conn, CONN_READY);
+ if (CONN_GET_STATE(stmt->conn) != CONN_QUIT_SENT) {
+ CONN_SET_STATE(stmt->conn, CONN_READY);
+ }
DBG_RETURN(PASS);
}