Skip to content

Commit 04de280

Browse files
committed
Handle out of memory errors in io:Read::read_to_end()
1 parent ee85f7f commit 04de280

File tree

3 files changed

+30
-3
lines changed

3 files changed

+30
-3
lines changed

library/std/src/fs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -770,14 +770,14 @@ impl Read for &File {
770770
// Reserves space in the buffer based on the file size when available.
771771
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
772772
let size = buffer_capacity_required(self);
773-
buf.reserve(size.unwrap_or(0));
773+
buf.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
774774
io::default_read_to_end(self, buf, size)
775775
}
776776

777777
// Reserves space in the buffer based on the file size when available.
778778
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
779779
let size = buffer_capacity_required(self);
780-
buf.reserve(size.unwrap_or(0));
780+
buf.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
781781
io::default_read_to_string(self, buf, size)
782782
}
783783
}

library/std/src/io/buffered/bufreader.rs

+1
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ impl<R: ?Sized + Read> Read for BufReader<R> {
345345
// delegate to the inner implementation.
346346
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
347347
let inner_buf = self.buffer();
348+
buf.try_reserve(inner_buf.len()).map_err(|_| io::ErrorKind::OutOfMemory)?;
348349
buf.extend_from_slice(inner_buf);
349350
let nread = inner_buf.len();
350351
self.discard_buffer();

library/std/src/io/mod.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,8 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
418418
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
419419
loop {
420420
if buf.len() == buf.capacity() {
421-
buf.reserve(32); // buf is full, need more space
421+
// buf is full, need more space
422+
buf.try_reserve(32).map_err(|_| ErrorKind::OutOfMemory)?;
422423
}
423424

424425
let mut spare = buf.spare_capacity_mut();
@@ -464,6 +465,7 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
464465
match r.read(&mut probe) {
465466
Ok(0) => return Ok(buf.len() - start_len),
466467
Ok(n) => {
468+
buf.try_reserve(n).map_err(|_| ErrorKind::OutOfMemory)?;
467469
buf.extend_from_slice(&probe[..n]);
468470
break;
469471
}
@@ -767,6 +769,30 @@ pub trait Read {
767769
/// file.)
768770
///
769771
/// [`std::fs::read`]: crate::fs::read
772+
///
773+
/// ## Implementing `read_to_end`
774+
///
775+
/// When implementing the `io::Read` trait, it is recommended to allocate
776+
/// memory using [`Vec::try_reserve`]. However, this behavior is not guaranteed
777+
/// by all implementations, and `read_to_end` may not handle out-of-memory
778+
/// situations gracefully.
779+
///
780+
/// ```no_run
781+
/// # use std::io;
782+
/// # struct Example; impl Example {
783+
/// # fn read_some_data_for_the_example(&self) -> &'static [u8] { &[] }
784+
/// fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
785+
/// let data_read = self.read_some_data_for_the_example();
786+
///
787+
/// buf.try_reserve(data_read.len()).map_err(|_| io::ErrorKind::OutOfMemory)?;
788+
/// buf.extend_from_slice(data_read);
789+
///
790+
/// Ok(data_read.len())
791+
/// }
792+
/// # }
793+
/// ```
794+
///
795+
/// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve
770796
#[stable(feature = "rust1", since = "1.0.0")]
771797
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
772798
default_read_to_end(self, buf, None)

0 commit comments

Comments
 (0)