This commit is contained in:
Heyang Zhou
2019-05-05 09:32:35 -07:00
parent cc01e40dc5
commit 7bc09ee220
6 changed files with 256 additions and 5 deletions

1
Cargo.lock generated
View File

@ -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)",

View File

@ -19,6 +19,7 @@ include = [
]
[dependencies]
byteorder = "1.3.1"
errno = "0.2.4"
structopt = "0.2.11"
wabt = "0.7.2"

View File

@ -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<Vec<u8>, 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))
}
}

View File

@ -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<i32> {
let req = RunCodeRequest {
pub fn run_code(&mut self, run: RunProfile) -> ServiceResult<u64> {
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<Vec<u8>> {
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
)
};
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(())
}
}
}

View File

@ -23,6 +23,13 @@ pub trait Loader {
pub trait Instance {
type Error: Debug;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error>;
fn read_memory(&mut self, offset: u32, len: u32) -> Result<Vec<u8>, Self::Error> {
unimplemented!()
}
fn write_memory(&mut self, offset: u32, len: u32, buf: &[u8]) -> Result<(), Self::Error> {
unimplemented!()
}
}
pub struct LocalLoader;

156
src/bin/kwasmd.rs Normal file
View File

@ -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::<LittleEndian>().unwrap();
if binary_size > 1048576 * 16 {
println!("binary too large");
return;
}
let mut wasm_binary: Vec<u8> = 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::<LittleEndian>().unwrap();
match cmd {
CMD_RUN_CODE => {
let func_name_len = stream.read_u32::<LittleEndian>().unwrap();
if func_name_len > 32 {
println!("function name too long");
return;
}
let mut func_name: Vec<u8> = 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::<LittleEndian>().unwrap();
if arg_count > 0 {
println!("Too many arguments");
return;
}
let mut args: Vec<Value> = Vec::with_capacity(arg_count as usize);
for _ in 0..arg_count {
args.push(Value::I64(stream.read_u64::<LittleEndian>().unwrap() as _));
}
let index = instance.resolve_local_func(func_name).unwrap();
let ret = ins.call(index, &args);
match ret {
Ok(x) => {
stream.write_u32::<LittleEndian>(1).unwrap();
stream.write_u64::<LittleEndian>(x).unwrap();
},
Err(e) => {
println!("Execution error: {:?}", e);
stream.write_u32::<LittleEndian>(0).unwrap();
},
}
},
CMD_READ_MEMORY => {
let offset = stream.read_u32::<LittleEndian>().unwrap();
let len = stream.read_u32::<LittleEndian>().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::<LittleEndian>().unwrap();
let len = stream.read_u32::<LittleEndian>().unwrap();
if len > 1048576 * 16 {
println!("memory size too large");
return;
}
let mut buf: Vec<u8> = 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);
}
}
}