Skip to content

Validate the length of a BIT column value coming from MySQL - #119680

Merged
alexey-milovidov merged 1 commit into
masterfrom
fix-mysql-bit-column-length-validation
Sep 13, 2026
Merged

Validate the length of a BIT column value coming from MySQL#119680
alexey-milovidov merged 1 commit into
masterfrom
fix-mysql-bit-column-length-validation

Conversation

@alexey-milovidov

@alexey-milovidov alexey-milovidov commented Sep 12, 2026

Copy link
Copy Markdown
Member

The value of a MYSQL_TYPE_BIT column was copied into an eight-byte stack local with memcpy, using the length reported by the server without checking it first. A BIT column holds at most 64 bits, but nothing bounded what the server actually sent: value.size() is the field length taken from the row packet of the text protocol, unrelated to the declared width of the column. The std::reverse that follows then walked the same out-of-bounds range.

So any MySQL endpoint that ClickHouse connects to - through the mysql table function, a MySQL table or database engine, or a MySQL dictionary - could overflow that local. insertValue is a real out-of-line function, and the translation unit is compiled without a stack protector and without _FORTIFY_SOURCE, so nothing detected the overflow.

Confirmed against a fake MySQL server:

  • a hundred-byte value produced 4702111234474983745, the first eight of its A bytes, having written the other ninety two past the end of the destination;
  • a 2200-byte value, which is enough to reach the saved return address (measured at 2136 bytes past the local in an aarch64 release build), crashed with SIGBUS after jumping to an address made out of the value bytes.

The length is now checked before the copy. The new test 04870_mysql_bit_column_oversized_value starts a fake MySQL server that returns a BIT value of two, eight, or a hundred bytes depending on the remote table name: the first two must still be read correctly, most significant byte first, and the last must be rejected.

Changelog category (leave one):

  • Critical Bug Fix (crash, data loss, RBAC)

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Fixed a stack buffer overflow when reading a BIT column from MySQL, reachable through the mysql table function, the MySQL table and database engines, and MySQL dictionaries. The length of the value sent by the server was copied without being validated.


Workflow [PR]
Sync PR [sync-upstream/pr/119680]

Version info

  • Backported to: 26.8.3.105, 26.7.8.12, 26.6.6.5, 26.3.33.73

The value of a `MYSQL_TYPE_BIT` column was copied into an eight-byte stack
local with `memcpy`, using the length reported by the server without checking
it first. A `BIT` column holds at most 64 bits, but nothing bounded what the
server actually sent: the length comes from the row packet of the text
protocol and is unrelated to the declared width of the column. The
`std::reverse` that follows then walked the same out-of-bounds range.

So any MySQL endpoint that ClickHouse connects to - through the `mysql` table
function, a `MySQL` table or database engine, or a MySQL dictionary - could
overflow that local. The translation unit is compiled without a stack
protector and without `_FORTIFY_SOURCE`, so nothing detected it.

Confirmed against a fake MySQL server: a hundred-byte value produced
`4702111234474983745`, the first eight of its bytes, having written the
other ninety two past the end of the destination; a 2200-byte value, which
is enough to reach the saved return address of `insertValue`, crashed with
`SIGBUS` after jumping to an address made out of the value bytes.

The length is now checked before the copy, and the new test covers values of
two, eight, and a hundred bytes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@clickhouse-gh

clickhouse-gh Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [8807675]

Summary:


AI Review

Summary

This PR adds a length check before copying MYSQL_TYPE_BIT values out of a MySQL row and adds a regression test with a fake server. The overflow fix itself is correct, but the touched decoder still misreads short BIT values on big-endian builds, so this is not ready to approve as-is.

Findings

⚠️ Majors

  • [src/Processors/Sources/MySQLSource.cpp:332] The MYSQL_TYPE_BIT decoder still assumes little-endian layout for short values. After the new guard, a big-endian build copies a 1- to 7-byte value into the beginning of UInt64 and skips the reversal, so 0x01 0x02 becomes 0x0102000000000000 instead of 0x0102. The overflow is gone, but BIT columns remain misdecoded on s390x. Suggested fix: decode the bytes in a host-independent loop (val = (val << 8) | byte) or right-align the copied bytes on big-endian before inserting.
Final Verdict

Needs changes before approval: fix the remaining big-endian BIT decoding bug in the touched path.

