Skip to content

Commit 9951a8d

Browse files
committed
Read to uninitialized buffer
Currently, when using an uninitialized buffer for `read`, as would be typical in C or required for `Read::read_buf`, it is casted from `*mut u8` at the FFI boundary in `sys_read`/`sys_readv` to a `&[u8]`. I think this is unsound. Instead, use `&[MaybeUninit<u8>]` internally. I use this instead of `core::io::BorrowedCursor<'_>`, because there are currently no cases where the initialized portion needs to be separately tracked. This enables implementing `std::io::Read::read_buf` for `std::fs::File` and `std::io::Stdin` on Hermit. That effort is tracked in rust-lang/rust#136756.
1 parent 9123cda commit 9951a8d

File tree

12 files changed

+39
-29
lines changed

12 files changed

+39
-29
lines changed

src/fd/eventfd.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use alloc::boxed::Box;
22
use alloc::collections::vec_deque::VecDeque;
33
use core::future::{self, Future};
4-
use core::mem;
4+
use core::mem::{self, MaybeUninit};
55
use core::task::{Poll, Waker, ready};
66

77
use async_lock::Mutex;
@@ -45,7 +45,7 @@ impl EventFd {
4545

4646
#[async_trait]
4747
impl ObjectInterface for EventFd {
48-
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
48+
async fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
4949
let len = mem::size_of::<u64>();
5050

5151
if buf.len() < len {
@@ -58,8 +58,7 @@ impl ObjectInterface for EventFd {
5858
let mut guard = ready!(pinned.as_mut().poll(cx));
5959
if guard.counter > 0 {
6060
guard.counter -= 1;
61-
let tmp = u64::to_ne_bytes(1);
62-
buf[..len].copy_from_slice(&tmp);
61+
buf[..len].write_copy_of_slice(&u64::to_ne_bytes(1));
6362
if let Some(cx) = guard.write_queue.pop_front() {
6463
cx.wake_by_ref();
6564
}
@@ -74,7 +73,7 @@ impl ObjectInterface for EventFd {
7473
let tmp = guard.counter;
7574
if tmp > 0 {
7675
guard.counter = 0;
77-
buf[..len].copy_from_slice(&u64::to_ne_bytes(tmp));
76+
buf[..len].write_copy_of_slice(&u64::to_ne_bytes(tmp));
7877
if let Some(cx) = guard.read_queue.pop_front() {
7978
cx.wake_by_ref();
8079
}

src/fd/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use alloc::boxed::Box;
22
use alloc::sync::Arc;
33
use alloc::vec::Vec;
44
use core::future::{self, Future};
5+
use core::mem::MaybeUninit;
56
use core::task::Poll::{Pending, Ready};
67
use core::time::Duration;
78

@@ -152,7 +153,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug {
152153

153154
/// `async_read` attempts to read `len` bytes from the object references
154155
/// by the descriptor
155-
async fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
156+
async fn read(&self, _buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
156157
Err(io::Error::ENOSYS)
157158
}
158159

@@ -264,7 +265,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug {
264265
}
265266
}
266267

267-
pub(crate) fn read(fd: FileDescriptor, buf: &mut [u8]) -> io::Result<usize> {
268+
pub(crate) fn read(fd: FileDescriptor, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
268269
let obj = get_object(fd)?;
269270

270271
if buf.is_empty() {

src/fd/socket/tcp.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use alloc::boxed::Box;
22
use alloc::collections::BTreeSet;
33
use alloc::sync::Arc;
44
use core::future;
5+
use core::mem::MaybeUninit;
56
use core::sync::atomic::{AtomicU16, Ordering};
67
use core::task::Poll;
78

@@ -171,7 +172,7 @@ impl Socket {
171172
.await
172173
}
173174

174-
async fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
175+
async fn read(&self, buffer: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
175176
future::poll_fn(|cx| {
176177
self.with(|socket| {
177178
let state = socket.state();
@@ -187,7 +188,7 @@ impl Socket {
187188
socket
188189
.recv(|data| {
189190
let len = core::cmp::min(buffer.len(), data.len());
190-
buffer[..len].copy_from_slice(&data[..len]);
191+
buffer[..len].write_copy_of_slice(&data[..len]);
191192
(len, len)
192193
})
193194
.map_err(|_| io::Error::EIO),
@@ -468,7 +469,7 @@ impl ObjectInterface for async_lock::RwLock<Socket> {
468469
self.read().await.poll(event).await
469470
}
470471

471-
async fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
472+
async fn read(&self, buffer: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
472473
self.read().await.read(buffer).await
473474
}
474475

src/fd/socket/vsock.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use alloc::boxed::Box;
22
use alloc::sync::Arc;
33
use alloc::vec::Vec;
44
use core::future;
5+
use core::mem::MaybeUninit;
56
use core::task::Poll;
67

78
use async_trait::async_trait;
@@ -312,7 +313,7 @@ impl Socket {
312313
}
313314
}
314315

315-
async fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
316+
async fn read(&self, buffer: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
316317
let port = self.port;
317318
future::poll_fn(|cx| {
318319
let mut guard = VSOCK_MAP.lock();
@@ -331,7 +332,7 @@ impl Socket {
331332
}
332333
} else {
333334
let tmp: Vec<_> = raw.buffer.drain(..len).collect();
334-
buffer[..len].copy_from_slice(tmp.as_slice());
335+
buffer[..len].write_copy_of_slice(tmp.as_slice());
335336

336337
Poll::Ready(Ok(len))
337338
}
@@ -343,7 +344,7 @@ impl Socket {
343344
Poll::Ready(Ok(0))
344345
} else {
345346
let tmp: Vec<_> = raw.buffer.drain(..len).collect();
346-
buffer[..len].copy_from_slice(tmp.as_slice());
347+
buffer[..len].write_copy_of_slice(tmp.as_slice());
347348

348349
Poll::Ready(Ok(len))
349350
}
@@ -424,7 +425,7 @@ impl ObjectInterface for async_lock::RwLock<Socket> {
424425
self.read().await.poll(event).await
425426
}
426427

427-
async fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
428+
async fn read(&self, buffer: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
428429
self.read().await.read(buffer).await
429430
}
430431

src/fd/stdio.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use alloc::boxed::Box;
22
use core::future;
3+
use core::mem::MaybeUninit;
34
use core::task::Poll;
45

56
use async_trait::async_trait;
@@ -27,7 +28,7 @@ impl ObjectInterface for GenericStdin {
2728
Ok(event & available)
2829
}
2930

30-
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
31+
async fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
3132
future::poll_fn(|cx| {
3233
let mut read_bytes = 0;
3334
let mut guard = CONSOLE.lock();
@@ -36,7 +37,7 @@ impl ObjectInterface for GenericStdin {
3637
let c = unsafe { char::from_u32_unchecked(byte.into()) };
3738
guard.write(c.as_bytes());
3839

39-
buf[read_bytes] = byte;
40+
buf[read_bytes].write(byte);
4041
read_bytes += 1;
4142

4243
if read_bytes >= buf.len() {

src/fs/fuse.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use alloc::ffi::CString;
44
use alloc::string::String;
55
use alloc::sync::Arc;
66
use alloc::vec::Vec;
7+
use core::mem::MaybeUninit;
78
use core::sync::atomic::{AtomicU64, Ordering};
89
use core::task::Poll;
910
use core::{future, mem};
@@ -629,7 +630,7 @@ impl FuseFileHandleInner {
629630
.await
630631
}
631632

632-
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
633+
fn read(&mut self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
633634
let mut len = buf.len();
634635
if len > MAX_READ_LEN {
635636
debug!("Reading longer than max_read_len: {}", len);
@@ -651,7 +652,7 @@ impl FuseFileHandleInner {
651652
};
652653
self.offset += len;
653654

654-
buf[..len].copy_from_slice(&rsp.payload.unwrap()[..len]);
655+
buf[..len].write_copy_of_slice(&rsp.payload.unwrap()[..len]);
655656

656657
Ok(len)
657658
} else {
@@ -767,7 +768,7 @@ impl ObjectInterface for FuseFileHandle {
767768
self.0.lock().await.poll(event).await
768769
}
769770

770-
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
771+
async fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
771772
self.0.lock().await.read(buf)
772773
}
773774

src/fs/mem.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use alloc::collections::BTreeMap;
1414
use alloc::string::{String, ToString};
1515
use alloc::sync::Arc;
1616
use alloc::vec::Vec;
17+
use core::mem::MaybeUninit;
1718

1819
use async_lock::{Mutex, RwLock};
1920
use async_trait::async_trait;
@@ -59,7 +60,7 @@ impl ObjectInterface for RomFileInterface {
5960
Ok(ret)
6061
}
6162

62-
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
63+
async fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
6364
{
6465
let microseconds = arch::kernel::systemtime::now_micros();
6566
let t = timespec::from_usec(microseconds as i64);
@@ -81,7 +82,7 @@ impl ObjectInterface for RomFileInterface {
8182
buf.len()
8283
};
8384

84-
buf[0..len].clone_from_slice(&vec[pos..pos + len]);
85+
buf[..len].write_copy_of_slice(&vec[pos..pos + len]);
8586
*pos_guard = pos + len;
8687

8788
Ok(len)
@@ -170,7 +171,7 @@ impl ObjectInterface for RamFileInterface {
170171
Ok(event & available)
171172
}
172173

173-
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
174+
async fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
174175
{
175176
let microseconds = arch::kernel::systemtime::now_micros();
176177
let t = timespec::from_usec(microseconds as i64);
@@ -192,7 +193,7 @@ impl ObjectInterface for RamFileInterface {
192193
buf.len()
193194
};
194195

195-
buf[0..len].clone_from_slice(&guard.data[pos..pos + len]);
196+
buf[..len].write_copy_of_slice(&guard.data[pos..pos + len]);
196197
*pos_guard = pos + len;
197198

198199
Ok(len)
@@ -214,7 +215,7 @@ impl ObjectInterface for RamFileInterface {
214215
guard.attr.st_mtim = t;
215216
guard.attr.st_ctim = t;
216217

217-
guard.data[pos..pos + buf.len()].clone_from_slice(buf);
218+
guard.data[pos..pos + buf.len()].copy_from_slice(buf);
218219
*pos_guard = pos + buf.len();
219220

220221
Ok(buf.len())

src/fs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ impl File {
496496

497497
impl crate::io::Read for File {
498498
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
499+
let buf = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len()) };
499500
fd::read(self.fd, buf)
500501
}
501502
}

src/fs/uhyve.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use alloc::ffi::CString;
44
use alloc::string::{String, ToString};
55
use alloc::sync::Arc;
66
use alloc::vec::Vec;
7+
use core::mem::MaybeUninit;
78

89
use async_lock::Mutex;
910
use async_trait::async_trait;
@@ -29,7 +30,7 @@ impl UhyveFileHandleInner {
2930
Self(fd)
3031
}
3132

32-
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
33+
fn read(&mut self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
3334
let mut read_params = ReadParams {
3435
fd: self.0,
3536
buf: GuestVirtAddr::new(buf.as_mut_ptr() as u64),
@@ -94,7 +95,7 @@ impl UhyveFileHandle {
9495

9596
#[async_trait]
9697
impl ObjectInterface for UhyveFileHandle {
97-
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
98+
async fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
9899
self.0.lock().await.read(buf)
99100
}
100101

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#![feature(map_try_insert)]
1515
#![feature(maybe_uninit_as_bytes)]
1616
#![feature(maybe_uninit_slice)]
17+
#![feature(maybe_uninit_write_slice)]
1718
#![feature(naked_functions)]
1819
#![feature(never_type)]
1920
#![feature(slice_from_ptr_range)]

src/syscalls/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ pub extern "C" fn sys_close(fd: FileDescriptor) -> i32 {
387387
#[hermit_macro::system]
388388
#[unsafe(no_mangle)]
389389
pub unsafe extern "C" fn sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize {
390-
let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) };
390+
let slice = unsafe { core::slice::from_raw_parts_mut(buf.cast(), len) };
391391
crate::fd::read(fd, slice).map_or_else(
392392
|e| -num::ToPrimitive::to_isize(&e).unwrap(),
393393
|v| v.try_into().unwrap(),
@@ -420,7 +420,9 @@ pub unsafe extern "C" fn sys_readv(fd: i32, iov: *const iovec, iovcnt: usize) ->
420420
let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
421421

422422
for iovec_buf in iovec_buffers {
423-
let buf = unsafe { core::slice::from_raw_parts_mut(iovec_buf.iov_base, iovec_buf.iov_len) };
423+
let buf = unsafe {
424+
core::slice::from_raw_parts_mut(iovec_buf.iov_base.cast(), iovec_buf.iov_len)
425+
};
424426

425427
let len = crate::fd::read(fd, buf).map_or_else(
426428
|e| -num::ToPrimitive::to_isize(&e).unwrap(),

src/syscalls/socket.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ pub extern "C" fn sys_shutdown_socket(fd: i32, how: i32) -> i32 {
884884
#[unsafe(no_mangle)]
885885
pub unsafe extern "C" fn sys_recv(fd: i32, buf: *mut u8, len: usize, flags: i32) -> isize {
886886
if flags == 0 {
887-
let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) };
887+
let slice = unsafe { core::slice::from_raw_parts_mut(buf.cast(), len) };
888888
crate::fd::read(fd, slice).map_or_else(
889889
|e| -num::ToPrimitive::to_isize(&e).unwrap(),
890890
|v| v.try_into().unwrap(),

0 commit comments

Comments
 (0)