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!(),
|
_ => 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) {
|
Ok(match mem_flags.endianness(self.native_endianness) {
|
||||||
Endianness::Big => DataValue::read_from_slice_be(src, ty),
|
Endianness::Big => DataValue::read_from_slice_be(src, ty),
|
||||||
Endianness::Little => DataValue::read_from_slice_le(src, ty),
|
Endianness::Little => DataValue::read_from_slice_le(src, ty),
|
||||||
@@ -363,6 +368,11 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
|||||||
_ => unimplemented!(),
|
_ => 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) {
|
Ok(match mem_flags.endianness(self.native_endianness) {
|
||||||
Endianness::Big => v.write_to_slice_be(dst),
|
Endianness::Big => v.write_to_slice_be(dst),
|
||||||
Endianness::Little => v.write_to_slice_le(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))])
|
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 },
|
OutOfBoundsLoad { addr: Address, load_size: usize },
|
||||||
#[error("Store of {store_size} bytes is larger than available size at address {addr:?}")]
|
#[error("Store of {store_size} bytes is larger than available size at address {addr:?}")]
|
||||||
OutOfBoundsStore { addr: Address, store_size: usize },
|
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.
|
/// 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::InvalidEntry { .. } => TrapCode::HeapOutOfBounds,
|
||||||
MemoryError::OutOfBoundsStore { .. } => TrapCode::HeapOutOfBounds,
|
MemoryError::OutOfBoundsStore { .. } => TrapCode::HeapOutOfBounds,
|
||||||
MemoryError::OutOfBoundsLoad { .. } => TrapCode::HeapOutOfBounds,
|
MemoryError::OutOfBoundsLoad { .. } => TrapCode::HeapOutOfBounds,
|
||||||
|
MemoryError::MisalignedLoad { .. } => TrapCode::HeapMisaligned,
|
||||||
|
MemoryError::MisalignedStore { .. } => TrapCode::HeapMisaligned,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assigns or traps depending on the value of the result
|
// Assigns or traps depending on the value of the result
|
||||||
@@ -522,7 +524,7 @@ where
|
|||||||
let load_ty = inst_context.controlling_type().unwrap();
|
let load_ty = inst_context.controlling_type().unwrap();
|
||||||
let slot = inst.stack_slot().unwrap();
|
let slot = inst.stack_slot().unwrap();
|
||||||
let offset = sum(imm(), args()?)? as u64;
|
let offset = sum(imm(), args()?)? as u64;
|
||||||
let mem_flags = MemFlags::trusted();
|
let mem_flags = MemFlags::new();
|
||||||
assign_or_memtrap({
|
assign_or_memtrap({
|
||||||
state
|
state
|
||||||
.stack_address(AddressSize::_64, slot, offset)
|
.stack_address(AddressSize::_64, slot, offset)
|
||||||
@@ -533,7 +535,7 @@ where
|
|||||||
let arg = arg(0)?;
|
let arg = arg(0)?;
|
||||||
let slot = inst.stack_slot().unwrap();
|
let slot = inst.stack_slot().unwrap();
|
||||||
let offset = sum(imm(), args_range(1..)?)? as u64;
|
let offset = sum(imm(), args_range(1..)?)? as u64;
|
||||||
let mem_flags = MemFlags::trusted();
|
let mem_flags = MemFlags::new();
|
||||||
continue_or_memtrap({
|
continue_or_memtrap({
|
||||||
state
|
state
|
||||||
.stack_address(AddressSize::_64, slot, offset)
|
.stack_address(AddressSize::_64, slot, offset)
|
||||||
|
|||||||
Reference in New Issue
Block a user