Skip to content

Commit 33b6165

Browse files
committed
fix(mysql): add bounds checking to get_uint_lenenc
Change get_uint_lenenc to return Result and validate buffer has sufficient bytes before each read
1 parent 0ffef15 commit 33b6165

File tree

7 files changed

+46
-15
lines changed

7 files changed

+46
-15
lines changed

sqlx-mysql/src/connection/executor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ impl MySqlConnection {
212212
// otherwise, this first packet is the start of the result-set metadata,
213213
*self.inner.stream.waiting.front_mut().unwrap() = Waiting::Row;
214214

215-
let num_columns = packet.get_uint_lenenc(); // column count
215+
let num_columns = packet.get_uint_lenenc()?; // column count
216216
let num_columns = usize::try_from(num_columns)
217217
.map_err(|_| err_protocol!("column count overflows usize: {num_columns}"))?;
218218

sqlx-mysql/src/connection/stream.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ impl<S: Socket> MySqlStream<S> {
194194
}
195195

196196
async fn skip_result_metadata(&mut self, mut packet: Packet<Bytes>) -> Result<(), Error> {
197-
let num_columns: u64 = packet.get_uint_lenenc(); // column count
197+
let num_columns: u64 = packet.get_uint_lenenc()?; // column count
198198

199199
for _ in 0..num_columns {
200200
let _ = self.recv_packet().await?;

sqlx-mysql/src/io/buf.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub trait MySqlBufExt: Buf {
88
// NOTE: 0xfb or NULL is only returned for binary value encoding to indicate NULL.
99
// NOTE: 0xff is only returned during a result set to indicate ERR.
1010
// <https://dev.mysql.com/doc/internals/en/integer.html#packet-Protocol::LengthEncodedInteger>
11-
fn get_uint_lenenc(&mut self) -> u64;
11+
fn get_uint_lenenc(&mut self) -> Result<u64, Error>;
1212

1313
// Read a length-encoded string.
1414
#[allow(dead_code)]
@@ -19,26 +19,57 @@ pub trait MySqlBufExt: Buf {
1919
}
2020

2121
impl MySqlBufExt for Bytes {
22-
fn get_uint_lenenc(&mut self) -> u64 {
22+
fn get_uint_lenenc(&mut self) -> Result<u64, Error> {
23+
if self.remaining() < 1 {
24+
return Err(err_protocol!(
25+
"lenenc int: no bytes remaining",
26+
self.remaining()
27+
));
28+
}
29+
2330
match self.get_u8() {
24-
0xfc => u64::from(self.get_u16_le()),
25-
0xfd => self.get_uint_le(3),
26-
0xfe => self.get_u64_le(),
31+
0xfc => {
32+
if self.remaining() < 2 {
33+
return Err(err_protocol!(
34+
"lenenc int: need 2 more bytes, have {}",
35+
self.remaining()
36+
));
37+
}
38+
Ok(u64::from(self.get_u16_le()))
39+
}
40+
0xfd => {
41+
if self.remaining() < 3 {
42+
return Err(err_protocol!(
43+
"lenenc int: need 3 more bytes, have {}",
44+
self.remaining()
45+
));
46+
}
47+
Ok(self.get_uint_le(3))
48+
}
49+
0xfe => {
50+
if self.remaining() < 8 {
51+
return Err(err_protocol!(
52+
"lenenc int: need 8 more bytes, have {}",
53+
self.remaining()
54+
));
55+
}
56+
Ok(self.get_u64_le())
57+
}
2758

28-
v => u64::from(v),
59+
v => Ok(u64::from(v)),
2960
}
3061
}
3162

3263
fn get_str_lenenc(&mut self) -> Result<String, Error> {
33-
let size = self.get_uint_lenenc();
64+
let size = self.get_uint_lenenc()?;
3465
let size = usize::try_from(size)
3566
.map_err(|_| err_protocol!("string length overflows usize: {size}"))?;
3667

3768
self.get_str(size)
3869
}
3970

4071
fn get_bytes_lenenc(&mut self) -> Result<Bytes, Error> {
41-
let size = self.get_uint_lenenc();
72+
let size = self.get_uint_lenenc()?;
4273
let size = usize::try_from(size)
4374
.map_err(|_| err_protocol!("string length overflows usize: {size}"))?;
4475

sqlx-mysql/src/protocol/response/ok.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ impl ProtocolDecode<'_> for OkPacket {
2424
));
2525
}
2626

27-
let affected_rows = buf.get_uint_lenenc();
28-
let last_insert_id = buf.get_uint_lenenc();
27+
let affected_rows = buf.get_uint_lenenc()?;
28+
let last_insert_id = buf.get_uint_lenenc()?;
2929

3030
if buf.remaining() < 4 {
3131
return Err(err_protocol!(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl<'de> ProtocolDecode<'de, &'de [MySqlColumn]> for BinaryRow {
7676
| ColumnType::Decimal
7777
| ColumnType::Json
7878
| ColumnType::NewDecimal => {
79-
let size = buf.get_uint_lenenc();
79+
let size = buf.get_uint_lenenc()?;
8080
usize::try_from(size)
8181
.map_err(|_| err_protocol!("BLOB length out of range: {size}"))?
8282
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl ProtocolDecode<'_, Capabilities> for ColumnDefinition {
147147
let table = buf.get_bytes_lenenc()?;
148148
let alias = buf.get_bytes_lenenc()?;
149149
let name = buf.get_bytes_lenenc()?;
150-
let _next_len = buf.get_uint_lenenc(); // always 0x0c
150+
let _next_len = buf.get_uint_lenenc()?; // always 0x0c
151151
let collation = buf.get_u16_le();
152152
let max_size = buf.get_u32_le();
153153
let type_id = buf.get_u8();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl<'de> ProtocolDecode<'de, &'de [MySqlColumn]> for TextRow {
2222
values.push(None);
2323
buf.advance(1);
2424
} else {
25-
let size = buf.get_uint_lenenc();
25+
let size = buf.get_uint_lenenc()?;
2626
if (buf.remaining() as u64) < size {
2727
return Err(err_protocol!(
2828
"buffer exhausted when reading data for column {:?}; decoded length is {}, but only {} bytes remain in buffer. Malformed packet or protocol error?",

0 commit comments

Comments
 (0)