Commit 345b88deebf for php.net

commit 345b88deebf59574023020f5c1feebabfe0767f3
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Fri Jul 10 08:32:15 2026 -0400

    Fix GH-22665: pdo_odbc OOB write on oversized diagnostic length

    pdo_odbc_error() writes the NUL terminator at
    einfo->last_err_msg[errmsgsize], where errmsgsize is the diagnostic length
    SQLGetDiagRec reports. On a truncated message (SQL_SUCCESS_WITH_INFO) the
    driver reports the full untruncated length, which can reach or exceed the
    512-byte buffer, so the terminator lands out of bounds in the adjacent
    last_error field. Clamp the index to sizeof(last_err_msg) - 1; the size_t
    cast also folds a negative driver-reported length into the same guard.

    Fixes GH-22665
    Closes GH-22675

diff --git a/NEWS b/NEWS
index 76be38bcaa7..70dd9bce1c4 100644
--- a/NEWS
+++ b/NEWS
@@ -53,6 +53,8 @@ PHP                                                                        NEWS
     driver-reported display size). (iliaal)
   . Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is
     longer than the declared maxlen). (iliaal)
+  . Fixed bug GH-22665 (Out-of-bounds write when the ODBC driver reports a
+    diagnostic message length beyond the error buffer). (iliaal)

 - Phar:
   . Fixed inconsistent handling of the magic ".phar" directory. Paths such as
diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c
index 2cdac30223c..3bb028ed631 100644
--- a/ext/pdo_odbc/odbc_driver.c
+++ b/ext/pdo_odbc/odbc_driver.c
@@ -90,6 +90,8 @@ void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement,

 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
 		errmsgsize = 0;
+	} else if ((size_t) errmsgsize >= sizeof(einfo->last_err_msg)) {
+		errmsgsize = sizeof(einfo->last_err_msg) - 1;
 	}

 	einfo->last_err_msg[errmsgsize] = '\0';
diff --git a/ext/pdo_odbc/tests/gh22665.phpt b/ext/pdo_odbc/tests/gh22665.phpt
new file mode 100644
index 00000000000..d6edc62288e
--- /dev/null
+++ b/ext/pdo_odbc/tests/gh22665.phpt
@@ -0,0 +1,32 @@
+--TEST--
+GH-22665 (OOB write in pdo_odbc_error when the driver reports a long message)
+--EXTENSIONS--
+pdo_odbc
+--SKIPIF--
+<?php
+require __DIR__ . '/config.inc';
+try {
+    $pdo = new PDO(PDO_ODBC_SQLITE_DSN);
+} catch (PDOException $e) {
+    die("skip requires the SQLite3 ODBC driver");
+}
+?>
+--FILE--
+<?php
+require __DIR__ . '/config.inc';
+$pdo = new PDO(PDO_ODBC_SQLITE_DSN, null, null, [
+    PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
+]);
+
+// A failing query with a long identifier makes the driver report a diagnostic
+// message length >= the fixed last_err_msg buffer. The terminator write must
+// stay inside the buffer.
+$pdo->query('SELECT * FROM "' . str_repeat('A', 4096) . '"');
+$info = $pdo->errorInfo();
+
+echo "sqlstate: ", $info[0], "\n";
+echo "done\n";
+?>
+--EXPECT--
+sqlstate: HY000
+done