diff --git a/Cargo.lock b/Cargo.lock index 0064d8553..cefe24d7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2240,6 +2240,7 @@ dependencies = [ name = "wasmer" version = "0.4.0" dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index b34b27715..b11e13340 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ include = [ ] [dependencies] +byteorder = "1.3.1" errno = "0.2.4" structopt = "0.2.11" wabt = "0.7.2" diff --git a/lib/kwasm-loader/src/lib.rs b/lib/kwasm-loader/src/lib.rs index 0acd356ff..365013f57 100644 --- a/lib/kwasm-loader/src/lib.rs +++ b/lib/kwasm-loader/src/lib.rs @@ -99,6 +99,14 @@ impl Instance for KernelInstance { entry_offset: self.offsets[id] as u32, params: &args, }).map_err(|x| format!("{:?}", x))?; - Ok(ret as u64) + Ok(ret) + } + + fn read_memory(&mut self, offset: u32, len: u32) -> Result, String> { + self.context.read_memory(offset, len).map_err(|x| format!("{:?}", x)) + } + + fn write_memory(&mut self, offset: u32, len: u32, buf: &[u8]) -> Result<(), String> { + self.context.write_memory(offset, len, buf).map_err(|x| format!("{:?}", x)) } } \ No newline at end of file diff --git a/lib/kwasm-loader/src/service.rs b/lib/kwasm-loader/src/service.rs index eaa8230a9..f169327bf 100644 --- a/lib/kwasm-loader/src/service.rs +++ b/lib/kwasm-loader/src/service.rs @@ -17,6 +17,8 @@ macro_rules! impl_debug_display { pub enum Command { LoadCode = 0x1001, RunCode = 0x1002, + ReadMemory = 0x1003, + WriteMemory = 0x1004, } #[derive(Debug)] @@ -67,6 +69,27 @@ struct RunCodeRequest { entry_offset: u32, params: *const u64, param_count: u32, + result: *mut RunCodeResult, +} + +#[repr(C)] +struct RunCodeResult { + success: u32, + retval: u64, +} + +#[repr(C)] +struct ReadMemoryRequest { + out: *mut u8, + offset: u32, + len: u32, +} + +#[repr(C)] +struct WriteMemoryRequest { + _in: *const u8, + offset: u32, + len: u32, } #[repr(C)] @@ -144,20 +167,75 @@ impl ServiceContext { } } - pub fn run_code(&mut self, run: RunProfile) -> ServiceResult { - let req = RunCodeRequest { + pub fn run_code(&mut self, run: RunProfile) -> ServiceResult { + let mut result: RunCodeResult = unsafe { ::std::mem::zeroed() }; + let mut req = RunCodeRequest { entry_offset: run.entry_offset, params: run.params.as_ptr(), param_count: run.params.len() as u32, + result: &mut result, }; let fd = self.dev.as_raw_fd(); - let ret = unsafe { + let err = unsafe { ::libc::ioctl( fd, Command::RunCode as i32 as ::libc::c_ulong, + &mut req as *mut _ as ::libc::c_ulong + ) + }; + if err < 0 { + Err(ServiceError::Code(err)) + } else if result.success == 0 { + println!("Rejected {} {}", result.success, result.retval); + Err(ServiceError::Rejected) + } else { + Ok(result.retval) + } + } + + pub fn read_memory(&mut self, offset: u32, len: u32) -> ServiceResult> { + let fd = self.dev.as_raw_fd(); + let mut ret = Vec::with_capacity(len as usize); + unsafe { + ret.set_len(len as usize); + } + let req = ReadMemoryRequest { + out: ret.as_mut_ptr(), + offset: offset, + len: len, + }; + let err = unsafe { + ::libc::ioctl( + fd, + Command::ReadMemory as i32 as ::libc::c_ulong, &req as *const _ as ::libc::c_ulong ) }; - Ok(ret) + if err < 0 { + Err(ServiceError::Code(err)) + } else { + Ok(ret) + } + } + + pub fn write_memory(&mut self, offset: u32, len: u32, buf: &[u8]) -> ServiceResult<()> { + let fd = self.dev.as_raw_fd(); + let req = WriteMemoryRequest { + _in: buf.as_ptr(), + offset: offset, + len: len, + }; + let err = unsafe { + ::libc::ioctl( + fd, + Command::WriteMemory as i32 as ::libc::c_ulong, + &req as *const _ as ::libc::c_ulong + ) + }; + if err < 0 { + Err(ServiceError::Code(err)) + } else { + Ok(()) + } } } diff --git a/lib/runtime-core/src/loader.rs b/lib/runtime-core/src/loader.rs index b109ddb14..749508a57 100644 --- a/lib/runtime-core/src/loader.rs +++ b/lib/runtime-core/src/loader.rs @@ -23,6 +23,13 @@ pub trait Loader { pub trait Instance { type Error: Debug; fn call(&mut self, id: usize, args: &[Value]) -> Result; + fn read_memory(&mut self, offset: u32, len: u32) -> Result, Self::Error> { + unimplemented!() + } + + fn write_memory(&mut self, offset: u32, len: u32, buf: &[u8]) -> Result<(), Self::Error> { + unimplemented!() + } } pub struct LocalLoader; diff --git a/src/bin/kwasmd.rs b/src/bin/kwasmd.rs new file mode 100644 index 000000000..994ae73ff --- /dev/null +++ b/src/bin/kwasmd.rs @@ -0,0 +1,156 @@ +extern crate structopt; +extern crate byteorder; + +use std::thread; +use structopt::StructOpt; +use wasmer::*; +use wasmer_runtime::{ + error::RuntimeError, + Func, Value, +}; +use wasmer_runtime_core::{ + self, + backend::{Compiler, CompilerConfig}, + loader::{self, Loader, Instance as LoadedInstance, LocalLoader}, +}; +use wasmer_singlepass_backend::SinglePassCompiler; + +use std::os::unix::net::{UnixStream, UnixListener}; +use std::io::prelude::*; + +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + +#[derive(Debug, StructOpt)] +#[structopt(name = "kwasmd", about = "Kernel-mode WebAssembly service.")] +enum CLIOptions { + #[structopt(name = "listen")] + Listen(Listen), +} + +#[derive(Debug, StructOpt)] +struct Listen { + #[structopt(long = "socket")] + socket: String, +} + +const CMD_RUN_CODE: u32 = 0x901; +const CMD_READ_MEMORY: u32 = 0x902; +const CMD_WRITE_MEMORY: u32 = 0x903; + +fn handle_client(mut stream: UnixStream) { + let binary_size = stream.read_u32::().unwrap(); + if binary_size > 1048576 * 16 { + println!("binary too large"); + return; + } + let mut wasm_binary: Vec = Vec::with_capacity(binary_size as usize); + unsafe { wasm_binary.set_len(binary_size as usize) }; + stream.read_exact(&mut wasm_binary).unwrap(); + let module = webassembly::compile_with_config_with( + &wasm_binary[..], + CompilerConfig { + symbol_map: None, + }, + &SinglePassCompiler::new(), + ).unwrap(); + + let mut import_object = wasmer_runtime_core::import::ImportObject::new(); + import_object.allow_missing_functions = true; // Import initialization might be left to the loader. + let instance = module.instantiate(&import_object).unwrap(); + let mut ins = instance.load(::kwasm_loader::KernelLoader).unwrap(); + + loop { + let cmd = stream.read_u32::().unwrap(); + match cmd { + CMD_RUN_CODE => { + let func_name_len = stream.read_u32::().unwrap(); + if func_name_len > 32 { + println!("function name too long"); + return; + } + let mut func_name: Vec = Vec::with_capacity(func_name_len as usize); + unsafe { func_name.set_len(func_name_len as usize) }; + stream.read_exact(&mut func_name).unwrap(); + let func_name = ::std::str::from_utf8(&func_name).unwrap(); + let arg_count = stream.read_u32::().unwrap(); + if arg_count > 0 { + println!("Too many arguments"); + return; + } + let mut args: Vec = Vec::with_capacity(arg_count as usize); + for _ in 0..arg_count { + args.push(Value::I64(stream.read_u64::().unwrap() as _)); + } + + let index = instance.resolve_local_func(func_name).unwrap(); + let ret = ins.call(index, &args); + match ret { + Ok(x) => { + stream.write_u32::(1).unwrap(); + stream.write_u64::(x).unwrap(); + }, + Err(e) => { + println!("Execution error: {:?}", e); + stream.write_u32::(0).unwrap(); + }, + } + }, + CMD_READ_MEMORY => { + let offset = stream.read_u32::().unwrap(); + let len = stream.read_u32::().unwrap(); + if len > 1048576 * 16 { + println!("memory size too large"); + return; + } + let buf = ins.read_memory(offset, len).unwrap(); + stream.write_all(&buf).unwrap(); + }, + CMD_WRITE_MEMORY => { + let offset = stream.read_u32::().unwrap(); + let len = stream.read_u32::().unwrap(); + if len > 1048576 * 16 { + println!("memory size too large"); + return; + } + let mut buf: Vec = Vec::with_capacity(len as usize); + unsafe { buf.set_len(len as usize) }; + stream.read_exact(&mut buf).unwrap(); + ins.write_memory(offset, len, &buf).unwrap(); + }, + _ => { + println!("Unknown command"); + return; + } + } + } +} + +fn run_listen(opts: Listen) { + let listener = UnixListener::bind(&opts.socket).unwrap(); + for stream in listener.incoming() { + match stream { + Ok(stream) => { + thread::spawn(|| { + match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| { + handle_client(stream); + })) { + Ok(()) => {}, + Err(_) => {} + } + }); + } + Err(err) => { + panic!("{:?}", err); + } + } + } +} + +fn main() { + let options = CLIOptions::from_args(); + match options { + CLIOptions::Listen(listen) => { + run_listen(listen); + } + } +}