Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement read_buf and vectored read/write for SGX stdio #137355

Merged
merged 3 commits into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 48 additions & 16 deletions library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::super::mem::{is_enclave_range, is_user_range};
use crate::arch::asm;
use crate::cell::UnsafeCell;
use crate::convert::TryInto;
use crate::mem::{self, ManuallyDrop};
use crate::mem::{self, ManuallyDrop, MaybeUninit};
use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut};
use crate::pin::PinCoerceUnsized;
use crate::ptr::{self, NonNull};
Expand Down Expand Up @@ -209,6 +209,45 @@ impl<T: ?Sized> NewUserRef<NonNull<T>> for NonNull<UserRef<T>> {
}
}

/// A type which can a destination for safely copying from userspace.
///
/// # Safety
///
/// Requires that `T` and `Self` have identical layouts.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub unsafe trait UserSafeCopyDestination<T: ?Sized> {
/// Returns a pointer for writing to the value.
fn as_mut_ptr(&mut self) -> *mut T;
}

#[unstable(feature = "sgx_platform", issue = "56975")]
unsafe impl<T> UserSafeCopyDestination<T> for T {
fn as_mut_ptr(&mut self) -> *mut T {
self as _
}
}

#[unstable(feature = "sgx_platform", issue = "56975")]
unsafe impl<T> UserSafeCopyDestination<[T]> for [T] {
fn as_mut_ptr(&mut self) -> *mut [T] {
self as _
}
}

#[unstable(feature = "sgx_platform", issue = "56975")]
unsafe impl<T> UserSafeCopyDestination<T> for MaybeUninit<T> {
fn as_mut_ptr(&mut self) -> *mut T {
self as *mut Self as _
}
}

#[unstable(feature = "sgx_platform", issue = "56975")]
unsafe impl<T> UserSafeCopyDestination<[T]> for [MaybeUninit<T>] {
fn as_mut_ptr(&mut self) -> *mut [T] {
self as *mut Self as _
}
}

#[unstable(feature = "sgx_platform", issue = "56975")]
impl<T: ?Sized> User<T>
where
Expand Down Expand Up @@ -544,12 +583,12 @@ where
/// # Panics
/// This function panics if the destination doesn't have the same size as
/// the source. This can happen for dynamically-sized types such as slices.
pub fn copy_to_enclave(&self, dest: &mut T) {
pub fn copy_to_enclave<U: ?Sized + UserSafeCopyDestination<T>>(&self, dest: &mut U) {
unsafe {
assert_eq!(size_of_val(dest), size_of_val(&*self.0.get()));
copy_from_userspace(
self.0.get() as *const T as *const u8,
dest as *mut T as *mut u8,
dest.as_mut_ptr() as *mut u8,
size_of_val(dest),
);
}
Expand Down Expand Up @@ -639,25 +678,18 @@ where
unsafe { (*self.0.get()).len() }
}

/// Copies the value from user memory and place it into `dest`. Afterwards,
/// `dest` will contain exactly `self.len()` elements.
///
/// # Panics
/// This function panics if the destination doesn't have the same size as
/// the source. This can happen for dynamically-sized types such as slices.
pub fn copy_to_enclave_vec(&self, dest: &mut Vec<T>) {
if let Some(missing) = self.len().checked_sub(dest.capacity()) {
dest.reserve(missing)
}
/// Copies the value from user memory and appends it to `dest`.
pub fn append_to_enclave_vec(&self, dest: &mut Vec<T>) {
dest.reserve(self.len());
self.copy_to_enclave(&mut dest.spare_capacity_mut()[..self.len()]);
// SAFETY: We reserve enough space above.
unsafe { dest.set_len(self.len()) };
self.copy_to_enclave(&mut dest[..]);
unsafe { dest.set_len(dest.len() + self.len()) };
}

/// Copies the value from user memory into a vector in enclave memory.
pub fn to_enclave(&self) -> Vec<T> {
let mut ret = Vec::with_capacity(self.len());
self.copy_to_enclave_vec(&mut ret);
self.append_to_enclave_vec(&mut ret);
ret
}

Expand Down
17 changes: 16 additions & 1 deletion library/std/src/sys/pal/sgx/abi/usercalls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::cmp;
use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
use crate::io::{
BorrowedCursor, Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult,
};
use crate::random::{DefaultRandomSource, Random};
use crate::time::{Duration, Instant};

Expand Down Expand Up @@ -36,6 +38,19 @@ pub fn read(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> {
}
}

/// Usercall `read` with an uninitialized buffer. See the ABI documentation for
/// more information.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn read_buf(fd: Fd, mut buf: BorrowedCursor<'_>) -> IoResult<()> {
unsafe {
let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.capacity());
let len = raw::read(fd, userbuf.as_mut_ptr().cast(), userbuf.len()).from_sgx_result()?;
userbuf[..len].copy_to_enclave(&mut buf.as_mut()[..len]);
buf.advance_unchecked(len);
Ok(())
}
}

/// Usercall `read_alloc`. See the ABI documentation for more information.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn read_alloc(fd: Fd) -> IoResult<Vec<u8>> {
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/pal/sgx/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl FileDesc {
}

pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|b| self.read(b), buf)
usercalls::read_buf(self.fd, buf)
}

pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
Expand Down
33 changes: 32 additions & 1 deletion library/std/src/sys/stdio/sgx.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use fortanix_sgx_abi as abi;

use crate::io;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::sys::fd::FileDesc;

pub struct Stdin(());
Expand All @@ -24,6 +24,19 @@ impl io::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
with_std_fd(abi::FD_STDIN, |fd| fd.read(buf))
}

fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
with_std_fd(abi::FD_STDIN, |fd| fd.read_buf(buf))
}

fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
with_std_fd(abi::FD_STDIN, |fd| fd.read_vectored(bufs))
}

#[inline]
fn is_read_vectored(&self) -> bool {
true
}
}

impl Stdout {
Expand All @@ -40,6 +53,15 @@ impl io::Write for Stdout {
fn flush(&mut self) -> io::Result<()> {
with_std_fd(abi::FD_STDOUT, |fd| fd.flush())
}

fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
with_std_fd(abi::FD_STDOUT, |fd| fd.write_vectored(bufs))
}

#[inline]
fn is_write_vectored(&self) -> bool {
true
}
}

impl Stderr {
Expand All @@ -56,6 +78,15 @@ impl io::Write for Stderr {
fn flush(&mut self) -> io::Result<()> {
with_std_fd(abi::FD_STDERR, |fd| fd.flush())
}

fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
with_std_fd(abi::FD_STDERR, |fd| fd.write_vectored(bufs))
}

#[inline]
fn is_write_vectored(&self) -> bool {
true
}
}

pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
Expand Down
Loading