Optimize the perf of sendmsg/recvmsg by allocating untrusted buffers directly
It is slow to allocate big buffers using SGX SDK's malloc. Even worse, it consumes a large amount of precious trusted memory inside enclaves. This commit avoids using trusted buffers and allocates untrusted buffers for sendmsg/recvmsg directly via OCall, thus improving the performance of sendmsg/recvmsg. Note that this optimization does not affect the security of network data as it has to be sent/received via OCalls.
This commit is contained in:
parent
c3d042dcd0
commit
e352a190ea
@ -57,6 +57,9 @@ enclave {
|
||||
|
||||
void occlum_ocall_sync(void);
|
||||
|
||||
void* occlum_ocall_posix_memalign(size_t alignment, size_t size);
|
||||
void occlum_ocall_free([user_check] void* ptr);
|
||||
|
||||
void occlum_ocall_sched_yield(void);
|
||||
int occlum_ocall_sched_getaffinity(
|
||||
int host_tid,
|
||||
@ -87,8 +90,8 @@ enclave {
|
||||
int sockfd,
|
||||
[in, size=msg_namelen] const void* msg_name,
|
||||
socklen_t msg_namelen,
|
||||
[in, size=buf_len] const void* buf,
|
||||
size_t buf_len,
|
||||
[in, count=msg_iovlen] const struct iovec* msg_iov,
|
||||
size_t msg_iovlen,
|
||||
[in, size=msg_controllen] const void* msg_control,
|
||||
size_t msg_controllen,
|
||||
int flags
|
||||
@ -98,8 +101,8 @@ enclave {
|
||||
[out, size=msg_namelen] void *msg_name,
|
||||
socklen_t msg_namelen,
|
||||
[out] socklen_t* msg_namelen_recv,
|
||||
[out, size=buf_len] void* buf,
|
||||
size_t buf_len,
|
||||
[in, count=msg_iovlen] struct iovec* msg_iov,
|
||||
size_t msg_iovlen,
|
||||
[out, size=msg_controllen] void *msg_control,
|
||||
size_t msg_controllen,
|
||||
[out] size_t* msg_controllen_recv,
|
||||
|
@ -98,3 +98,15 @@ impl ToErrno for rcore_fs::vfs::FsError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::alloc::AllocErr {
|
||||
fn errno(&self) -> Errno {
|
||||
ENOMEM
|
||||
}
|
||||
}
|
||||
|
||||
impl ToErrno for std::alloc::LayoutErr {
|
||||
fn errno(&self) -> Errno {
|
||||
EINVAL
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(atomic_min_max)]
|
||||
#![feature(no_more_cas)]
|
||||
#![feature(alloc_layout_extra)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
@ -59,6 +61,7 @@ mod net;
|
||||
mod process;
|
||||
mod syscall;
|
||||
mod time;
|
||||
mod untrusted;
|
||||
mod util;
|
||||
mod vm;
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! I/O vectors
|
||||
|
||||
use super::*;
|
||||
use crate::untrusted::SliceAsPtrAndLen;
|
||||
use std::iter::Iterator;
|
||||
|
||||
/// A memory safe, immutable version of C iovec array
|
||||
pub struct Iovs<'a> {
|
||||
@ -19,19 +21,6 @@ impl<'a> Iovs<'a> {
|
||||
pub fn total_bytes(&self) -> usize {
|
||||
self.iovs.iter().map(|s| s.len()).sum()
|
||||
}
|
||||
|
||||
pub fn gather_to_vec(&self) -> Vec<u8> {
|
||||
Self::gather_slices_to_vec(&self.iovs[..])
|
||||
}
|
||||
|
||||
fn gather_slices_to_vec(slices: &[&[u8]]) -> Vec<u8> {
|
||||
let vec_len = slices.iter().map(|slice| slice.len()).sum();
|
||||
let mut vec = Vec::with_capacity(vec_len);
|
||||
for slice in slices {
|
||||
vec.extend_from_slice(slice);
|
||||
}
|
||||
vec
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory safe, mutable version of C iovec array
|
||||
@ -59,30 +48,41 @@ impl<'a> IovsMut<'a> {
|
||||
self.iovs.iter().map(|s| s.len()).sum()
|
||||
}
|
||||
|
||||
pub fn gather_to_vec(&self) -> Vec<u8> {
|
||||
Iovs::gather_slices_to_vec(self.as_slices())
|
||||
/// Copy as many bytes from an u8 iterator as possible
|
||||
pub fn copy_from_iter<'b, T>(&mut self, src_iter: &mut T) -> usize
|
||||
where
|
||||
T: Iterator<Item = &'b u8>,
|
||||
{
|
||||
let mut bytes_copied = 0;
|
||||
let mut dst_iter = self
|
||||
.as_slices_mut()
|
||||
.iter_mut()
|
||||
.flat_map(|mut slice| slice.iter_mut());
|
||||
while let (Some(mut d), Some(s)) = (dst_iter.next(), src_iter.next()) {
|
||||
*d = *s;
|
||||
bytes_copied += 1;
|
||||
}
|
||||
bytes_copied
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scatter_copy_from(&mut self, data: &[u8]) -> usize {
|
||||
let mut total_nbytes = 0;
|
||||
let mut remain_slice = data;
|
||||
for iov in &mut self.iovs {
|
||||
if remain_slice.len() == 0 {
|
||||
break;
|
||||
/// An extention trait that converts slice to libc::iovec
|
||||
pub trait SliceAsLibcIovec {
|
||||
fn as_libc_iovec(&self) -> libc::iovec;
|
||||
}
|
||||
|
||||
let copy_nbytes = remain_slice.len().min(iov.len());
|
||||
let dst_slice = unsafe {
|
||||
debug_assert!(iov.len() >= copy_nbytes);
|
||||
iov.get_unchecked_mut(..copy_nbytes)
|
||||
};
|
||||
let (src_slice, _remain_slice) = remain_slice.split_at(copy_nbytes);
|
||||
dst_slice.copy_from_slice(src_slice);
|
||||
impl SliceAsLibcIovec for &[u8] {
|
||||
fn as_libc_iovec(&self) -> libc::iovec {
|
||||
let (iov_base, iov_len) = self.as_ptr_and_len();
|
||||
let iov_base = iov_base as *mut u8 as *mut c_void;
|
||||
libc::iovec { iov_base, iov_len }
|
||||
}
|
||||
}
|
||||
|
||||
remain_slice = _remain_slice;
|
||||
total_nbytes += copy_nbytes;
|
||||
}
|
||||
debug_assert!(remain_slice.len() == 0);
|
||||
total_nbytes
|
||||
impl SliceAsLibcIovec for &mut [u8] {
|
||||
fn as_libc_iovec(&self) -> libc::iovec {
|
||||
let (iov_base, iov_len) = self.as_ptr_and_len();
|
||||
let iov_base = iov_base as *mut u8 as *mut c_void;
|
||||
libc::iovec { iov_base, iov_len }
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use std::*;
|
||||
|
||||
mod iovs;
|
||||
mod msg;
|
||||
@ -6,7 +7,7 @@ mod msg_flags;
|
||||
mod socket_file;
|
||||
mod syscalls;
|
||||
|
||||
pub use self::iovs::{Iovs, IovsMut};
|
||||
pub use self::iovs::{Iovs, IovsMut, SliceAsLibcIovec};
|
||||
pub use self::msg::{msghdr, msghdr_mut, MsgHdr, MsgHdrMut};
|
||||
pub use self::msg_flags::MsgFlags;
|
||||
pub use self::socket_file::{AsSocket, SocketFile};
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen, UntrustedSliceAlloc};
|
||||
|
||||
impl SocketFile {
|
||||
// TODO: need sockaddr type to implement send/sento
|
||||
@ -19,37 +20,48 @@ impl SocketFile {
|
||||
}*/
|
||||
|
||||
pub fn recvmsg<'a, 'b>(&self, msg: &'b mut MsgHdrMut<'a>, flags: MsgFlags) -> Result<usize> {
|
||||
// Allocate a single data buffer is big enough for all iovecs of msg.
|
||||
// This is a workaround for the OCall that takes only one data buffer.
|
||||
let mut data_buf = {
|
||||
let data_buf_len = msg.get_iovs().total_bytes();
|
||||
let data_vec = vec![0; data_buf_len];
|
||||
data_vec.into_boxed_slice()
|
||||
};
|
||||
// Alloc untrusted iovecs to receive data via OCall
|
||||
let msg_iov = msg.get_iovs();
|
||||
let u_slice_alloc = UntrustedSliceAlloc::new(msg_iov.total_bytes())?;
|
||||
let mut u_slices = msg_iov
|
||||
.as_slices()
|
||||
.iter()
|
||||
.map(|slice| {
|
||||
u_slice_alloc
|
||||
.new_slice_mut(slice.len())
|
||||
.expect("unexpected out of memory error in UntrustedSliceAlloc")
|
||||
})
|
||||
.collect();
|
||||
let mut u_iovs = IovsMut::new(u_slices);
|
||||
|
||||
// Do OCall-based recvmsg
|
||||
let (bytes_recvd, namelen_recvd, controllen_recvd, flags_recvd) = {
|
||||
let data = &mut data_buf[..];
|
||||
// Acquire mutable references to the name and control buffers
|
||||
let (name, control) = msg.get_name_and_control_mut();
|
||||
// Fill the data, the name, and the control buffers
|
||||
self.do_recvmsg(data, flags, name, control)?
|
||||
self.do_recvmsg(u_iovs.as_slices_mut(), flags, name, control)?
|
||||
};
|
||||
|
||||
// Update the lengths and flags
|
||||
// Update the output lengths and flags
|
||||
msg.set_name_len(namelen_recvd)?;
|
||||
msg.set_control_len(controllen_recvd)?;
|
||||
msg.set_flags(flags_recvd);
|
||||
|
||||
let recv_data = &data_buf[..bytes_recvd];
|
||||
// TODO: avoid this one extra copy due to the intermediate data buffer
|
||||
msg.get_iovs_mut().scatter_copy_from(recv_data);
|
||||
// Copy data from untrusted iovecs into the output iovecs
|
||||
let mut msg_iov = msg.get_iovs_mut();
|
||||
let mut u_iovs_iter = u_iovs
|
||||
.as_slices()
|
||||
.iter()
|
||||
.flat_map(|slice| slice.iter())
|
||||
.take(bytes_recvd);
|
||||
msg_iov.copy_from_iter(&mut u_iovs_iter);
|
||||
|
||||
Ok(bytes_recvd)
|
||||
}
|
||||
|
||||
fn do_recvmsg(
|
||||
&self,
|
||||
data: &mut [u8],
|
||||
data: &mut [&mut [u8]],
|
||||
flags: MsgFlags,
|
||||
mut name: Option<&mut [u8]>,
|
||||
mut control: Option<&mut [u8]>,
|
||||
@ -58,14 +70,15 @@ impl SocketFile {
|
||||
// Host socket fd
|
||||
let host_fd = self.host_fd;
|
||||
// Name
|
||||
let (msg_name, msg_namelen) = name.get_mut_ptr_and_len();
|
||||
let (msg_name, msg_namelen) = name.as_mut_ptr_and_len();
|
||||
let msg_name = msg_name as *mut c_void;
|
||||
let mut msg_namelen_recvd = 0_u32;
|
||||
// Data
|
||||
let msg_data = data.as_mut_ptr();
|
||||
let msg_datalen = data.len();
|
||||
// Iovs
|
||||
let mut raw_iovs: Vec<libc::iovec> =
|
||||
data.iter().map(|slice| slice.as_libc_iovec()).collect();
|
||||
let (msg_iov, msg_iovlen) = raw_iovs.as_mut_slice().as_mut_ptr_and_len();
|
||||
// Control
|
||||
let (msg_control, msg_controllen) = control.get_mut_ptr_and_len();
|
||||
let (msg_control, msg_controllen) = control.as_mut_ptr_and_len();
|
||||
let msg_control = msg_control as *mut c_void;
|
||||
let mut msg_controllen_recvd = 0;
|
||||
// Flags
|
||||
@ -81,8 +94,8 @@ impl SocketFile {
|
||||
msg_name,
|
||||
msg_namelen as u32,
|
||||
&mut msg_namelen_recvd as *mut u32,
|
||||
msg_data,
|
||||
msg_datalen,
|
||||
msg_iov,
|
||||
msg_iovlen,
|
||||
msg_control,
|
||||
msg_controllen,
|
||||
&mut msg_controllen_recvd as *mut usize,
|
||||
@ -103,7 +116,8 @@ impl SocketFile {
|
||||
let retval = retval as usize;
|
||||
|
||||
// Check bytes_recvd returned from outside the enclave
|
||||
assert!(retval <= data.len());
|
||||
let max_bytes_recvd = data.iter().map(|x| x.len()).sum();
|
||||
assert!(retval <= max_bytes_recvd);
|
||||
retval
|
||||
};
|
||||
let msg_namelen_recvd = msg_namelen_recvd as usize;
|
||||
@ -127,8 +141,8 @@ extern "C" {
|
||||
msg_name: *mut c_void,
|
||||
msg_namelen: libc::socklen_t,
|
||||
msg_namelen_recv: *mut libc::socklen_t,
|
||||
msg_data: *mut u8,
|
||||
msg_data: size_t,
|
||||
msg_data: *mut libc::iovec,
|
||||
msg_datalen: size_t,
|
||||
msg_control: *mut c_void,
|
||||
msg_controllen: size_t,
|
||||
msg_controllen_recv: *mut size_t,
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::untrusted::{SliceAsMutPtrAndLen, SliceAsPtrAndLen, UntrustedSliceAlloc};
|
||||
|
||||
impl SocketFile {
|
||||
// TODO: need sockaddr type to implement send/sento
|
||||
@ -18,44 +19,55 @@ impl SocketFile {
|
||||
*/
|
||||
|
||||
pub fn sendmsg<'a, 'b>(&self, msg: &'b MsgHdr<'a>, flags: MsgFlags) -> Result<usize> {
|
||||
// Copy data in iovs into a single buffer
|
||||
let data_buf = msg.get_iovs().gather_to_vec();
|
||||
// Copy message's iovecs into untrusted iovecs
|
||||
let msg_iov = msg.get_iovs();
|
||||
let u_slice_alloc = UntrustedSliceAlloc::new(msg_iov.total_bytes())?;
|
||||
let u_slices = msg_iov
|
||||
.as_slices()
|
||||
.iter()
|
||||
.map(|src_slice| {
|
||||
u_slice_alloc
|
||||
.new_slice(src_slice)
|
||||
.expect("unexpected out of memory")
|
||||
})
|
||||
.collect();
|
||||
let u_iovs = Iovs::new(u_slices);
|
||||
|
||||
self.do_sendmsg(&data_buf[..], flags, msg.get_name(), msg.get_control())
|
||||
self.do_sendmsg(u_iovs.as_slices(), flags, msg.get_name(), msg.get_control())
|
||||
}
|
||||
|
||||
fn do_sendmsg(
|
||||
&self,
|
||||
data: &[u8],
|
||||
data: &[&[u8]],
|
||||
flags: MsgFlags,
|
||||
name: Option<&[u8]>,
|
||||
control: Option<&[u8]>,
|
||||
) -> Result<usize> {
|
||||
let bytes_sent = try_libc!({
|
||||
// Prepare the arguments for OCall
|
||||
let mut retval: isize = 0;
|
||||
// Host socket fd
|
||||
let host_fd = self.host_fd;
|
||||
// Name
|
||||
let (msg_name, msg_namelen) = name.get_ptr_and_len();
|
||||
let (msg_name, msg_namelen) = name.as_ptr_and_len();
|
||||
let msg_name = msg_name as *const c_void;
|
||||
// Data
|
||||
let msg_data = data.as_ptr();
|
||||
let msg_datalen = data.len();
|
||||
// Iovs
|
||||
let raw_iovs: Vec<libc::iovec> = data.iter().map(|slice| slice.as_libc_iovec()).collect();
|
||||
let (msg_iov, msg_iovlen) = raw_iovs.as_slice().as_ptr_and_len();
|
||||
// Control
|
||||
let (msg_control, msg_controllen) = control.get_ptr_and_len();
|
||||
let (msg_control, msg_controllen) = control.as_ptr_and_len();
|
||||
let msg_control = msg_control as *const c_void;
|
||||
// Flags
|
||||
let flags = flags.to_u32() as i32;
|
||||
|
||||
let bytes_sent = try_libc!({
|
||||
// Do OCall
|
||||
let status = occlum_ocall_sendmsg(
|
||||
&mut retval as *mut isize,
|
||||
host_fd,
|
||||
msg_name,
|
||||
msg_namelen as u32,
|
||||
msg_data,
|
||||
msg_datalen,
|
||||
msg_iov,
|
||||
msg_iovlen,
|
||||
msg_control,
|
||||
msg_controllen,
|
||||
flags,
|
||||
@ -75,7 +87,7 @@ extern "C" {
|
||||
fd: c_int,
|
||||
msg_name: *const c_void,
|
||||
msg_namelen: libc::socklen_t,
|
||||
msg_data: *const u8,
|
||||
msg_data: *const libc::iovec,
|
||||
msg_datalen: size_t,
|
||||
msg_control: *const c_void,
|
||||
msg_controllen: size_t,
|
||||
|
@ -31,29 +31,3 @@ pub fn align_down(addr: usize, align: usize) -> usize {
|
||||
pub fn unbox<T>(value: Box<T>) -> T {
|
||||
*value
|
||||
}
|
||||
|
||||
pub trait SliceOptionExt<T> {
|
||||
fn get_ptr_and_len(&self) -> (*const T, usize);
|
||||
}
|
||||
|
||||
impl<T> SliceOptionExt<T> for Option<&[T]> {
|
||||
fn get_ptr_and_len(&self) -> (*const T, usize) {
|
||||
match self {
|
||||
Some(self_slice) => (self_slice.as_ptr(), self_slice.len()),
|
||||
None => (std::ptr::null(), 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MutSliceOptionExt<T> {
|
||||
fn get_mut_ptr_and_len(&mut self) -> (*mut T, usize);
|
||||
}
|
||||
|
||||
impl<T> MutSliceOptionExt<T> for Option<&mut [T]> {
|
||||
fn get_mut_ptr_and_len(&mut self) -> (*mut T, usize) {
|
||||
match self {
|
||||
Some(self_slice) => (self_slice.as_mut_ptr(), self_slice.len()),
|
||||
None => (std::ptr::null_mut(), 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/libos/src/untrusted/alloc.rs
Normal file
62
src/libos/src/untrusted/alloc.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use super::*;
|
||||
use std::alloc::{Alloc, AllocErr, Layout};
|
||||
use std::ptr::{self, NonNull};
|
||||
|
||||
/// The global memory allocator for untrusted memory
|
||||
pub static mut UNTRUSTED_ALLOC: UntrustedAlloc = UntrustedAlloc;
|
||||
|
||||
pub struct UntrustedAlloc;
|
||||
|
||||
unsafe impl Alloc for UntrustedAlloc {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> std::result::Result<NonNull<u8>, AllocErr> {
|
||||
if layout.size() == 0 {
|
||||
return Err(AllocErr);
|
||||
}
|
||||
|
||||
// Do OCall to allocate the untrusted memory according to the given layout
|
||||
let layout = layout
|
||||
.align_to(std::mem::size_of::<*const c_void>())
|
||||
.unwrap();
|
||||
let mem_ptr = {
|
||||
let mut mem_ptr: *mut c_void = ptr::null_mut();
|
||||
let sgx_status = unsafe {
|
||||
occlum_ocall_posix_memalign(&mut mem_ptr as *mut _, layout.align(), layout.size())
|
||||
};
|
||||
debug_assert!(sgx_status == sgx_status_t::SGX_SUCCESS);
|
||||
mem_ptr
|
||||
} as *mut u8;
|
||||
if mem_ptr == std::ptr::null_mut() {
|
||||
return Err(AllocErr);
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
// Post-condition 1: alignment
|
||||
debug_assert!(mem_ptr as usize % layout.align() == 0);
|
||||
// Post-condition 2: out-of-enclave
|
||||
assert!(sgx_trts::trts::rsgx_raw_is_outside_enclave(
|
||||
mem_ptr as *const u8,
|
||||
layout.size()
|
||||
));
|
||||
Ok(NonNull::new(mem_ptr).unwrap())
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
|
||||
// Pre-condition: out-of-enclave
|
||||
debug_assert!(sgx_trts::trts::rsgx_raw_is_outside_enclave(
|
||||
ptr.as_ptr(),
|
||||
layout.size()
|
||||
));
|
||||
|
||||
let sgx_status = unsafe { occlum_ocall_free(ptr.as_ptr() as *mut c_void) };
|
||||
debug_assert!(sgx_status == sgx_status_t::SGX_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn occlum_ocall_posix_memalign(
|
||||
ptr: *mut *mut c_void,
|
||||
align: usize, // must be power of two and a multiple of sizeof(void*)
|
||||
size: usize,
|
||||
) -> sgx_status_t;
|
||||
fn occlum_ocall_free(ptr: *mut c_void) -> sgx_status_t;
|
||||
}
|
10
src/libos/src/untrusted/mod.rs
Normal file
10
src/libos/src/untrusted/mod.rs
Normal file
@ -0,0 +1,10 @@
|
||||
/// Manipulate and access untrusted memory or functionalities safely
|
||||
mod alloc;
|
||||
mod slice_alloc;
|
||||
mod slice_ext;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub use self::alloc::UNTRUSTED_ALLOC;
|
||||
pub use self::slice_alloc::UntrustedSliceAlloc;
|
||||
pub use self::slice_ext::{SliceAsMutPtrAndLen, SliceAsPtrAndLen};
|
81
src/libos/src/untrusted/slice_alloc.rs
Normal file
81
src/libos/src/untrusted/slice_alloc.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use super::*;
|
||||
use std::alloc::{Alloc, AllocErr, Layout};
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
/// An memory allocator for slices, backed by a fixed-size, untrusted buffer
|
||||
pub struct UntrustedSliceAlloc {
|
||||
/// The pointer to the untrusted buffer
|
||||
buf_ptr: *mut u8,
|
||||
/// The size of the untrusted buffer
|
||||
buf_size: usize,
|
||||
/// The next position to allocate new slice
|
||||
/// New slices must be allocated from [buf_ptr + buf_pos, buf_ptr + buf_size)
|
||||
buf_pos: AtomicUsize,
|
||||
}
|
||||
|
||||
impl UntrustedSliceAlloc {
|
||||
pub fn new(buf_size: usize) -> Result<Self> {
|
||||
if buf_size == 0 {
|
||||
// Create a dummy object
|
||||
return Ok(Self {
|
||||
buf_ptr: std::ptr::null_mut(),
|
||||
buf_size: 0,
|
||||
buf_pos: AtomicUsize::new(0),
|
||||
});
|
||||
}
|
||||
|
||||
let layout = Layout::from_size_align(buf_size, 1)?;
|
||||
let buf_ptr = unsafe { UNTRUSTED_ALLOC.alloc(layout)?.as_ptr() };
|
||||
let buf_pos = AtomicUsize::new(0);
|
||||
Ok(Self {
|
||||
buf_ptr,
|
||||
buf_size,
|
||||
buf_pos,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_slice(&self, src_slice: &[u8]) -> Result<&[u8]> {
|
||||
let mut new_slice = self.new_slice_mut(src_slice.len())?;
|
||||
new_slice.copy_from_slice(src_slice);
|
||||
Ok(new_slice)
|
||||
}
|
||||
|
||||
pub fn new_slice_mut(&self, new_slice_len: usize) -> Result<&mut [u8]> {
|
||||
let new_slice_ptr = {
|
||||
// Move self.buf_pos forward if enough space _atomically_.
|
||||
let old_pos = self
|
||||
.buf_pos
|
||||
.fetch_update(
|
||||
|old_pos| {
|
||||
let new_pos = old_pos + new_slice_len;
|
||||
if new_pos <= self.buf_size {
|
||||
Some(new_pos)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
Ordering::SeqCst,
|
||||
Ordering::SeqCst,
|
||||
)
|
||||
.map_err(|e| errno!(ENOMEM, "No enough space"))?;
|
||||
unsafe { self.buf_ptr.add(old_pos) }
|
||||
};
|
||||
let new_slice = unsafe { std::slice::from_raw_parts_mut(new_slice_ptr, new_slice_len) };
|
||||
Ok(new_slice)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UntrustedSliceAlloc {
|
||||
fn drop(&mut self) {
|
||||
// Do nothing for the dummy case
|
||||
if self.buf_size == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let layout = Layout::from_size_align(self.buf_size, 1).unwrap();
|
||||
unsafe {
|
||||
UNTRUSTED_ALLOC.dealloc(NonNull::new(self.buf_ptr).unwrap(), layout);
|
||||
}
|
||||
}
|
||||
}
|
76
src/libos/src/untrusted/slice_ext.rs
Normal file
76
src/libos/src/untrusted/slice_ext.rs
Normal file
@ -0,0 +1,76 @@
|
||||
/// Extension traits for slices
|
||||
use super::*;
|
||||
use std::ptr;
|
||||
|
||||
/// An extension trait for slice to get its _const_ pointer and length.
|
||||
///
|
||||
/// If the length is zero, then the pointer is null. This trait is handy when
|
||||
/// it comes to converting slices to pointers and lengths for OCalls.
|
||||
pub trait SliceAsPtrAndLen<T> {
|
||||
fn as_ptr_and_len(&self) -> (*const T, usize);
|
||||
}
|
||||
|
||||
impl<T> SliceAsPtrAndLen<T> for Option<&[T]> {
|
||||
fn as_ptr_and_len(&self) -> (*const T, usize) {
|
||||
match self {
|
||||
Some(self_slice) => self_slice.as_ptr_and_len(),
|
||||
None => (std::ptr::null(), 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SliceAsPtrAndLen<T> for Option<&mut [T]> {
|
||||
fn as_ptr_and_len(&self) -> (*const T, usize) {
|
||||
match self {
|
||||
Some(self_slice) => self_slice.as_ptr_and_len(),
|
||||
None => (std::ptr::null(), 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SliceAsPtrAndLen<T> for &[T] {
|
||||
fn as_ptr_and_len(&self) -> (*const T, usize) {
|
||||
if self.len() > 0 {
|
||||
(self.as_ptr(), self.len())
|
||||
} else {
|
||||
(ptr::null(), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SliceAsPtrAndLen<T> for &mut [T] {
|
||||
fn as_ptr_and_len(&self) -> (*const T, usize) {
|
||||
if self.len() > 0 {
|
||||
(self.as_ptr(), self.len())
|
||||
} else {
|
||||
(ptr::null(), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension trait for slice to get its _mutable_ pointer and length.
|
||||
///
|
||||
/// If the length is zero, then the pointer is null. This trait is handy when
|
||||
/// it comes to converting slices to pointers and lengths for OCalls.
|
||||
pub trait SliceAsMutPtrAndLen<T> {
|
||||
fn as_mut_ptr_and_len(&mut self) -> (*mut T, usize);
|
||||
}
|
||||
|
||||
impl<T> SliceAsMutPtrAndLen<T> for Option<&mut [T]> {
|
||||
fn as_mut_ptr_and_len(&mut self) -> (*mut T, usize) {
|
||||
match self {
|
||||
Some(self_slice) => self_slice.as_mut_ptr_and_len(),
|
||||
None => (std::ptr::null_mut(), 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SliceAsMutPtrAndLen<T> for &mut [T] {
|
||||
fn as_mut_ptr_and_len(&mut self) -> (*mut T, usize) {
|
||||
if self.len() > 0 {
|
||||
(self.as_mut_ptr(), self.len())
|
||||
} else {
|
||||
(ptr::null_mut(), 0)
|
||||
}
|
||||
}
|
||||
}
|
@ -3,5 +3,6 @@
|
||||
|
||||
#include <time.h> // import struct timespec
|
||||
#include <sys/time.h> // import struct timeval
|
||||
#include <sys/uio.h> // import struct iovec
|
||||
|
||||
#endif /* __OCCLUM_EDL_TYPES__ */
|
||||
|
27
src/pal/src/ocalls/mem.c
Normal file
27
src/pal/src/ocalls/mem.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include <stdlib.h>
|
||||
#include "ocalls.h"
|
||||
|
||||
void* occlum_ocall_posix_memalign(size_t alignment, size_t size) {
|
||||
void* ptr = NULL;
|
||||
int ret = posix_memalign(&ptr, alignment, size);
|
||||
if (ret == 0) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Handle errors
|
||||
switch(ret) {
|
||||
case ENOMEM:
|
||||
PAL_ERROR("Out of memory on the untrusted side");
|
||||
break;
|
||||
case EINVAL:
|
||||
PAL_ERROR("Invalid arguments given to occlum_ocall_posix_memalign");
|
||||
break;
|
||||
default:
|
||||
PAL_ERROR("Unexpected error in occlum_ocall_posix_memalign");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void occlum_ocall_free(void* ptr) {
|
||||
free(ptr);
|
||||
}
|
@ -6,20 +6,16 @@
|
||||
ssize_t occlum_ocall_sendmsg(int sockfd,
|
||||
const void *msg_name,
|
||||
socklen_t msg_namelen,
|
||||
const void *buf,
|
||||
size_t buf_len,
|
||||
const struct iovec *msg_iov,
|
||||
size_t msg_iovlen,
|
||||
const void *msg_control,
|
||||
size_t msg_controllen,
|
||||
int flags)
|
||||
{
|
||||
struct iovec msg_iov = { .iov_base = (void*)buf, .iov_len = buf_len };
|
||||
struct iovec* p_msg_iov = buf != NULL ? &msg_iov : NULL;
|
||||
size_t msg_iovlen = buf != NULL ? 1 : 0;
|
||||
|
||||
struct msghdr msg = {
|
||||
(void*) msg_name,
|
||||
msg_namelen,
|
||||
p_msg_iov,
|
||||
(struct iovec *) msg_iov,
|
||||
msg_iovlen,
|
||||
(void*) msg_control,
|
||||
msg_controllen,
|
||||
@ -32,22 +28,18 @@ ssize_t occlum_ocall_recvmsg(int sockfd,
|
||||
void *msg_name,
|
||||
socklen_t msg_namelen,
|
||||
socklen_t* msg_namelen_recv,
|
||||
void *buf,
|
||||
size_t buf_len,
|
||||
struct iovec *msg_iov,
|
||||
size_t msg_iovlen,
|
||||
void *msg_control,
|
||||
size_t msg_controllen,
|
||||
size_t* msg_controllen_recv,
|
||||
int* msg_flags_recv,
|
||||
int flags)
|
||||
{
|
||||
struct iovec msg_iov = { .iov_base = buf, .iov_len = buf_len };
|
||||
struct iovec* p_msg_iov = buf != NULL ? &msg_iov : NULL;
|
||||
size_t msg_iovlen = buf != NULL ? 1 : 0;
|
||||
|
||||
struct msghdr msg = {
|
||||
msg_name,
|
||||
msg_namelen,
|
||||
p_msg_iov,
|
||||
msg_iov,
|
||||
msg_iovlen,
|
||||
msg_control,
|
||||
msg_controllen,
|
||||
|
@ -76,6 +76,13 @@ int client_sendmsg(int server_fd, char *buf) {
|
||||
ret = sendmsg(server_fd, &msg, 0);
|
||||
if (ret <= 0)
|
||||
THROW_ERROR("sendmsg failed");
|
||||
|
||||
msg.msg_iov = NULL;
|
||||
msg.msg_iovlen = 0;
|
||||
|
||||
ret = sendmsg(server_fd, &msg, 0);
|
||||
if (ret != 0)
|
||||
THROW_ERROR("empty sendmsg failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -64,13 +64,13 @@ int connect_with_child(int port, int *child_pid) {
|
||||
|
||||
int neogotiate_msg(int client_fd) {
|
||||
char buf[16];
|
||||
if (write(client_fd, ECHO_MSG, sizeof(ECHO_MSG)) < 0)
|
||||
if (write(client_fd, ECHO_MSG, strlen(ECHO_MSG)) < 0)
|
||||
THROW_ERROR("write failed");
|
||||
|
||||
if (read(client_fd, buf, 16) < 0)
|
||||
THROW_ERROR("read failed");
|
||||
|
||||
if (strncmp(buf, RESPONSE, sizeof(RESPONSE)) != 0) {
|
||||
if (strncmp(buf, RESPONSE, strlen(RESPONSE)) != 0) {
|
||||
THROW_ERROR("msg recv mismatch");
|
||||
}
|
||||
return 0;
|
||||
@ -83,7 +83,7 @@ int server_recv(int client_fd) {
|
||||
if (recv(client_fd, buf, buf_size, 0) <= 0)
|
||||
THROW_ERROR("msg recv failed");
|
||||
|
||||
if (strncmp(buf, ECHO_MSG, sizeof(ECHO_MSG)) != 0) {
|
||||
if (strncmp(buf, ECHO_MSG, strlen(ECHO_MSG)) != 0) {
|
||||
THROW_ERROR("msg recv mismatch");
|
||||
}
|
||||
return 0;
|
||||
@ -91,17 +91,21 @@ int server_recv(int client_fd) {
|
||||
|
||||
int server_recvmsg(int client_fd) {
|
||||
int ret = 0;
|
||||
const int buf_size = 1000;
|
||||
char buf[buf_size];
|
||||
const int buf_size = 10;
|
||||
char buf[3][buf_size];
|
||||
struct msghdr msg;
|
||||
struct iovec iov[1];
|
||||
struct iovec iov[3];
|
||||
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
iov[0].iov_base = buf;
|
||||
iov[0].iov_base = buf[0];
|
||||
iov[0].iov_len = buf_size;
|
||||
iov[1].iov_base = buf[1];
|
||||
iov[1].iov_len = buf_size;
|
||||
iov[2].iov_base = buf[2];
|
||||
iov[2].iov_len = buf_size;
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_iovlen = 3;
|
||||
msg.msg_control = 0;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
@ -110,11 +114,18 @@ int server_recvmsg(int client_fd) {
|
||||
if (ret <= 0) {
|
||||
THROW_ERROR("recvmsg failed");
|
||||
} else {
|
||||
if (strncmp(buf, ECHO_MSG, sizeof(ECHO_MSG)) != 0) {
|
||||
printf("recvmsg : %d, msg: %s\n", ret, buf);
|
||||
if (strncmp(buf[0], ECHO_MSG, buf_size) != 0 &&
|
||||
strstr(ECHO_MSG, buf[1]) != NULL &&
|
||||
strstr(ECHO_MSG, buf[2]) != NULL) {
|
||||
printf("recvmsg : %d, msg: %s, %s, %s\n", ret, buf[0], buf[1], buf[2]);
|
||||
THROW_ERROR("msg recvmsg mismatch");
|
||||
}
|
||||
}
|
||||
msg.msg_iov = NULL;
|
||||
msg.msg_iovlen = 0;
|
||||
ret = recvmsg(client_fd, &msg, 0);
|
||||
if (ret != 0)
|
||||
THROW_ERROR("recvmsg empty failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -157,7 +168,7 @@ int server_connectionless_recvmsg() {
|
||||
if (ret <= 0) {
|
||||
THROW_ERROR("recvmsg failed");
|
||||
} else {
|
||||
if (strncmp(buf, DEFAULT_MSG, sizeof(DEFAULT_MSG)) != 0) {
|
||||
if (strncmp(buf, DEFAULT_MSG, strlen(DEFAULT_MSG)) != 0) {
|
||||
printf("recvmsg : %d, msg: %s\n", ret, buf);
|
||||
THROW_ERROR("msg recvmsg mismatch");
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user