LLVM Coverage Report

Measured on commit 8807675.

Metric Baseline Current Δ
Lines 89.00% 89.00% +0.00%
Functions 91.70% 91.80% +0.10%
Branches 81.30% 81.30% +0.00%

Changed lines: Changed C/C++ lines covered: 9/9 (100.00%) · Uncovered code

Full report · Diff report

@clickhouse-gh clickhouse-gh Bot added pr-critical-bugfix pr-must-backport Pull request should be backported intentionally. Use this label with great care! labels Sep 12, 2026
Comment thread src/Processors/Sources/MySQLSource.cpp
@clickhouse-gh clickhouse-gh Bot added the comp-mysql MySQL-specific integration (table engine/function/protocol mapping). label Sep 12, 2026
@clickhouse-gh

clickhouse-gh Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Build profile diff (arm_release)

Comparing 8807675a5 with master eea50a05f (stripped binary size, per-symbol sizes and ThinLTO time; compile times per translation unit against the most recent warmup build that recompiled it).

✅ No significant changes.

Binary sizes

programs/clickhouse-stripped: smaller than the master baseline by the known offset between the two builds, so the difference is not shown. A delta that differs from the offset by more than 50% of it is shown, in either direction.

The official master build is compiled with -g and a pull request build is not, and XRay counts debug instructions towards its instrumentation threshold, so master instruments thousands of functions more and its binary is ~0.4% larger no matter what the pull request does.

Compile time of recompiled translation units

7 translation units recompiled, 11 s compile time in total, 7 of them have a recent master baseline.

Job report

@alexey-milovidov alexey-milovidov self-assigned this Sep 13, 2026
@alexey-milovidov
alexey-milovidov added this pull request to the merge queue Sep 13, 2026
Merged via the queue into master with commit 3f77324 Sep 13, 2026
347 of 348 checks passed
@alexey-milovidov
alexey-milovidov deleted the fix-mysql-bit-column-length-validation branch September 13, 2026 08:13
@robot-ch-test-poll robot-ch-test-poll added the pr-must-backport-synced The `*-must-backport` labels are synced into the cloud Sync PR label Sep 13, 2026
@robot-clickhouse-ci-1 robot-clickhouse-ci-1 added the pr-synced-to-cloud The PR is synced to the cloud repo label Sep 13, 2026
clickhouse-gh Bot pushed a commit that referenced this pull request Sep 13, 2026
clickhouse-gh Bot added a commit that referenced this pull request Sep 13, 2026
Backport #119680 to 26.8: Validate the length of a `BIT` column value coming from MySQL
robot-clickhouse-ci-2 added a commit that referenced this pull request Sep 13, 2026
Cherry pick #119680 to 26.3: Validate the length of a `BIT` column value coming from MySQL
robot-clickhouse added a commit that referenced this pull request Sep 13, 2026
robot-ch-test-poll added a commit that referenced this pull request Sep 13, 2026
Cherry pick #119680 to 26.6: Validate the length of a `BIT` column value coming from MySQL
robot-clickhouse added a commit that referenced this pull request Sep 13, 2026
@robot-clickhouse-ci-1 robot-clickhouse-ci-1 added the pr-backports-created Backport PRs are successfully created, it won't be processed by CI script anymore label Sep 13, 2026
clickhouse-gh Bot added a commit that referenced this pull request Sep 13, 2026
Backport #119680 to 26.6: Validate the length of a `BIT` column value coming from MySQL
alexey-milovidov added a commit that referenced this pull request Sep 13, 2026
Backport #119680 to 26.3: Validate the length of a `BIT` column value coming from MySQL
clickhouse-gh Bot pushed a commit that referenced this pull request Sep 13, 2026
clickhouse-gh Bot added a commit that referenced this pull request Sep 13, 2026
Backport #119680 to 26.7: Validate the length of a `BIT` column value coming from MySQL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp-mysql MySQL-specific integration (table engine/function/protocol mapping). pr-backports-created Backport PRs are successfully created, it won't be processed by CI script anymore pr-critical-bugfix pr-must-backport Pull request should be backported intentionally. Use this label with great care! pr-must-backport-synced The `*-must-backport` labels are synced into the cloud Sync PR pr-synced-to-cloud The PR is synced to the cloud repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants