Skip to content

Commit 9bdbaa9

Browse files
committed
Implement the same OOM fix for reading Bytes
Nobody noticed likely because `with-bytes` feature is rarely used. CC: #411
1 parent f3f0ab0 commit 9bdbaa9

File tree

2 files changed

+75
-7
lines changed

2 files changed

+75
-7
lines changed

protobuf/src/buf_read_iter.rs

+68-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use bytes::BufMut;
1515
use ProtobufResult;
1616
use ProtobufError;
1717
use error::WireError;
18+
use stream::READ_RAW_BYTES_MAX_ALLOC;
1819

1920

2021
// If an input stream is constructed with a `Read`, we create a
@@ -221,6 +222,61 @@ impl<'ignore> BufReadIter<'ignore> {
221222
Ok(r)
222223
}
223224

225+
/// Read at most `max` bytes, append to `Vec`.
226+
///
227+
/// Returns 0 when EOF or limit reached.
228+
fn read_to_vec(&mut self, vec: &mut Vec<u8>, max: usize) -> ProtobufResult<usize> {
229+
let rem = self.fill_buf()?;
230+
231+
let len = cmp::min(rem.len(), max);
232+
vec.extend_from_slice(&rem[..len]);
233+
self.pos_within_buf += len;
234+
Ok(len)
235+
}
236+
237+
/// Read exact number of bytes into `Vec`.
238+
///
239+
/// `Vec` is cleared in the beginning.
240+
pub fn read_exact_to_vec(&mut self, count: usize, target: &mut Vec<u8>) -> ProtobufResult<()> {
241+
// TODO: also do some limits when reading from unlimited source
242+
if count as u64 > self.bytes_until_limit() {
243+
return Err(ProtobufError::WireError(WireError::TruncatedMessage));
244+
}
245+
246+
target.clear();
247+
248+
if count >= READ_RAW_BYTES_MAX_ALLOC && count > target.capacity() {
249+
// avoid calling `reserve` on buf with very large buffer: could be a malformed message
250+
251+
target.reserve(READ_RAW_BYTES_MAX_ALLOC);
252+
253+
while target.len() < count {
254+
if count - target.len() <= target.len() {
255+
target.reserve_exact(count - target.len());
256+
} else {
257+
target.reserve(1);
258+
}
259+
260+
let max = cmp::min(target.capacity() - target.len(), count - target.len());
261+
let read = self.read_to_vec(target, max)?;
262+
if read == 0 {
263+
return Err(ProtobufError::WireError(WireError::TruncatedMessage));
264+
}
265+
}
266+
} else {
267+
target.reserve_exact(count);
268+
269+
unsafe {
270+
self.read_exact(&mut target.get_unchecked_mut(..count))?;
271+
target.set_len(count);
272+
}
273+
}
274+
275+
debug_assert_eq!(count, target.len());
276+
277+
Ok(())
278+
}
279+
224280
#[cfg(feature = "bytes")]
225281
pub fn read_exact_bytes(&mut self, len: usize) -> ProtobufResult<Bytes> {
226282
if let InputSource::Bytes(bytes) = self.input_source {
@@ -231,15 +287,21 @@ impl<'ignore> BufReadIter<'ignore> {
231287
self.pos_within_buf += len;
232288
Ok(r)
233289
} else {
234-
let mut r = BytesMut::with_capacity(len);
235-
unsafe {
236-
{
237-
let mut buf = &mut r.bytes_mut()[..len];
290+
if len >= READ_RAW_BYTES_MAX_ALLOC {
291+
// We cannot trust `len` because protobuf message could be malformed.
292+
// Reading should not result in OOM when allocating a buffer.
293+
let mut v = Vec::new();
294+
self.read_exact_to_vec(len, &mut v)?;
295+
Ok(Bytes::from(v))
296+
} else {
297+
let mut r = BytesMut::with_capacity(len);
298+
unsafe {
299+
let buf = &mut r.bytes_mut()[..len];
238300
self.read_exact(buf)?;
301+
r.advance_mut(len);
239302
}
240-
r.advance_mut(len);
303+
Ok(r.freeze())
241304
}
242-
Ok(r.freeze())
243305
}
244306
}
245307

protobuf/src/stream.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const OUTPUT_STREAM_BUFFER_SIZE: usize = 8 * 1024;
3434
const DEFAULT_RECURSION_LIMIT: u32 = 100;
3535

3636
// Max allocated vec when reading length-delimited from unknown input stream
37-
const READ_RAW_BYTES_MAX_ALLOC: usize = 10_000_000;
37+
pub(crate) const READ_RAW_BYTES_MAX_ALLOC: usize = 10_000_000;
3838

3939

4040
pub mod wire_format {
@@ -626,6 +626,12 @@ impl<'a> CodedInputStream<'a> {
626626
/// Read raw bytes into the supplied vector. The vector will be resized as needed and
627627
/// overwritten.
628628
pub fn read_raw_bytes_into(&mut self, count: u32, target: &mut Vec<u8>) -> ProtobufResult<()> {
629+
if false {
630+
// Master uses this version, but keep existing version for a while
631+
// to avoid possible breakages.
632+
return self.source.read_exact_to_vec(count as usize, target);
633+
}
634+
629635
let count = count as usize;
630636

631637
// TODO: also do some limits when reading from unlimited source

0 commit comments

Comments
 (0)