Skip to content

Commit ba4027e

Browse files
committed
fix(flexbuffers): harden Reader against panics from untrusted input
The FlexBuffers Rust Reader had multiple code paths that could panic when processing malformed or malicious input: 1. Bounds check failures: `get_bool()`, `get_key_len()`, `read_usize()`, and `MapReader::lazy_strcmp()` used direct slice indexing (`buffer[addr..]`) which panics on out-of-bounds access. Replaced with checked `.get()` calls that return errors instead. 2. Integer overflow: `get_str()`, `get_blob()`, `get_key()`, `get_slice()`, `VectorReader::index()`, `VectorReader::get_elem_type()`, `MapReader::index_key()`, and `MapReader::usize_index()` computed `address + length` or `address + width * count` using unchecked arithmetic, which panics on overflow in debug mode and wraps in release mode. Replaced with `checked_add()` / `checked_mul()`. These panics are reachable from any code that deserializes FlexBuffers from untrusted sources (network, files, IPC), enabling denial of service. All three crash inputs found by fuzzing now return `Err` instead of panicking. Fixes #8923
1 parent c21bda1 commit ba4027e

File tree

3 files changed

+74
-16
lines changed

3 files changed

+74
-16
lines changed

rust/flexbuffers/src/reader/map.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,11 @@ impl<B: Buffer> MapReader<B> {
7979
// Using &CStr will eagerly compute the length of the key. &str needs length info AND utf8
8080
// validation. This version is faster than both.
8181
fn lazy_strcmp(&self, key_addr: usize, key: &str) -> Ordering {
82-
// TODO: Can we know this won't OOB and panic?
83-
let k = self.buffer[key_addr..].iter().take_while(|&&b| b != b'\0');
82+
let tail = match self.buffer.get(key_addr..) {
83+
Some(s) => s,
84+
None => return Ordering::Less,
85+
};
86+
let k = tail.iter().take_while(|&&b| b != b'\0');
8487
k.cmp(key.as_bytes().iter())
8588
}
8689

@@ -89,7 +92,9 @@ impl<B: Buffer> MapReader<B> {
8992
let (mut low, mut high) = (0, self.length);
9093
while low < high {
9194
let i = (low + high) / 2;
92-
let key_offset_address = self.keys_address + i * self.keys_width.n_bytes();
95+
let key_offset_address = i
96+
.checked_mul(self.keys_width.n_bytes())
97+
.and_then(|offset| self.keys_address.checked_add(offset))?;
9398
let key_address =
9499
deref_offset(&self.buffer, key_offset_address, self.keys_width).ok()?;
95100
match self.lazy_strcmp(key_address, key) {
@@ -115,8 +120,19 @@ impl<B: Buffer> MapReader<B> {
115120
if i >= self.length {
116121
return Err(Error::IndexOutOfBounds);
117122
}
118-
let data_address = self.values_address + self.values_width.n_bytes() * i;
119-
let type_address = self.values_address + self.values_width.n_bytes() * self.length + i;
123+
let data_address = self
124+
.values_width
125+
.n_bytes()
126+
.checked_mul(i)
127+
.and_then(|offset| self.values_address.checked_add(offset))
128+
.ok_or(Error::FlexbufferOutOfBounds)?;
129+
let type_address = self
130+
.values_width
131+
.n_bytes()
132+
.checked_mul(self.length)
133+
.and_then(|offset| self.values_address.checked_add(offset))
134+
.and_then(|addr| addr.checked_add(i))
135+
.ok_or(Error::FlexbufferOutOfBounds)?;
120136
let (fxb_type, width) = self
121137
.buffer
122138
.get(type_address)

rust/flexbuffers/src/reader/mod.rs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,14 @@ impl<B: Buffer> Reader<B> {
304304
if self.bitwidth().n_bytes() != std::mem::size_of::<T>() {
305305
self.expect_bw(T::WIDTH)?;
306306
}
307-
let end = self.address + self.length() * std::mem::size_of::<T>();
307+
let byte_len = self
308+
.length()
309+
.checked_mul(std::mem::size_of::<T>())
310+
.ok_or(Error::FlexbufferOutOfBounds)?;
311+
let end = self
312+
.address
313+
.checked_add(byte_len)
314+
.ok_or(Error::FlexbufferOutOfBounds)?;
308315
let slice: &[u8] =
309316
self.buffer.get(self.address..end).ok_or(Error::FlexbufferOutOfBounds)?;
310317

@@ -323,7 +330,12 @@ impl<B: Buffer> Reader<B> {
323330
/// Otherwise Returns error.
324331
pub fn get_bool(&self) -> Result<bool, Error> {
325332
self.expect_type(FlexBufferType::Bool)?;
326-
Ok(self.buffer[self.address..self.address + self.width.n_bytes()].iter().any(|&b| b != 0))
333+
let end = self.address + self.width.n_bytes();
334+
let slice = self
335+
.buffer
336+
.get(self.address..end)
337+
.ok_or(Error::FlexbufferOutOfBounds)?;
338+
Ok(slice.iter().any(|&b| b != 0))
327339
}
328340

329341
/// Gets the length of the key if this type is a key.
@@ -332,7 +344,11 @@ impl<B: Buffer> Reader<B> {
332344
#[inline]
333345
fn get_key_len(&self) -> Result<usize, Error> {
334346
self.expect_type(FlexBufferType::Key)?;
335-
let (length, _) = self.buffer[self.address..]
347+
let tail = self
348+
.buffer
349+
.get(self.address..)
350+
.ok_or(Error::FlexbufferOutOfBounds)?;
351+
let (length, _) = tail
336352
.iter()
337353
.enumerate()
338354
.find(|(_, &b)| b == b'\0')
@@ -342,18 +358,27 @@ impl<B: Buffer> Reader<B> {
342358

343359
/// Retrieves the string value up until the first `\0` character.
344360
pub fn get_key(&self) -> Result<B::BufferString, Error> {
361+
let key_len = self.get_key_len()?;
362+
let end = self
363+
.address
364+
.checked_add(key_len)
365+
.ok_or(Error::FlexbufferOutOfBounds)?;
345366
let bytes = self
346367
.buffer
347-
.slice(self.address..self.address + self.get_key_len()?)
368+
.slice(self.address..end)
348369
.ok_or(Error::IndexOutOfBounds)?;
349370
Ok(bytes.buffer_str()?)
350371
}
351372

352373
pub fn get_blob(&self) -> Result<Blob<B>, Error> {
353374
self.expect_type(FlexBufferType::Blob)?;
375+
let end = self
376+
.address
377+
.checked_add(self.length())
378+
.ok_or(Error::FlexbufferOutOfBounds)?;
354379
Ok(Blob(
355380
self.buffer
356-
.slice(self.address..self.address + self.length())
381+
.slice(self.address..end)
357382
.ok_or(Error::IndexOutOfBounds)?,
358383
))
359384
}
@@ -366,7 +391,11 @@ impl<B: Buffer> Reader<B> {
366391
/// is out of bounds.
367392
pub fn get_str(&self) -> Result<B::BufferString, Error> {
368393
self.expect_type(FlexBufferType::String)?;
369-
let bytes = self.buffer.slice(self.address..self.address + self.length());
394+
let end = self
395+
.address
396+
.checked_add(self.length())
397+
.ok_or(Error::FlexbufferOutOfBounds)?;
398+
let bytes = self.buffer.slice(self.address..end);
370399
Ok(bytes.ok_or(Error::ReadUsizeOverflowed)?.buffer_str()?)
371400
}
372401

@@ -601,9 +630,12 @@ fn f64_from_le_bytes(bytes: [u8; 8]) -> f64 {
601630
}
602631

603632
fn read_usize(buffer: &[u8], address: usize, width: BitWidth) -> usize {
604-
let cursor = &buffer[address..];
633+
let cursor = match buffer.get(address..) {
634+
Some(c) => c,
635+
None => return 0,
636+
};
605637
match width {
606-
BitWidth::W8 => cursor[0] as usize,
638+
BitWidth::W8 => cursor.first().copied().unwrap_or_default() as usize,
607639
BitWidth::W16 => cursor
608640
.get(0..2)
609641
.and_then(|s| s.try_into().ok())

rust/flexbuffers/src/reader/vector.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,14 @@ impl<B: Buffer> VectorReader<B> {
5252
if let Some(ty) = self.reader.fxb_type.typed_vector_type() {
5353
Ok((ty, self.reader.width))
5454
} else {
55-
let types_addr = self.reader.address + self.length * self.reader.width.n_bytes();
55+
let types_addr = self
56+
.length
57+
.checked_mul(self.reader.width.n_bytes())
58+
.and_then(|offset| self.reader.address.checked_add(offset))
59+
.ok_or(Error::FlexbufferOutOfBounds)?;
5660
self.reader
5761
.buffer
58-
.get(types_addr + i)
62+
.get(types_addr.checked_add(i).ok_or(Error::FlexbufferOutOfBounds)?)
5963
.ok_or(Error::FlexbufferOutOfBounds)
6064
.and_then(|&t| unpack_type(t))
6165
}
@@ -70,7 +74,13 @@ impl<B: Buffer> VectorReader<B> {
7074
return Err(Error::IndexOutOfBounds);
7175
}
7276
let (fxb_type, bw) = self.get_elem_type(i)?;
73-
let data_address = self.reader.address + self.reader.width.n_bytes() * i;
77+
let data_address = self
78+
.reader
79+
.width
80+
.n_bytes()
81+
.checked_mul(i)
82+
.and_then(|offset| self.reader.address.checked_add(offset))
83+
.ok_or(Error::FlexbufferOutOfBounds)?;
7484
Reader::new(
7585
self.reader.buffer.shallow_copy(),
7686
data_address,

0 commit comments

Comments
 (0)