Skip to content

Fix heap over-read seeding the long-column buffer in pdo_odbc#22349

Open
iliaal wants to merge 1 commit into
php:PHP-8.5from
iliaal:fix-pdo-odbc-long-overread
Open

Fix heap over-read seeding the long-column buffer in pdo_odbc#22349
iliaal wants to merge 1 commit into
php:PHP-8.5from
iliaal:fix-pdo-odbc-long-overread

Conversation

@iliaal

@iliaal iliaal commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

In the long-column fetch path, when the ODBC driver reports the total column length rather than SQL_NO_TOTAL, the result string was seeded by copying orig_fetched_len + 1 bytes out of C->data, which holds at most LONG_COLUMN_BUFFER_SIZE bytes from the first SQLGetData. For a column larger than that buffer this reads past C->data. Seed only the bytes actually present, matching the SQL_NO_TOTAL branch; the remainder is still fetched by the loop.

@SakiTakamachi SakiTakamachi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. I’d also like to wait for Calvin’s review.

@NattyNarwhal NattyNarwhal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds right. Have you ran into this with a driver or is this static analysis?

In the long-column fetch path, when the ODBC driver reports the total
column length rather than SQL_NO_TOTAL, the result string was seeded by
copying orig_fetched_len + 1 bytes out of C->data, which holds at most
LONG_COLUMN_BUFFER_SIZE bytes from the first SQLGetData. For a column
larger than that buffer this reads past C->data. Seed only the bytes
actually present in the buffer, matching the SQL_NO_TOTAL branch; the
remainder is still fetched by the loop.
@iliaal iliaal force-pushed the fix-pdo-odbc-long-overread branch from 8a28a54 to a1ee48e Compare June 17, 2026 10:30
@iliaal

iliaal commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Originally static analysis, but I've now reproduced it against a live driver. psqlODBC (PostgreSQL ANSI) returns the exact column length on the truncated SQLGetData, so a text value larger than LONG_COLUMN_BUFFER_SIZE takes the unclamped branch. With a 64 KB text column and an ASAN build, the fetch trips a heap-buffer-overflow READ of 65537 bytes out of the 4064-byte buffer in odbc_stmt_get_col. The sqlite ODBC driver returns SQL_NO_TOTAL, which is why that path stayed hidden. Added a regression test (gh22349.phpt); it skips without an ODBC DSN and fails under ASAN against psqlODBC.

@iliaal iliaal requested a review from NattyNarwhal June 17, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants