Skip to content

Commit 90f0629

Browse files
io: extend Buf length only after having read into it
1 parent bd3e857 commit 90f0629

File tree

6 files changed

+56
-24
lines changed

6 files changed

+56
-24
lines changed

tokio/src/fs/file.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::io::blocking::{Buf, DEFAULT_MAX_BUF_SIZE};
77
use crate::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
88
use crate::sync::Mutex;
99

10+
use std::cmp;
1011
use std::fmt;
1112
use std::fs::{Metadata, Permissions};
1213
use std::future::Future;
@@ -600,11 +601,14 @@ impl AsyncRead for File {
600601
return Poll::Ready(Ok(()));
601602
}
602603

603-
buf.ensure_capacity_for(dst, me.max_buf_size);
604604
let std = me.std.clone();
605605

606+
let max_buf_size = cmp::min(dst.remaining(), me.max_buf_size);
606607
inner.state = State::Busy(spawn_blocking(move || {
607-
let res = buf.read_from(&mut &*std);
608+
// SAFETY: the `Read` implementation of `std` does not
609+
// read from the buffer it is borrowing and correctly
610+
// reports the length of the data written into the buffer.
611+
let res = unsafe { buf.read_from(&mut &*std, max_buf_size) };
608612
(Operation::Read(res), buf)
609613
}));
610614
}

tokio/src/io/blocking.rs

+30-18
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::cmp;
55
use std::future::Future;
66
use std::io;
77
use std::io::prelude::*;
8+
use std::mem::MaybeUninit;
89
use std::pin::Pin;
910
use std::task::{ready, Context, Poll};
1011

@@ -33,8 +34,13 @@ enum State<T> {
3334

3435
cfg_io_blocking! {
3536
impl<T> Blocking<T> {
37+
/// # Safety
38+
///
39+
/// The `Read` implementation of `inner` must never read from the buffer
40+
/// it is borrowing and must correctly report the length of the data
41+
/// written into the buffer.
3642
#[cfg_attr(feature = "fs", allow(dead_code))]
37-
pub(crate) fn new(inner: T) -> Blocking<T> {
43+
pub(crate) unsafe fn new(inner: T) -> Blocking<T> {
3844
Blocking {
3945
inner: Some(inner),
4046
state: State::Idle(Some(Buf::with_capacity(0))),
@@ -64,11 +70,12 @@ where
6470
return Poll::Ready(Ok(()));
6571
}
6672

67-
buf.ensure_capacity_for(dst, DEFAULT_MAX_BUF_SIZE);
6873
let mut inner = self.inner.take().unwrap();
6974

75+
let max_buf_size = cmp::min(dst.remaining(), DEFAULT_MAX_BUF_SIZE);
7076
self.state = State::Busy(sys::run(move || {
71-
let res = buf.read_from(&mut inner);
77+
// SAFETY: the requirements are satisfied by `Blocking::new`.
78+
let res = unsafe { buf.read_from(&mut inner, max_buf_size) };
7279
(res, buf, inner)
7380
}));
7481
}
@@ -227,25 +234,30 @@ impl Buf {
227234
&self.buf[self.pos..]
228235
}
229236

230-
pub(crate) fn ensure_capacity_for(&mut self, bytes: &ReadBuf<'_>, max_buf_size: usize) {
237+
/// # Safety
238+
///
239+
/// `rd` must not read from the buffer `read` is borrowing and must correctly
240+
/// report the length of the data written into the buffer.
241+
pub(crate) unsafe fn read_from<T: Read>(
242+
&mut self,
243+
rd: &mut T,
244+
max_buf_size: usize,
245+
) -> io::Result<usize> {
231246
assert!(self.is_empty());
247+
self.buf.reserve(max_buf_size);
232248

233-
let len = cmp::min(bytes.remaining(), max_buf_size);
234-
235-
if self.buf.len() < len {
236-
self.buf.reserve(len - self.buf.len());
237-
}
238-
239-
unsafe {
240-
self.buf.set_len(len);
241-
}
242-
}
243-
244-
pub(crate) fn read_from<T: Read>(&mut self, rd: &mut T) -> io::Result<usize> {
245-
let res = uninterruptibly!(rd.read(&mut self.buf));
249+
let buf = &mut self.buf.spare_capacity_mut()[..max_buf_size];
250+
// SAFETY: The memory may be uninitialized, but `rd.read` will only write to the buffer.
251+
let buf = unsafe { &mut *(buf as *mut [MaybeUninit<u8>] as *mut [u8]) };
252+
let res = uninterruptibly!(rd.read(buf));
246253

247254
if let Ok(n) = res {
248-
self.buf.truncate(n);
255+
// SAFETY: the caller promises that `rd.read` initializes
256+
// a section of `buf` and correctly reports that length.
257+
// The `self.is_empty()` assertion verifies that `n`
258+
// equals the length of the `buf` capacity that was written
259+
// to (and that `buf` isn't being shrunk).
260+
unsafe { self.buf.set_len(n) }
249261
} else {
250262
self.buf.clear();
251263
}

tokio/src/io/stderr.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,12 @@ cfg_io_std! {
6767
/// ```
6868
pub fn stderr() -> Stderr {
6969
let std = io::stderr();
70+
// SAFETY: The `Read` implementation of `std` does not read from the
71+
// buffer it is borrowing and correctly reports the length of the data
72+
// written into the buffer.
73+
let blocking = unsafe { Blocking::new(std) };
7074
Stderr {
71-
std: SplitByUtf8BoundaryIfWindows::new(Blocking::new(std)),
75+
std: SplitByUtf8BoundaryIfWindows::new(blocking),
7276
}
7377
}
7478
}

tokio/src/io/stdin.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@ cfg_io_std! {
4242
/// user input and use blocking IO directly in that thread.
4343
pub fn stdin() -> Stdin {
4444
let std = io::stdin();
45+
// SAFETY: The `Read` implementation of `std` does not read from the
46+
// buffer it is borrowing and correctly reports the length of the data
47+
// written into the buffer.
48+
let std = unsafe {Blocking::new(std)};
4549
Stdin {
46-
std: Blocking::new(std),
50+
std,
4751
}
4852
}
4953
}

tokio/src/io/stdout.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ cfg_io_std! {
116116
/// ```
117117
pub fn stdout() -> Stdout {
118118
let std = io::stdout();
119+
// SAFETY: The `Read` implementation of `std` does not read from the
120+
// buffer it is borrowing and correctly reports the length of the data
121+
// written into the buffer.
122+
let blocking = unsafe {Blocking::new(std)};
119123
Stdout {
120-
std: SplitByUtf8BoundaryIfWindows::new(Blocking::new(std)),
124+
std: SplitByUtf8BoundaryIfWindows::new(blocking),
121125
}
122126
}
123127
}

tokio/src/process/windows.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,11 @@ where
242242
use std::os::windows::prelude::FromRawHandle;
243243

244244
let raw = Arc::new(unsafe { StdFile::from_raw_handle(io.into_raw_handle()) });
245-
let io = Blocking::new(ArcFile(raw.clone()));
245+
let io = ArcFile(raw.clone());
246+
// SAFETY: the `Read` implementation of `io` does not
247+
// read from the buffer it is borrowing and correctly
248+
// reports the length of the data written into the buffer.
249+
let io = unsafe { Blocking::new(io) };
246250
Ok(ChildStdio { raw, io })
247251
}
248252

0 commit comments

Comments
 (0)