From cf446cd90b749b82ec4d45e28362a7a836a839c4 Mon Sep 17 00:00:00 2001 From: Kornel Date: Mon, 9 Oct 2023 14:25:31 +0100 Subject: [PATCH 1/3] Handle out of memory errors in io:Read::read_to_end() --- library/std/src/fs.rs | 4 ++-- library/std/src/io/buffered/bufreader.rs | 1 + library/std/src/io/mod.rs | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 61c3913361717..f4020e13e11dc 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -770,14 +770,14 @@ impl Read for &File { // Reserves space in the buffer based on the file size when available. fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { let size = buffer_capacity_required(self); - buf.reserve(size.unwrap_or(0)); + buf.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?; io::default_read_to_end(self, buf, size) } // Reserves space in the buffer based on the file size when available. fn read_to_string(&mut self, buf: &mut String) -> io::Result { let size = buffer_capacity_required(self); - buf.reserve(size.unwrap_or(0)); + buf.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?; io::default_read_to_string(self, buf, size) } } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 55aafc3db330a..75b7d5db071be 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -345,6 +345,7 @@ impl Read for BufReader { // delegate to the inner implementation. fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { let inner_buf = self.buffer(); + buf.try_reserve(inner_buf.len()).map_err(|_| io::ErrorKind::OutOfMemory)?; buf.extend_from_slice(inner_buf); let nread = inner_buf.len(); self.discard_buffer(); diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index aa9a2482d2d92..4f27c1eb4657b 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -419,7 +419,8 @@ pub(crate) fn default_read_to_end( let mut initialized = 0; // Extra initialized bytes from previous loop iteration loop { if buf.len() == buf.capacity() { - buf.reserve(32); // buf is full, need more space + // buf is full, need more space + buf.try_reserve(32).map_err(|_| ErrorKind::OutOfMemory)?; } let mut spare = buf.spare_capacity_mut(); @@ -465,6 +466,7 @@ pub(crate) fn default_read_to_end( match r.read(&mut probe) { Ok(0) => return Ok(buf.len() - start_len), Ok(n) => { + buf.try_reserve(n).map_err(|_| ErrorKind::OutOfMemory)?; buf.extend_from_slice(&probe[..n]); break; } From 75e10650fa61f0a074c5b5d881a1bfbdd71c3593 Mon Sep 17 00:00:00 2001 From: Kornel Date: Mon, 9 Oct 2023 14:32:15 +0100 Subject: [PATCH 2/3] Handle out of memory errors in fs::read/read_to_string --- library/std/src/fs.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index f4020e13e11dc..e03aab1cc8fa1 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -254,7 +254,8 @@ pub fn read>(path: P) -> io::Result> { fn inner(path: &Path) -> io::Result> { let mut file = File::open(path)?; let size = file.metadata().map(|m| m.len() as usize).ok(); - let mut bytes = Vec::with_capacity(size.unwrap_or(0)); + let mut bytes = Vec::new(); + bytes.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?; io::default_read_to_end(&mut file, &mut bytes, size)?; Ok(bytes) } @@ -296,7 +297,8 @@ pub fn read_to_string>(path: P) -> io::Result { fn inner(path: &Path) -> io::Result { let mut file = File::open(path)?; let size = file.metadata().map(|m| m.len() as usize).ok(); - let mut string = String::with_capacity(size.unwrap_or(0)); + let mut string = String::new(); + string.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?; io::default_read_to_string(&mut file, &mut string, size)?; Ok(string) } From 98f7d4cd1e024b356a3c49c3d40dc3442aa7d50f Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 7 Nov 2023 13:08:38 +0000 Subject: [PATCH 3/3] Document read_to_end memory handling non-guarantee --- library/std/src/io/mod.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 4f27c1eb4657b..2b0f1f22349c2 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -770,6 +770,30 @@ pub trait Read { /// file.) /// /// [`std::fs::read`]: crate::fs::read + /// + /// ## Implementing `read_to_end` + /// + /// When implementing the `io::Read` trait, it is recommended to allocate + /// memory using [`Vec::try_reserve`]. However, this behavior is not guaranteed + /// by all implementations, and `read_to_end` may not handle out-of-memory + /// situations gracefully. + /// + /// ```no_run + /// # use std::io; + /// # struct Example; impl Example { + /// # fn read_some_data_for_the_example(&self) -> &'static [u8] { &[] } + /// fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + /// let data_read = self.read_some_data_for_the_example(); + /// + /// buf.try_reserve(data_read.len()).map_err(|_| io::ErrorKind::OutOfMemory)?; + /// buf.extend_from_slice(data_read); + /// + /// Ok(data_read.len()) + /// } + /// # } + /// ``` + /// + /// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve #[stable(feature = "rust1", since = "1.0.0")] fn read_to_end(&mut self, buf: &mut Vec) -> Result { default_read_to_end(self, buf, None)