[php-src] PHP-8.5: Merge branch 'PHP-8.4' into 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:55:29-04:00
Commit: https://github.com/php/php-src/commit/9a479812aee620a7403ae0ae4081b30ee43f8fd4
Raw diff: https://github.com/php/php-src/commit/9a479812aee620a7403ae0ae4081b30ee43f8fd4.diff
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
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 2c88dee0b23c..a954be2e35c2 100644
--- a/NEWS
+++ b/NEWS
@@ -72,6 +72,8 @@ PHP NEWS
carries no credentials). (iliaal)
. 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)
- Phar:
. Fixed inconsistent handling of the magic ".phar" directory. Paths such as
diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c
index 4aca921f01a0..a0b008cdd9d9 100644
--- a/ext/pdo_odbc/odbc_stmt.c
+++ b/ext/pdo_odbc/odbc_stmt.c
@@ -358,6 +358,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);
@@ -382,6 +383,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;
}
}
@@ -479,10 +481,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;
}
@@ -504,6 +512,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 473d70ff7076..ea93c58b2b60 100644
--- a/ext/pdo_odbc/php_pdo_odbc_int.h
+++ b/ext/pdo_odbc/php_pdo_odbc_int.h
@@ -153,6 +153,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"