Commit 4e26dd0f795 for php.net
commit 4e26dd0f795f8467196424573b8ce0985930bee8
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Mon Aug 24 18:32:11 2026 -0400
ext/pdo: Bound-check column index in getColumnMeta()
PDOStatement::getColumnMeta() indexed stmt->columns as soon as the driver
hook reported success, without checking that the columns had been
described or that the index was in range, so pdo_odbc, whose hook always
reports success, read out of bounds and crashed. Raise SQLSTATE 07009 and
return false instead, matching what the drivers that bound-check the
index in their own hook already return.
Closes GH-23654
diff --git a/NEWS b/NEWS
index 5f598989c66..9a862b7e0e1 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,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)
+- PDO:
+ . Fixed PDOStatement::getColumnMeta() reading out of bounds for an invalid
+ column index. (Ilia Alshanetsky)
+
- Zip:
. Fixed ZipArchive::extractTo() ignoring files given in a non-list array.
(David Carlier)
diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c
index 51c2f58c6c2..8612a01e044 100644
--- a/ext/pdo/pdo_stmt.c
+++ b/ext/pdo/pdo_stmt.c
@@ -1700,6 +1700,13 @@ PHP_METHOD(PDOStatement, getColumnMeta)
RETURN_FALSE;
}
+ if (stmt->columns == NULL || colno >= stmt->column_count) {
+ zval_ptr_dtor(return_value);
+ ZVAL_UNDEF(return_value);
+ pdo_raise_impl_error(stmt->dbh, stmt, "07009", "invalid column index");
+ RETURN_FALSE;
+ }
+
/* add stock items */
col = &stmt->columns[colno];
add_assoc_str(return_value, "name", zend_string_copy(col->name));
diff --git a/ext/pdo_odbc/tests/getcolumnmeta_bounds.phpt b/ext/pdo_odbc/tests/getcolumnmeta_bounds.phpt
new file mode 100644
index 00000000000..192da0903ce
--- /dev/null
+++ b/ext/pdo_odbc/tests/getcolumnmeta_bounds.phpt
@@ -0,0 +1,49 @@
+--TEST--
+PDO_odbc getColumnMeta() bounds checking on unexecuted statements and invalid indexes
+--EXTENSIONS--
+pdo_odbc
+--SKIPIF--
+<?php
+try {
+ new Pdo\Odbc('odbc:Driver={SQLite3};Database=:memory:');
+} catch (Throwable $e) {
+ die('skip SQLite3 ODBC driver not available');
+}
+?>
+--FILE--
+<?php
+$pdo = new Pdo\Odbc('odbc:Driver={SQLite3};Database=:memory:',
+ null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
+$stmt = $pdo->prepare('SELECT 1 AS one');
+
+try {
+ var_dump($stmt->getColumnMeta(0));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+$stmt->execute();
+var_dump($stmt->getColumnMeta(0));
+
+try {
+ var_dump($stmt->getColumnMeta(5));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+echo "done\n";
+?>
+--EXPECTF--
+PDOException: SQLSTATE[07009]: Invalid descriptor index: invalid column index
+array(4) {
+ ["pdo_type"]=>
+ int(2)
+ ["name"]=>
+ string(3) "one"
+ ["len"]=>
+ int(%d)
+ ["precision"]=>
+ int(%d)
+}
+PDOException: SQLSTATE[07009]: Invalid descriptor index: invalid column index
+done