[php-src] master: Merge branch 'PHP-8.5'

Ilia Alshanetsky <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Date: 2026-07-14T15:56:05-04:00

Commit: https://github.com/php/php-src/commit/3a40e29e084f0284a5d929daff26b772f2a05de9
Raw diff: https://github.com/php/php-src/commit/3a40e29e084f0284a5d929daff26b772f2a05de9.diff

Merge branch 'PHP-8.5'

* PHP-8.5:
  Fix GH-22666: pdo_odbc heap overflow on oversized output param

Changed paths:
  A  ext/pdo_odbc/tests/gh22666.phpt
  M  NEWS
  M  ext/pdo_odbc/odbc_stmt.c
  M  ext/pdo_odbc/php_pdo_odbc_int.h


Diff:

diff --git a/NEWS b/NEWS
index 793e0966dfbb..367860feb471 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,8 @@ PHP                                                                        NEWS
 - PDO_ODBC:
   . Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the
     driver-reported display size). (iliaal)
+  . Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is
+    longer than the declared maxlen). (iliaal)
 
 - Reflection:
   . Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes).
diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c
index 8d39a723f756..e628ce58086c 100644
--- a/ext/pdo_odbc/odbc_stmt.c
+++ b/ext/pdo_odbc/odbc_stmt.c
@@ -356,6 +356,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
 				param->driver_data = P;
 
 				P->len = 0; /* is re-populated each EXEC_PRE */
+				P->outbuflen = 0;
 				P->outbuf = NULL;
 
 				P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);
@@ -380,6 +381,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
 							P->len *= 2;
 						}
 						P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
+						P->outbuflen = P->len;
 					}
 				}
 
@@ -477,10 +479,16 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
 							case PDO_ODBC_CONV_FAIL:
 							case PDO_ODBC_CONV_NOT_REQUIRED:
 								P->len = Z_STRLEN_P(parameter);
+								if (P->len > P->outbuflen) {
+									P->len = P->outbuflen;
+								}
 								memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);
 								break;
 							case PDO_ODBC_CONV_OK:
 								P->len = ulen;
+								if (P->len > P->outbuflen) {
+									P->len = P->outbuflen;
+								}
 								memcpy(P->outbuf, S->convbuf, P->len);
 								break;
 						}
@@ -502,6 +510,9 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
 					zval_ptr_dtor(parameter);
 
 					if (P->len >= 0) {
+							if (P->len > P->outbuflen) {
+								P->len = P->outbuflen;
+							}
 							ZVAL_STRINGL(parameter, P->outbuf, P->len);
 							switch (pdo_odbc_ucs22utf8(P->is_unicode, parameter)) {
 								case PDO_ODBC_CONV_FAIL:
diff --git a/ext/pdo_odbc/php_pdo_odbc_int.h b/ext/pdo_odbc/php_pdo_odbc_int.h
index 18b45af21a02..3ebfadb57523 100644
--- a/ext/pdo_odbc/php_pdo_odbc_int.h
+++ b/ext/pdo_odbc/php_pdo_odbc_int.h
@@ -151,6 +151,7 @@ typedef struct {
 
 typedef struct {
 	SQLLEN len;
+	SQLLEN outbuflen;
 	SQLSMALLINT paramtype;
 	char *outbuf;
 	unsigned is_unicode:1;
diff --git a/ext/pdo_odbc/tests/gh22666.phpt b/ext/pdo_odbc/tests/gh22666.phpt
new file mode 100644
index 000000000000..b6ba2265b9be
--- /dev/null
+++ b/ext/pdo_odbc/tests/gh22666.phpt
@@ -0,0 +1,30 @@
+--TEST--
+GH-22666 (Heap buffer overflow when an output param value exceeds its maxlen)
+--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);
+
+// An INPUT_OUTPUT parameter declares a maxlen of 4, so the output buffer is 4
+// bytes, but the runtime value is longer. The copy into that buffer must be
+// bounded to the declared capacity, not overflow it.
+$value = str_repeat('A', 64);
+$stmt = $pdo->prepare('SELECT ?');
+$stmt->bindParam(1, $value, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 4);
+$stmt->execute();
+
+echo "bounded to maxlen: "; var_dump($value);
+?>
+--EXPECT--
+bounded to maxlen: string(4) "AAAA"
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.