cranelift-interpreter: Add trap on misaligned memory accesses (#5921)
* Add checks to `InterpreterState::checked_{load,store}` to trap on misaligned memory accesses
where `aligned` memory flag is set.
* Alter `stack_{load,store}` instructions to now rely on `MemFlags::new()` instead of
`MemFlags::trusted` since `InterpreterState::checked_{load,store}` is only able to
deduce type alignment and not stack slot alignment.
This commit is contained in:
committed by
GitHub
parent
3c9fc3ec8c
commit
a2beacd288
@@ -336,6 +336,11 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
// Aligned flag is set and address is not aligned for the given type
|
||||
if mem_flags.aligned() && addr_start % load_size != 0 {
|
||||
return Err(MemoryError::MisalignedLoad { addr, load_size });
|
||||
}
|
||||
|
||||
Ok(match mem_flags.endianness(self.native_endianness) {
|
||||
Endianness::Big => DataValue::read_from_slice_be(src, ty),
|
||||
Endianness::Little => DataValue::read_from_slice_le(src, ty),
|
||||
@@ -363,6 +368,11 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
// Aligned flag is set and address is not aligned for the given type
|
||||
if mem_flags.aligned() && addr_start % store_size != 0 {
|
||||
return Err(MemoryError::MisalignedStore { addr, store_size });
|
||||
}
|
||||
|
||||
Ok(match mem_flags.endianness(self.native_endianness) {
|
||||
Endianness::Big => v.write_to_slice_be(dst),
|
||||
Endianness::Little => v.write_to_slice_le(dst),
|
||||
@@ -965,4 +975,55 @@ mod tests {
|
||||
|
||||
assert_eq!(result, vec![DataValue::F32(Ieee32::with_float(1.0))])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn misaligned_store_traps() {
|
||||
let code = "
|
||||
function %test() {
|
||||
ss0 = explicit_slot 16
|
||||
|
||||
block0:
|
||||
v0 = stack_addr.i64 ss0
|
||||
v1 = iconst.i64 1
|
||||
store.i64 aligned v1, v0+2
|
||||
return
|
||||
}";
|
||||
|
||||
let func = parse_functions(code).unwrap().into_iter().next().unwrap();
|
||||
let mut env = FunctionStore::default();
|
||||
env.add(func.name.to_string(), &func);
|
||||
let state = InterpreterState::default().with_function_store(env);
|
||||
let trap = Interpreter::new(state)
|
||||
.call_by_name("%test", &[])
|
||||
.unwrap()
|
||||
.unwrap_trap();
|
||||
|
||||
assert_eq!(trap, CraneliftTrap::User(TrapCode::HeapMisaligned));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn misaligned_load_traps() {
|
||||
let code = "
|
||||
function %test() {
|
||||
ss0 = explicit_slot 16
|
||||
|
||||
block0:
|
||||
v0 = stack_addr.i64 ss0
|
||||
v1 = iconst.i64 1
|
||||
store.i64 aligned v1, v0
|
||||
v2 = load.i64 aligned v0+2
|
||||
return
|
||||
}";
|
||||
|
||||
let func = parse_functions(code).unwrap().into_iter().next().unwrap();
|
||||
let mut env = FunctionStore::default();
|
||||
env.add(func.name.to_string(), &func);
|
||||
let state = InterpreterState::default().with_function_store(env);
|
||||
let trap = Interpreter::new(state)
|
||||
.call_by_name("%test", &[])
|
||||
.unwrap()
|
||||
.unwrap_trap();
|
||||
|
||||
assert_eq!(trap, CraneliftTrap::User(TrapCode::HeapMisaligned));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,10 @@ pub enum MemoryError {
|
||||
OutOfBoundsLoad { addr: Address, load_size: usize },
|
||||
#[error("Store of {store_size} bytes is larger than available size at address {addr:?}")]
|
||||
OutOfBoundsStore { addr: Address, store_size: usize },
|
||||
#[error("Load of {load_size} bytes is misaligned at address {addr:?}")]
|
||||
MisalignedLoad { addr: Address, load_size: usize },
|
||||
#[error("Store of {store_size} bytes is misaligned at address {addr:?}")]
|
||||
MisalignedStore { addr: Address, store_size: usize },
|
||||
}
|
||||
|
||||
/// This dummy state allows interpretation over an immutable mapping of values in a single frame.
|
||||
|
||||
@@ -173,6 +173,8 @@ where
|
||||
MemoryError::InvalidEntry { .. } => TrapCode::HeapOutOfBounds,
|
||||
MemoryError::OutOfBoundsStore { .. } => TrapCode::HeapOutOfBounds,
|
||||
MemoryError::OutOfBoundsLoad { .. } => TrapCode::HeapOutOfBounds,
|
||||
MemoryError::MisalignedLoad { .. } => TrapCode::HeapMisaligned,
|
||||
MemoryError::MisalignedStore { .. } => TrapCode::HeapMisaligned,
|
||||
};
|
||||
|
||||
// Assigns or traps depending on the value of the result
|
||||
@@ -522,7 +524,7 @@ where
|
||||
let load_ty = inst_context.controlling_type().unwrap();
|
||||
let slot = inst.stack_slot().unwrap();
|
||||
let offset = sum(imm(), args()?)? as u64;
|
||||
let mem_flags = MemFlags::trusted();
|
||||
let mem_flags = MemFlags::new();
|
||||
assign_or_memtrap({
|
||||
state
|
||||
.stack_address(AddressSize::_64, slot, offset)
|
||||
@@ -533,7 +535,7 @@ where
|
||||
let arg = arg(0)?;
|
||||
let slot = inst.stack_slot().unwrap();
|
||||
let offset = sum(imm(), args_range(1..)?)? as u64;
|
||||
let mem_flags = MemFlags::trusted();
|
||||
let mem_flags = MemFlags::new();
|
||||
continue_or_memtrap({
|
||||
state
|
||||
.stack_address(AddressSize::_64, slot, offset)
|
||||
|
||||
Reference in New Issue
Block a user