Skip to content

Instantly share code, notes, and snippets.

@valpackett
Last active February 15, 2016 19:46
Show Gist options
  • Save valpackett/9c13c245e9f4051236dd to your computer and use it in GitHub Desktop.
Save valpackett/9c13c245e9f4051236dd to your computer and use it in GitHub Desktop.
Early prototype of rusty-sandbox, using a shared memory arena like in sandblast
extern crate libc;
use std::{io, process, mem, ptr};
pub struct Shared<T> {
data: *mut T
}
pub struct SharedArena {
max_size: isize,
mem: *mut libc::c_void
}
impl Drop for SharedArena {
fn drop(&mut self) {
unsafe { libc::munmap(self.mem, self.max_size as libc::size_t); }
}
}
impl SharedArena {
fn new(len: isize) -> io::Result<SharedArena> {
let reserved_size = mem::size_of::<isize>() as isize + mem::size_of::<libc::intptr_t>() as isize;
let max_size = len + reserved_size;
let mem = unsafe {
libc::mmap(ptr::null_mut(),
max_size as libc::size_t,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED | libc::MAP_ANON,
-1,
0)
};
if mem == libc::MAP_FAILED {
Err(io::Error::last_os_error())
} else {
let cur_size : &mut isize = unsafe { mem::transmute(mem) };
*cur_size = reserved_size;
Ok(SharedArena {
max_size: max_size,
mem: mem,
})
}
}
fn cur_size(&mut self) -> &mut isize {
unsafe { mem::transmute(self.mem) }
}
fn ret_val(&mut self) -> &mut *mut libc::intptr_t {
unsafe { mem::transmute(self.mem.offset((mem::size_of::<isize>() / mem::size_of::<libc::c_void>()) as isize)) }
}
pub fn copy<T: Copy>(&mut self, obj: T) -> io::Result<Shared<T>> {
let osize = mem::size_of::<T>() as isize;
if *self.cur_size() + osize > self.max_size {
Err(io::Error::new(io::ErrorKind::Other, "Out of memory on the SharedArena"))
} else {
let ptr : &mut T = unsafe { mem::transmute(self.mem.offset(*self.cur_size())) };
*ptr = obj;
let ret = Shared { data: ptr };
*self.cur_size() += osize;
Ok(ret)
}
}
}
pub fn sandboxify<F, T: Copy>(memory_size: isize, fun: F) -> io::Result<T> where F: Fn(&mut SharedArena) -> Shared<T> {
let mut arena = try!(SharedArena::new(memory_size));
let pid = unsafe { libc::fork() };
if pid < 0 {
Err(io::Error::new(io::ErrorKind::Other, "fork() failed"))
} else if pid > 0 { // parent
let mut status = 0;
unsafe { libc::waitpid(pid, &mut status, 0) };
if status == 0 {
let result : &T = unsafe { mem::transmute(*arena.ret_val()) };
Ok(*result)
} else {
Err(io::Error::new(io::ErrorKind::Other, "Sandboxed child process exited with non-zero status"))
}
} else { // child
let ret = fun(&mut arena).data;
*arena.ret_val() = unsafe { mem::transmute(ret) };
mem::forget(ret);
process::exit(0);
}
}
#[cfg(test)]
mod tests {
use super::sandboxify;
#[test]
fn test_simple() {
assert_eq!(sandboxify(4, |a| { a.copy(7 as u32).unwrap() }).unwrap(), 7);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment