Skip to content

Commit e283bf9

Browse files
0xdeafbeefabonander
authored andcommitted
mysql: Fix panic on invalid text row length field
Previously, `TextRow::decode_with` would read a length-encoded field size and attempt to advance the buffer by that amount. If the server sent a malformed packet containing a length value larger than the remaining data in the buffer, the call to `buf.advance(size)` would panic. eg: ``` thread 'main' panicked at /home/user/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.10.1/src/bytes.rs:711:9: cannot advance past remaining: 8590116092 <= 0 stack backtrace: 0: 0x56119b657e00 - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h6d42cc84fc840290 1: 0x56119b67edd3 - core::fmt::write::h5af61a909e3ec64d 2: 0x56119b653ee3 - std::io::Write::write_fmt::h5a7b54aa6e4a315d 3: 0x56119b657c52 - std::sys::backtrace::BacktraceLock::print::h555579e7396c26ac 4: 0x56119b658cef - std::panicking::default_hook::{{closure}}::h9128866118196224 5: 0x56119b658b5a - std::panicking::default_hook::h52e9e7314e0255f6 6: 0x56119b659712 - std::panicking::rust_panic_with_hook::h541791bcc774ef34 7: 0x56119b65949a - std::panicking::begin_panic_handler::{{closure}}::h6479a2f0137c7d19 8: 0x56119b658319 - std::sys::backtrace::__rust_end_short_backtrace::ha04e7c0fc61ded91 9: 0x56119b65912d - rust_begin_unwind 10: 0x56119b67c390 - core::panicking::panic_fmt::h5764ee7030b7a73d 11: 0x56119b572b18 - <sqlx_mysql::protocol::text::row::TextRow as sqlx_core::io::decode::ProtocolDecode<&[sqlx_mysql::column::MySqlColumn]>>::decode_with::h17ac8b44140b5469 12: 0x56119b42df1f - sqlx_mysql::connection::executor::<impl sqlx_mysql::connection::MySqlConnection>::run::{{closure}}::{{closure}}::{{closure}}::h4874a0f73925d55a ``` This commit introduces a bounds check immediately after reading the field length from a packet. This panic condition was specifically observed when executing a TiDB `BATCH ON ... DELETE` statement via `pool.execute()`. It sends an OK packet immediately followed by a full result set describing the batch status (column defs, row data, EOF). Observed TiDB response sequence for `BATCH DML` via `COM_QUERY`: 1. OK Packet (seq=1, `SERVER_MORE_RESULTS_EXISTS` = false) 2. Column Count Packet (seq=1, non-standard, protocol violation) 3. Column Definition Packet (seq=2) 4. Column Definition Packet (seq=3) 5. Text Row Data Packet (seq=4) 6. EOF Packet (seq=5, `SERVER_MORE_RESULTS_EXISTS` = false) This differs from standard MySQL DML response (OK/ERR packet only) and causes `sqlx` using `execute()` to attempt parsing the unexpected result set packets after the initial OK packet.
1 parent 082aed5 commit e283bf9

File tree

1 file changed

+8
-1
lines changed
  • sqlx-mysql/src/protocol/text

1 file changed

+8
-1
lines changed

sqlx-mysql/src/protocol/text/row.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,20 @@ impl<'de> ProtocolDecode<'de, &'de [MySqlColumn]> for TextRow {
1616

1717
let mut values = Vec::with_capacity(columns.len());
1818

19-
for _ in columns {
19+
for c in columns {
2020
if buf[0] == 0xfb {
2121
// NULL is sent as 0xfb
2222
values.push(None);
2323
buf.advance(1);
2424
} else {
2525
let size = buf.get_uint_lenenc();
26+
if (buf.remaining() as u64) < size {
27+
return Err(err_protocol!(
28+
"buffer exhausted when reading data for column {:?}; decoded length is {}, but only {} bytes remain in buffer. Malformed packet or protocol error?",
29+
c,
30+
size,
31+
buf.remaining()));
32+
}
2633
let size = usize::try_from(size)
2734
.map_err(|_| err_protocol!("TextRow length out of range: {size}"))?;
2835

0 commit comments

Comments
 (0)