Skip to content

Commit 9bd085c

Browse files
io: fix unsound Buf::ensure_capacity_for
1 parent 970d880 commit 9bd085c

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

tokio/src/io/blocking.rs

+25
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub(crate) struct Blocking<T> {
2121
pub(crate) struct Buf {
2222
buf: Vec<u8>,
2323
pos: usize,
24+
init_len: usize,
2425
}
2526

2627
pub(crate) const DEFAULT_MAX_BUF_SIZE: usize = 2 * 1024 * 1024;
@@ -190,6 +191,7 @@ impl Buf {
190191
Buf {
191192
buf: Vec::with_capacity(n),
192193
pos: 0,
194+
init_len: 0,
193195
}
194196
}
195197

@@ -220,6 +222,7 @@ impl Buf {
220222
let n = cmp::min(src.len(), max_buf_size);
221223

222224
self.buf.extend_from_slice(&src[..n]);
225+
self.init_len = cmp::max(self.init_len, self.buf.len());
223226
n
224227
}
225228

@@ -236,6 +239,27 @@ impl Buf {
236239
self.buf.reserve(len - self.buf.len());
237240
}
238241

242+
if self.init_len < len {
243+
debug_assert!(
244+
self.init_len < self.buf.capacity(),
245+
"init_len of Vec is bigger than the capacity"
246+
);
247+
debug_assert!(
248+
len <= self.buf.capacity(),
249+
"uninit area of Vec is bigger than the capacity"
250+
);
251+
252+
let uninit_len = len - self.init_len;
253+
// SAFETY: the area is within the allocation of the Vec
254+
unsafe {
255+
self.buf
256+
.as_mut_ptr()
257+
.add(self.init_len)
258+
.write_bytes(0, uninit_len);
259+
}
260+
}
261+
262+
// SAFETY: `len` is within the capacity and is init
239263
unsafe {
240264
self.buf.set_len(len);
241265
}
@@ -287,6 +311,7 @@ cfg_fs! {
287311
self.buf.extend_from_slice(&buf[..len]);
288312
rem -= len;
289313
}
314+
self.init_len = cmp::max(self.init_len, self.buf.len());
290315

291316
max_buf_size - rem
292317
}

tokio/src/io/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,6 @@ cfg_io_blocking! {
293293
pub(crate) use crate::blocking::JoinHandle as Blocking;
294294
}
295295
}
296+
297+
#[cfg(test)]
298+
mod tests;

tokio/src/io/tests.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use std::{
2+
io::{self, Read},
3+
mem::MaybeUninit,
4+
};
5+
6+
use super::blocking::Buf;
7+
use crate::io::ReadBuf;
8+
9+
// Have miri check that `Buf::ensure_capacity_for` initializes its length.
10+
#[test]
11+
fn buf_ensure_capacity_for_len_is_init() {
12+
const MAX_BUF_SIZE: usize = 128;
13+
14+
let mut buf = Buf::with_capacity(0);
15+
16+
let mut dst = [MaybeUninit::uninit(); 96];
17+
buf.ensure_capacity_for(&ReadBuf::uninit(&mut dst), MAX_BUF_SIZE);
18+
let res = buf
19+
.read_from(&mut EnsureInitReader(&[123u8; 64][..]))
20+
.unwrap();
21+
assert_eq!(res, 64);
22+
assert_eq!(buf.bytes(), [123u8; 64]);
23+
}
24+
25+
struct EnsureInitReader<R>(R);
26+
27+
impl<R: Read> Read for EnsureInitReader<R> {
28+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
29+
// ensure `buf` is init when run under miri
30+
for &_b in &*buf {}
31+
32+
self.0.read(buf)
33+
}
34+
}

0 commit comments

Comments
 (0)