Skip to content

Commit 9a47981

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: Fix phpGH-22666: pdo_odbc heap overflow on oversized output param
2 parents 0c11f48 + a7539ff commit 9a47981

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
@@ -72,6 +72,8 @@ PHP NEWS
7272
carries no credentials). (iliaal)
7373
. Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the
7474
driver-reported display size). (iliaal)
75+
. Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is
76+
longer than the declared maxlen). (iliaal)
7577

7678
- Phar:
7779
. Fixed inconsistent handling of the magic ".phar" directory. Paths such as

ext/pdo_odbc/odbc_stmt.c

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

360360
P->len = 0; /* is re-populated each EXEC_PRE */
361+
P->outbuflen = 0;
361362
P->outbuf = NULL;
362363

363364
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
382383
P->len *= 2;
383384
}
384385
P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
386+
P->outbuflen = P->len;
385387
}
386388
}
387389

@@ -479,10 +481,16 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
479481
case PDO_ODBC_CONV_FAIL:
480482
case PDO_ODBC_CONV_NOT_REQUIRED:
481483
P->len = Z_STRLEN_P(parameter);
484+
if (P->len > P->outbuflen) {
485+
P->len = P->outbuflen;
486+
}
482487
memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);
483488
break;
484489
case PDO_ODBC_CONV_OK:
485490
P->len = ulen;
491+
if (P->len > P->outbuflen) {
492+
P->len = P->outbuflen;
493+
}
486494
memcpy(P->outbuf, S->convbuf, P->len);
487495
break;
488496
}
@@ -504,6 +512,9 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
504512
zval_ptr_dtor(parameter);
505513

506514
if (P->len >= 0) {
515+
if (P->len > P->outbuflen) {
516+
P->len = P->outbuflen;
517+
}
507518
ZVAL_STRINGL(parameter, P->outbuf, P->len);
508519
switch (pdo_odbc_ucs22utf8(P->is_unicode, parameter)) {
509520
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
@@ -153,6 +153,7 @@ typedef struct {
153153

154154
typedef struct {
155155
SQLLEN len;
156+
SQLLEN outbuflen;
156157
SQLSMALLINT paramtype;
157158
char *outbuf;
158159
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)