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

Add readv and writev syscalls for SGX #369

Merged
merged 5 commits into from Apr 3, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions enarx-keep-sgx-shim/src/event.rs
Expand Up @@ -24,7 +24,9 @@ pub extern "C" fn event(

aex.gpr.rax = match syscall {
SysCall::READ => h.read(),
SysCall::READV => h.readv(),
SysCall::WRITE => h.write(),
SysCall::WRITEV => h.writev(),
SysCall::EXIT => h.exit(None),
SysCall::GETUID => h.getuid(),
SysCall::ARCH_PRCTL => h.arch_prctl(),
Expand Down
144 changes: 140 additions & 4 deletions enarx-keep-sgx-shim/src/handler.rs
Expand Up @@ -2,10 +2,10 @@

use nolibc::x86_64::error::Number as ErrNo;
use nolibc::x86_64::syscall::Number as SysCall;
use nolibc::ArchPrctlTask;
use nolibc::{ArchPrctlTask, Iovec};
use sgx_types::{ssa::StateSaveArea, tcs::Tcs};

use core::ops::Range;
use core::{mem::size_of, ops::Range, slice::from_raw_parts_mut};

extern "C" {
#[no_mangle]
Expand Down Expand Up @@ -94,7 +94,9 @@ impl<'a> Handler<'a> {
)
};

if self.enclave.contains(&ret) {
// Make sure the allocated memory is page-aligned and outside of the enclave.
if self.enclave.contains(&ret) || self.enclave.contains(&(ret + bytes)) || ret & 0xfff != 0
{
self.attacked();
}

Expand Down Expand Up @@ -158,7 +160,7 @@ impl<'a> Handler<'a> {
self.attacked();
}

core::ptr::copy_nonoverlapping(buf, map, ret as _);
core::ptr::copy_nonoverlapping(map, buf, ret as _);
}

ret
Expand Down Expand Up @@ -224,4 +226,138 @@ impl<'a> Handler<'a> {
_ => ErrNo::EINVAL.into_syscall(),
}
}

/// Do a readv() syscall
pub fn readv(&mut self) -> u64 {
let fd = self.aex.gpr.rdi;
let trusted = unsafe {
from_raw_parts_mut(self.aex.gpr.rsi as *mut Iovec, self.aex.gpr.rdx as usize)
};

// Add up total size of buffers and size of iovec array.
let bufsize = trusted.iter().fold(0, |a, e| a + e.size);
let iovecsize = size_of::<Iovec>() * trusted.len();
let size = bufsize + iovecsize;

// Allocate some unencrypted memory.
let map = match self.ualloc(size as u64) {
Err(errno) => return errno.into_syscall(),
Ok(map) => unsafe { from_raw_parts_mut(map, size as usize) },
};

// Split allocated memory into that used by the iovec struct array and that used by its buffers.
let (uiovec, ubuffer) = map.split_at_mut(iovecsize);

// Convert the prefix from a byte slice into an iovec slice.
let (_, untrusted, _) = unsafe { uiovec.align_to_mut::<Iovec>() };
if untrusted.len() != trusted.len() {
self.attacked();
}

// Set pointers in unencrypted iovec slice to use the rest of the allocated memory.
// The offset is into the buffer area allocated immediately after the iovec struct
// array, measured in bytes.
let mut offset = 0;
for (t, mut u) in trusted.iter_mut().zip(untrusted.iter_mut()) {
u.base = ubuffer[offset..].as_mut_ptr();
u.size = t.size;
offset += t.size;
}

// Do the syscall; replace encrypted memory with unencrypted memory.
let ret = unsafe {
self.syscall(
SysCall::READV,
fd,
untrusted.as_ptr() as _,
untrusted.len() as u64,
0,
0,
0,
)
};

// Copy the unencrypted input into encrypted memory.
if ErrNo::from_syscall(ret).is_none() {
if ret > size as u64 {
self.attacked();
}

let mut offset = 0;
for (t, u) in trusted.iter_mut().zip(untrusted.iter_mut()) {
if u.base != ubuffer[offset..].as_mut_ptr() || u.size != t.size {
self.attacked();
}
npmccallum marked this conversation as resolved.
Show resolved Hide resolved

t.as_mut().copy_from_slice(u.as_ref());
offset += t.size;
}
}

unsafe { self.ufree(map.as_ptr() as *mut u8, size as u64) };
ret
}

/// Do a writev() syscall
pub fn writev(&mut self) -> u64 {
let fd = self.aex.gpr.rdi;
let trusted = unsafe {
from_raw_parts_mut(self.aex.gpr.rsi as *mut Iovec, self.aex.gpr.rdx as usize)
};

// Add up total size of buffers and size of iovec array.
let bufsize = trusted.iter().fold(0, |a, e| a + e.size);
let iovecsize = size_of::<Iovec>() * trusted.len();
let size = bufsize + iovecsize;

// Allocate some unencrypted memory.
let map = match self.ualloc(size as u64) {
Err(errno) => return errno.into_syscall(),
Ok(map) => unsafe { from_raw_parts_mut(map, size as usize) },
};

// Split allocated memory into that used by the iovec struct array
// and that used by its buffers.
let (uiovec, ubuffer) = map.split_at_mut(iovecsize);

// Convert the prefix from a byte slice into an iovec slice.
let (_, untrusted, _) = unsafe { uiovec.align_to_mut::<Iovec>() };
if untrusted.len() != trusted.len() {
self.attacked();
}

// Set pointers in unencrypted iovec slice to use the rest
// of the allocated memory, then copy the encrypted input
// into unencrypted memory. The offset is into the buffer
// area allocated immediately after the iovec struct array,
// measured in bytes.
let mut offset = 0;
for (t, mut u) in trusted.iter_mut().zip(untrusted.iter_mut()) {
u.base = ubuffer[offset..].as_mut_ptr();
u.size = t.size;
u.as_mut().copy_from_slice(t.as_ref());
offset += t.size;
}

// Do the syscall; replace encrypted memory with unencrypted memory.
let ret = unsafe {
self.syscall(
SysCall::WRITEV,
fd,
untrusted.as_ptr() as _,
untrusted.len() as u64,
0,
0,
0,
)
};

unsafe { self.ufree(map.as_ptr() as *mut u8, size as u64) };

if ErrNo::from_syscall(ret).is_none() && ret > size as u64 {
self.attacked()
}

ret
}
}
24 changes: 24 additions & 0 deletions nolibc/src/lib.rs
Expand Up @@ -25,3 +25,27 @@ enumerate::enumerate! {
ArchGetGs = 0x1004,
}
}

/// Buffer used by readv() and writev() syscalls
#[repr(C)]
pub struct Iovec<'a> {
/// Buffer start address
pub base: *mut u8,

/// Number of bytes to transfer
pub size: usize,

phantom: core::marker::PhantomData<&'a ()>,
}

impl<'a> AsRef<[u8]> for Iovec<'a> {
fn as_ref(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.base, self.size) }
}
}

impl<'a> AsMut<[u8]> for Iovec<'a> {
fn as_mut(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.base, self.size) }
}
}