Skip to content

Commit 3a40e29

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Fix phpGH-22666: pdo_odbc heap overflow on oversized output param
2 parents df2c6bb + 9a47981 commit 3a40e29

4 files changed

Lines changed: 44 additions & 0 deletions

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ PHP NEWS
99
- PDO_ODBC:
1010
. Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the
1111
driver-reported display size). (iliaal)
12+
. Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is
13+
longer than the declared maxlen). (iliaal)
1214

1315
- Reflection:
1416
. Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes).

ext/pdo_odbc/odbc_stmt.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
356356
param->driver_data = P;
357357

358358
P->len = 0; /* is re-populated each EXEC_PRE */
359+
P->outbuflen = 0;
359360
P->outbuf = NULL;
360361

361362
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
380381
P->len *= 2;
381382
}
382383
P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
384+
P->outbuflen = P->len;
383385
}
384386
}
385387

@@ -477,10 +479,16 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
477479
case PDO_ODBC_CONV_FAIL:
478480
case PDO_ODBC_CONV_NOT_REQUIRED:
479481
P->len = Z_STRLEN_P(parameter);
482+
if (P->len > P->outbuflen) {
483+
P->len = P->outbuflen;
484+
}
480485
memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);
481486
break;
482487
case PDO_ODBC_CONV_OK:
483488
P->len = ulen;
489+
if (P->len > P->outbuflen) {
490+
P->len = P->outbuflen;
491+
}
484492
memcpy(P->outbuf, S->convbuf, P->len);
485493
break;
486494
}
@@ -502,6 +510,9 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
502510
zval_ptr_dtor(parameter);
503511

504512
if (P->len >= 0) {
513+
if (P->len > P->outbuflen) {
514+
P->len = P->outbuflen;
515+
}
505516
ZVAL_STRINGL(parameter, P->outbuf, P->len);
506517
switch (pdo_odbc_ucs22utf8(P->is_unicode, parameter)) {
507518
case PDO_ODBC_CONV_FAIL:

ext/pdo_odbc/php_pdo_odbc_int.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ typedef struct {
151151

152152
typedef struct {
153153
SQLLEN len;
154+
SQLLEN outbuflen;
154155
SQLSMALLINT paramtype;
155156
char *outbuf;
156157
unsigned is_unicode:1;

ext/pdo_odbc/tests/gh22666.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
GH-22666 (Heap buffer overflow when an output param value exceeds its maxlen)
3+
--EXTENSIONS--
4+
pdo_odbc
5+
--SKIPIF--
6+
<?php
7+
require __DIR__ . '/config.inc';
8+
try {
9+
$pdo = new PDO(PDO_ODBC_SQLITE_DSN);
10+
} catch (PDOException $e) {
11+
die("skip requires the SQLite3 ODBC driver");
12+
}
13+
?>
14+
--FILE--
15+
<?php
16+
require __DIR__ . '/config.inc';
17+
$pdo = new PDO(PDO_ODBC_SQLITE_DSN);
18+
19+
// An INPUT_OUTPUT parameter declares a maxlen of 4, so the output buffer is 4
20+
// bytes, but the runtime value is longer. The copy into that buffer must be
21+
// bounded to the declared capacity, not overflow it.
22+
$value = str_repeat('A', 64);
23+
$stmt = $pdo->prepare('SELECT ?');
24+
$stmt->bindParam(1, $value, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 4);
25+
$stmt->execute();
26+
27+
echo "bounded to maxlen: "; var_dump($value);
28+
?>
29+
--EXPECT--
30+
bounded to maxlen: string(4) "AAAA"

0 commit comments

Comments
 (0)