Skip to content

Commit fec9e88

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Fix heap over-read seeding the long-column buffer in pdo_odbc
2 parents 9be7052 + 91ea928 commit fec9e88

2 files changed

Lines changed: 48 additions & 2 deletions

File tree

ext/pdo_odbc/odbc_stmt.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -702,8 +702,9 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo
702702
}
703703
ssize_t to_fetch_byte = to_fetch_len + 1;
704704
char *buf2 = emalloc(to_fetch_byte);
705-
zend_string *str = zend_string_init(C->data, to_fetch_byte, 0);
706-
size_t used = to_fetch_len;
705+
ssize_t seed_len = to_fetch_len > (LONG_COLUMN_BUFFER_SIZE - 1) ? (LONG_COLUMN_BUFFER_SIZE - 1) : to_fetch_len;
706+
zend_string *str = zend_string_init(C->data, seed_len + 1, 0);
707+
size_t used = seed_len;
707708

708709
do {
709710
C->fetched_len = 0;

ext/pdo_odbc/tests/gh22349.phpt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
GH-22349 (Heap over-read fetching a long column past the internal buffer)
3+
--EXTENSIONS--
4+
pdo_odbc
5+
--SKIPIF--
6+
<?php
7+
require 'ext/pdo/tests/pdo_test.inc';
8+
PDOTest::skip();
9+
?>
10+
--FILE--
11+
<?php
12+
require 'ext/pdo/tests/pdo_test.inc';
13+
$db = PDOTest::test_factory('ext/pdo_odbc/tests/common.phpt');
14+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
15+
16+
$db->exec('DROP TABLE test_gh22349');
17+
if (false === $db->exec('CREATE TABLE test_gh22349 (data text)')
18+
&& false === $db->exec('CREATE TABLE test_gh22349 (data CLOB)')
19+
&& false === $db->exec('CREATE TABLE test_gh22349 (data longtext)')) {
20+
die("BORK: no large text column type available here: " . implode(", ", $db->errorInfo()) . "\n");
21+
}
22+
23+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
24+
25+
// The driver fetches a long column into an internal buffer of roughly one
26+
// memory page and reassembles the remainder. Exercise values that span and
27+
// exceed that buffer so the seeded length must match the bytes present.
28+
foreach ([4096, 8192, 65536] as $len) {
29+
$db->exec('DELETE FROM test_gh22349');
30+
$text = str_repeat('A', $len);
31+
$db->exec("INSERT INTO test_gh22349 VALUES ('$text')");
32+
$got = $db->query('SELECT data FROM test_gh22349')->fetchColumn();
33+
printf("%d: %s\n", $len, ($got === $text) ? 'ok' : ('MISMATCH len=' . strlen($got)));
34+
}
35+
?>
36+
--CLEAN--
37+
<?php
38+
require 'ext/pdo/tests/pdo_test.inc';
39+
$db = PDOTest::test_factory('ext/pdo_odbc/tests/common.phpt');
40+
$db->exec('DROP TABLE test_gh22349');
41+
?>
42+
--EXPECT--
43+
4096: ok
44+
8192: ok
45+
65536: ok

0 commit comments

Comments
 (0)