Skip to content

Commit

Permalink
Rollup merge of rust-lang#80895 - sfackler:read-to-end-ub, r=m-ou-se
Browse files Browse the repository at this point in the history
Fix handling of malicious Readers in read_to_end

A malicious `Read` impl could return overly large values from `read`, which would result in the guard's drop impl setting the buffer's length to greater than its capacity! ~~To fix this, the drop impl now uses the safe `truncate` function instead of `set_len` which ensures that this will not happen. The result of calling the function will be nonsensical, but that's fine given the contract violation of the `Read` impl.~~

~~The `Guard` type is also used by `append_to_string` which does not pass untrusted values into the length field, so I've copied the guard type into each function and only modified the one used by `read_to_end`. We could just keep a single one and modify it, but it seems a bit cleaner to keep the guard code close to the functions and related specifically to them.~~

To fix this, we now assert that the returned length is not larger than the buffer passed to the method.

For reference, this bug has been present for ~2.5 years since 1.20: rust-lang@ecbb896.

Closes rust-lang#80894.
  • Loading branch information
m-ou-se authored Jan 14, 2021
2 parents 4913b50 + e6c07b0 commit 09276b0
Showing 1 changed file with 10 additions and 12 deletions.
22 changes: 10 additions & 12 deletions library/std/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,6 @@ where
{
let start_len = buf.len();
let mut g = Guard { len: buf.len(), buf };
let ret;
loop {
if g.len == g.buf.len() {
unsafe {
Expand All @@ -385,21 +384,20 @@ where
}
}

match r.read(&mut g.buf[g.len..]) {
Ok(0) => {
ret = Ok(g.len - start_len);
break;
let buf = &mut g.buf[g.len..];
match r.read(buf) {
Ok(0) => return Ok(g.len - start_len),
Ok(n) => {
// We can't allow bogus values from read. If it is too large, the returned vec could have its length
// set past its capacity, or if it overflows the vec could be shortened which could create an invalid
// string if this is called via read_to_string.
assert!(n <= buf.len());
g.len += n;
}
Ok(n) => g.len += n,
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => {
ret = Err(e);
break;
}
Err(e) => return Err(e),
}
}

ret
}

pub(crate) fn default_read_vectored<F>(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
Expand Down

0 comments on commit 09276b0

Please sign in to comment.