Skip to content

Commit b38805e

Browse files
authored
Fix handling of malicious readers in read_to_end (#2314)
1 parent 6f948ac commit b38805e

File tree

2 files changed

+88
-17
lines changed

2 files changed

+88
-17
lines changed

futures-util/src/io/read_to_end.rs

+24-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use futures_core::ready;
21
use futures_core::future::Future;
2+
use futures_core::ready;
33
use futures_core::task::{Context, Poll};
44
use futures_io::AsyncRead;
55
use std::io;
@@ -28,11 +28,16 @@ impl<'a, R: AsyncRead + ?Sized + Unpin> ReadToEnd<'a, R> {
2828
}
2929
}
3030

31-
struct Guard<'a> { buf: &'a mut Vec<u8>, len: usize }
31+
struct Guard<'a> {
32+
buf: &'a mut Vec<u8>,
33+
len: usize,
34+
}
3235

3336
impl Drop for Guard<'_> {
3437
fn drop(&mut self) {
35-
unsafe { self.buf.set_len(self.len); }
38+
unsafe {
39+
self.buf.set_len(self.len);
40+
}
3641
}
3742
}
3843

@@ -51,8 +56,10 @@ pub(super) fn read_to_end_internal<R: AsyncRead + ?Sized>(
5156
buf: &mut Vec<u8>,
5257
start_len: usize,
5358
) -> Poll<io::Result<usize>> {
54-
let mut g = Guard { len: buf.len(), buf };
55-
let ret;
59+
let mut g = Guard {
60+
len: buf.len(),
61+
buf,
62+
};
5663
loop {
5764
if g.len == g.buf.len() {
5865
unsafe {
@@ -63,24 +70,24 @@ pub(super) fn read_to_end_internal<R: AsyncRead + ?Sized>(
6370
}
6471
}
6572

66-
match ready!(rd.as_mut().poll_read(cx, &mut g.buf[g.len..])) {
67-
Ok(0) => {
68-
ret = Poll::Ready(Ok(g.len - start_len));
69-
break;
70-
}
71-
Ok(n) => g.len += n,
72-
Err(e) => {
73-
ret = Poll::Ready(Err(e));
74-
break;
73+
let buf = &mut g.buf[g.len..];
74+
match ready!(rd.as_mut().poll_read(cx, buf)) {
75+
Ok(0) => return Poll::Ready(Ok(g.len - start_len)),
76+
Ok(n) => {
77+
// We can't allow bogus values from read. If it is too large, the returned vec could have its length
78+
// set past its capacity, or if it overflows the vec could be shortened which could create an invalid
79+
// string if this is called via read_to_string.
80+
assert!(n <= buf.len());
81+
g.len += n;
7582
}
83+
Err(e) => return Poll::Ready(Err(e)),
7684
}
7785
}
78-
79-
ret
8086
}
8187

8288
impl<A> Future for ReadToEnd<'_, A>
83-
where A: AsyncRead + ?Sized + Unpin,
89+
where
90+
A: AsyncRead + ?Sized + Unpin,
8491
{
8592
type Output = io::Result<usize>;
8693

futures/tests/io_read_to_end.rs

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use futures::{
2+
io::{self, AsyncRead, AsyncReadExt},
3+
task::{Context, Poll},
4+
};
5+
use std::pin::Pin;
6+
7+
#[test]
8+
#[should_panic(expected = "assertion failed: n <= buf.len()")]
9+
fn issue2310() {
10+
struct MyRead {
11+
first: bool,
12+
}
13+
14+
impl MyRead {
15+
pub fn new() -> Self {
16+
MyRead { first: false }
17+
}
18+
}
19+
20+
impl AsyncRead for MyRead {
21+
fn poll_read(
22+
mut self: Pin<&mut Self>,
23+
_cx: &mut Context,
24+
_buf: &mut [u8],
25+
) -> Poll<io::Result<usize>> {
26+
Poll::Ready(if !self.first {
27+
self.first = true;
28+
// First iteration: return more than the buffer size
29+
Ok(64)
30+
} else {
31+
// Second iteration: indicate that we are done
32+
Ok(0)
33+
})
34+
}
35+
}
36+
37+
struct VecWrapper {
38+
inner: Vec<u8>,
39+
}
40+
41+
impl VecWrapper {
42+
pub fn new() -> Self {
43+
VecWrapper { inner: Vec::new() }
44+
}
45+
}
46+
47+
impl Drop for VecWrapper {
48+
fn drop(&mut self) {
49+
// Observe uninitialized bytes
50+
println!("{:?}", &self.inner);
51+
// Overwrite heap contents
52+
for b in &mut self.inner {
53+
*b = 0x90;
54+
}
55+
}
56+
}
57+
58+
futures::executor::block_on(async {
59+
let mut vec = VecWrapper::new();
60+
let mut read = MyRead::new();
61+
62+
read.read_to_end(&mut vec.inner).await.unwrap();
63+
})
64+
}

0 commit comments

Comments
 (0)