Run-time memory bound checking.

This commit is contained in:
losfair 2019-03-19 21:27:49 +08:00
parent a006a368c5
commit 14da8abc23
2 changed files with 198 additions and 36 deletions

View File

@ -30,7 +30,14 @@ thread_local! {
}
lazy_static! {
static ref CALL_WASM: unsafe extern "C" fn(params: *const u8, params_len: usize, target: *const u8, memory_base: *mut u8, vmctx: *mut vm::Ctx) -> i64 = {
static ref CALL_WASM: unsafe extern "C" fn(
params: *const u8,
params_len: usize,
target: *const u8,
memory_base: *mut u8,
memory_size_pages: usize,
vmctx: *mut vm::Ctx
) -> i64 = {
let mut assembler = Assembler::new().unwrap();
let offset = assembler.offset();
dynasm!(
@ -40,8 +47,16 @@ lazy_static! {
; push r13
; push r14
; push r15
; mov r15, rcx // memory_base
; mov r14, r8 // vmctx
// Use the upper 16 bits of r15 to store memory size (in pages). This can support memory size up to 4GB.
// Wasmer currently only runs in usermode so here we assume the upper 17 bits of memory base address are all zero.
// FIXME: Change this if want to use this backend in kernel mode.
; shl r8, 48
; or r15, r8
; mov r14, r9 // vmctx
; lea rax, [>after_call]
; push rax
; push rbp
@ -196,7 +211,7 @@ impl Register {
7 => R11,
8 => RBX,
9 => R12,
10 => R13,
// 10 => R13, // R13 is reserved as temporary register.
// 11 => R14, // R14 is reserved for vmctx.
// 12 => R15, // R15 is reserved for memory base pointer.
_ => unreachable!(),
@ -382,22 +397,22 @@ impl ProtectedCaller for X64ExecutionContext {
}
}
let memory_base: *mut u8 = if _module.info.memories.len() > 0 {
let (memory_base, memory_size): (*mut u8, usize) = if _module.info.memories.len() > 0 {
if _module.info.memories.len() != 1 || _module.info.imported_memories.len() != 0 {
return Err(RuntimeError::Trap {
msg: "only one linear memory is supported".into(),
});
}
unsafe { (**(*_vmctx).memories).base }
unsafe { ((**(*_vmctx).memories).base, (**(*_vmctx).memories).bound) }
} else if _module.info.imported_memories.len() > 0 {
if _module.info.memories.len() != 0 || _module.info.imported_memories.len() != 1 {
return Err(RuntimeError::Trap {
msg: "only one linear memory is supported".into(),
});
}
unsafe { (**(*_vmctx).imported_memories).base }
unsafe { ((**(*_vmctx).imported_memories).base, (**(*_vmctx).imported_memories).bound) }
} else {
::std::ptr::null_mut()
(::std::ptr::null_mut(), 0)
};
//println!("MEMORY = {:?}", memory_base);
@ -410,6 +425,7 @@ impl ProtectedCaller for X64ExecutionContext {
param_buf.len(),
ptr,
memory_base,
memory_size.wrapping_shr(16),
_vmctx,
)
})
@ -545,22 +561,6 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, X64RuntimeResolve
for X64ModuleCodeGenerator
{
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError> {
for mem in module_info
.memories
.iter()
.map(|(_, v)| v)
.chain(module_info.imported_memories.iter().map(|(_, v)| &v.1))
{
match mem.memory_type() {
MemoryType::Dynamic => {
return Err(CodegenError {
message: "dynamic memory isn't supported yet",
});
}
_ => {}
}
}
Ok(())
}
@ -1375,6 +1375,46 @@ impl X64FunctionCode {
Ok(())
}
fn emit_update_memory_from_ctx(
assembler: &mut Assembler,
info: &ModuleInfo,
) -> Result<(), CodegenError> {
if info.memories.len() > 0 {
if info.memories.len() != 1 || info.imported_memories.len() != 0 {
return Err(CodegenError {
message: "only one linear memory is supported",
});
}
dynasm!(
assembler
; mov r15, r14 => vm::Ctx.memories
);
} else if info.imported_memories.len() > 0 {
if info.memories.len() != 0 || info.imported_memories.len() != 1 {
return Err(CodegenError {
message: "only one linear memory is supported",
});
}
dynasm!(
assembler
; mov r15, r14 => vm::Ctx.imported_memories
);
} else {
return Ok(());
};
dynasm!(
assembler
; mov r15, [r15]
; mov r13, r15 => LocalMemory.bound
; shr r13, 16 // 65536 bytes per page
; shl r13, 48
; mov r15, r15 => LocalMemory.base
; or r15, r13
);
Ok(())
}
fn emit_managed_call_trampoline(
assembler: &mut Assembler,
info: &ModuleInfo,
@ -1416,7 +1456,7 @@ impl X64FunctionCode {
dynasm!(
assembler
; mov r8, rdi // vmctx
; mov r9, rdi // vmctx
; mov rdx, QWORD target.0 as usize as i64
; mov rsi, QWORD (num_params * 8) as i64
; mov rdi, rsp
@ -1430,7 +1470,7 @@ impl X64FunctionCode {
}
dynasm!(
assembler
; mov rcx, r8 => vm::Ctx.memories
; mov rcx, r9 => vm::Ctx.memories
);
true
} else if info.imported_memories.len() > 0 {
@ -1441,7 +1481,7 @@ impl X64FunctionCode {
}
dynasm!(
assembler
; mov rcx, r8 => vm::Ctx.imported_memories
; mov rcx, r9 => vm::Ctx.imported_memories
);
true
} else {
@ -1452,6 +1492,8 @@ impl X64FunctionCode {
dynasm!(
assembler
; mov rcx, [rcx]
; mov r8, rcx => LocalMemory.bound
; shr r8, 16 // 65536 bytes per page
; mov rcx, rcx => LocalMemory.base
);
} else {
@ -1732,11 +1774,48 @@ impl X64FunctionCode {
Ok(())
}
fn emit_memory_bound_check_if_needed(
assembler: &mut Assembler,
module_info: &ModuleInfo,
offset_reg: Register,
value_size: usize,
) {
let mem_desc = match MemoryIndex::new(0).local_or_import(module_info) {
LocalOrImport::Local(local_mem_index) => &module_info.memories[local_mem_index],
LocalOrImport::Import(import_mem_index) => &module_info.imported_memories[import_mem_index].1,
};
let need_check = match mem_desc.memory_type() {
MemoryType::Dynamic => true,
MemoryType::Static | MemoryType::SharedStatic => false,
};
if need_check || true {
dynasm!(
assembler
; movq xmm5, r14
; lea r14, [Rq(offset_reg as u8) + value_size as i32] // overflow isn't possible since offset_reg contains a 32-bit value.
; mov r13, r15
; shr r13, 48
; shl r13, 16
; cmp r14, r13
; ja >out_of_bounds
; jmp >ok
; out_of_bounds:
; ud2
; ok:
; movq r14, xmm5
);
}
}
fn emit_memory_load<F: FnOnce(&mut Assembler, Register)>(
assembler: &mut Assembler,
value_stack: &mut ValueStack,
f: F,
out_ty: WpType,
module_info: &ModuleInfo,
read_size: usize,
) -> Result<(), CodegenError> {
let addr_info = value_stack.pop()?;
let out_loc = value_stack.push(out_ty);
@ -1752,9 +1831,16 @@ impl X64FunctionCode {
match addr_info.location {
ValueLocation::Register(x) => {
let reg = Register::from_scratch_reg(x);
dynasm!(
assembler
; mov Rd(reg as u8), Rd(reg as u8)
);
Self::emit_memory_bound_check_if_needed(assembler, module_info, reg, read_size);
dynasm!(
assembler
; add Rq(reg as u8), r15
; shl Rq(reg as u8), 16
; shr Rq(reg as u8), 16
);
f(assembler, reg);
}
@ -1762,7 +1848,14 @@ impl X64FunctionCode {
dynasm!(
assembler
; pop rax
; mov eax, eax
);
Self::emit_memory_bound_check_if_needed(assembler, module_info, Register::RAX, read_size);
dynasm!(
assembler
; add rax, r15
; shl rax, 16
; shr rax, 16
);
f(assembler, Register::RAX);
dynasm!(
@ -1779,6 +1872,8 @@ impl X64FunctionCode {
value_stack: &mut ValueStack,
f: F,
value_ty: WpType,
module_info: &ModuleInfo,
write_size: usize,
) -> Result<(), CodegenError> {
let value_info = value_stack.pop()?;
let addr_info = value_stack.pop()?;
@ -1800,9 +1895,16 @@ impl X64FunctionCode {
let value_reg = Register::from_scratch_reg(x);
let addr_reg =
Register::from_scratch_reg(addr_info.location.get_register().unwrap()); // must be a register
dynasm!(
assembler
; mov Rd(addr_reg as u8), Rd(addr_reg as u8)
);
Self::emit_memory_bound_check_if_needed(assembler, module_info, addr_reg, write_size);
dynasm!(
assembler
; add Rq(addr_reg as u8), r15
; shl Rq(addr_reg as u8), 16
; shr Rq(addr_reg as u8), 16
);
f(assembler, addr_reg, value_reg);
}
@ -1810,9 +1912,16 @@ impl X64FunctionCode {
match addr_info.location {
ValueLocation::Register(x) => {
let addr_reg = Register::from_scratch_reg(x);
dynasm!(
assembler
; mov Rd(addr_reg as u8), Rd(addr_reg as u8)
);
Self::emit_memory_bound_check_if_needed(assembler, module_info, addr_reg, write_size);
dynasm!(
assembler
; add Rq(addr_reg as u8), r15
; shl Rq(addr_reg as u8), 16
; shr Rq(addr_reg as u8), 16
; pop rax
);
f(assembler, addr_reg, Register::RAX);
@ -1823,7 +1932,17 @@ impl X64FunctionCode {
; mov [rsp - 8], rcx // red zone
; pop rax // value
; pop rcx // address
);
dynasm!(
assembler
; mov ecx, ecx
);
Self::emit_memory_bound_check_if_needed(assembler, module_info, Register::RCX, write_size);
dynasm!(
assembler
; add rcx, r15
; shl rcx, 16
; shr rcx, 16
);
f(assembler, Register::RCX, Register::RAX);
dynasm!(
@ -3392,6 +3511,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I32,
module_info,
4
)?;
}
Operator::I32Load8U { memarg } => {
@ -3405,6 +3526,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I32,
module_info,
1
)?;
}
Operator::I32Load8S { memarg } => {
@ -3418,6 +3541,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I32,
module_info,
1
)?;
}
Operator::I32Load16U { memarg } => {
@ -3431,6 +3556,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I32,
module_info,
2
)?;
}
Operator::I32Load16S { memarg } => {
@ -3444,6 +3571,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I32,
module_info,
2
)?;
}
Operator::I32Store { memarg } => {
@ -3457,6 +3586,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I32,
module_info,
4
)?;
}
Operator::I32Store8 { memarg } => {
@ -3470,6 +3601,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I32,
module_info,
1
)?;
}
Operator::I32Store16 { memarg } => {
@ -3483,6 +3616,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I32,
module_info,
2
)?;
}
Operator::I64Load { memarg } => {
@ -3496,6 +3631,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
8
)?;
}
Operator::I64Load8U { memarg } => {
@ -3509,6 +3646,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
1
)?;
}
Operator::I64Load8S { memarg } => {
@ -3522,6 +3661,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
1
)?;
}
Operator::I64Load16U { memarg } => {
@ -3535,6 +3676,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
2
)?;
}
Operator::I64Load16S { memarg } => {
@ -3548,6 +3691,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
2
)?;
}
Operator::I64Load32U { memarg } => {
@ -3561,6 +3706,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
4
)?;
}
Operator::I64Load32S { memarg } => {
@ -3574,6 +3721,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
4
)?;
}
Operator::I64Store { memarg } => {
@ -3587,6 +3736,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
8
)?;
}
Operator::I64Store8 { memarg } => {
@ -3600,6 +3751,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
1
)?;
}
Operator::I64Store16 { memarg } => {
@ -3613,6 +3766,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
2
)?;
}
Operator::I64Store32 { memarg } => {
@ -3626,6 +3781,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::I64,
module_info,
4
)?;
}
Operator::F32Const { value } => {
@ -3676,6 +3833,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::F32,
module_info,
4
)?;
}
Operator::F32Store { memarg } => {
@ -3689,6 +3848,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::F32,
module_info,
4
)?;
}
Operator::F64Load { memarg } => {
@ -3702,6 +3863,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::F64,
module_info,
8
)?;
}
Operator::F64Store { memarg } => {
@ -3715,6 +3878,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
);
},
WpType::F64,
module_info,
8
)?;
}
Operator::I32ReinterpretF32 => {
@ -4813,8 +4978,7 @@ impl FunctionCodeGenerator for X64FunctionCode {
LocalOrImport::Local(local_mem_index) => {
let mem_desc = &module_info.memories[local_mem_index];
match mem_desc.memory_type() {
//MemoryType::Dynamic => self.native_trampolines.memory_size_dynamic_local,
MemoryType::Dynamic => unimplemented!(),
MemoryType::Dynamic => self.native_trampolines.memory_size_dynamic_local,
MemoryType::Static => self.native_trampolines.memory_size_static_local,
MemoryType::SharedStatic => {
self.native_trampolines.memory_size_shared_local
@ -4824,8 +4988,7 @@ impl FunctionCodeGenerator for X64FunctionCode {
LocalOrImport::Import(import_mem_index) => {
let mem_desc = &module_info.imported_memories[import_mem_index].1;
match mem_desc.memory_type() {
//MemoryType::Dynamic => self.native_trampolines.memory_size_dynamic_import,
MemoryType::Dynamic => unimplemented!(),
MemoryType::Dynamic => self.native_trampolines.memory_size_dynamic_import,
MemoryType::Static => self.native_trampolines.memory_size_static_import,
MemoryType::SharedStatic => {
self.native_trampolines.memory_size_shared_import
@ -4841,8 +5004,7 @@ impl FunctionCodeGenerator for X64FunctionCode {
LocalOrImport::Local(local_mem_index) => {
let mem_desc = &module_info.memories[local_mem_index];
match mem_desc.memory_type() {
//MemoryType::Dynamic => self.native_trampolines.memory_grow_dynamic_local,
MemoryType::Dynamic => unimplemented!(),
MemoryType::Dynamic => self.native_trampolines.memory_grow_dynamic_local,
MemoryType::Static => self.native_trampolines.memory_grow_static_local,
MemoryType::SharedStatic => {
self.native_trampolines.memory_grow_shared_local
@ -4852,8 +5014,7 @@ impl FunctionCodeGenerator for X64FunctionCode {
LocalOrImport::Import(import_mem_index) => {
let mem_desc = &module_info.imported_memories[import_mem_index].1;
match mem_desc.memory_type() {
//MemoryType::Dynamic => self.native_trampolines.memory_grow_dynamic_import,
MemoryType::Dynamic => unimplemented!(),
MemoryType::Dynamic => self.native_trampolines.memory_grow_dynamic_import,
MemoryType::Static => self.native_trampolines.memory_grow_static_import,
MemoryType::SharedStatic => {
self.native_trampolines.memory_grow_shared_import
@ -4868,6 +5029,7 @@ impl FunctionCodeGenerator for X64FunctionCode {
&[WpType::I32],
&[WpType::I32],
)?;
Self::emit_update_memory_from_ctx(assembler, module_info)?;
}
_ => {
panic!("{:?}", op);

View File

@ -239,8 +239,8 @@ impl MemoryDescriptor {
pub fn memory_type(self) -> MemoryType {
match (self.maximum.is_some(), self.shared) {
(true, true) => MemoryType::SharedStatic,
(true, false) | (false, false) => MemoryType::Static,
//(false, false) => MemoryType::Dynamic,
(true, false) => MemoryType::Static,
(false, false) => MemoryType::Dynamic,
(false, true) => panic!("shared memory without a max is not allowed"),
}
}