cranelift: Prevent panics when dividing INT_MIN / -1 in interpreter

This commit is contained in:
Afonso Bordado
2021-08-23 15:33:50 +01:00
committed by Andrew Brown
parent 7b8ab065c6
commit 3f6b889067
4 changed files with 48 additions and 2 deletions

View File

@@ -340,6 +340,27 @@ mod tests {
}
}
#[test]
fn sdiv_min_by_neg_one_traps_with_overflow() {
let code = "function %test() -> i8 {
block0:
v0 = iconst.i32 -2147483648
v1 = sdiv_imm.i32 v0, -1
return v1
}";
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 result = Interpreter::new(state).call_by_name("%test", &[]).unwrap();
match result {
ControlFlow::Trap(CraneliftTrap::User(TrapCode::IntegerOverflow)) => {}
_ => panic!("Unexpected ControlFlow: {:?}", result),
}
}
// This test verifies that functions can refer to each other using the function store. A double indirection is
// required, which is tricky to get right: a referenced function is a FuncRef when called but a FuncIndex inside the
// function store. This test would preferably be a CLIF filetest but the filetest infrastructure only looks at a

View File

@@ -81,6 +81,9 @@ where
Err(ValueError::IntegerDivisionByZero) => Ok(ControlFlow::Trap(CraneliftTrap::User(
TrapCode::IntegerDivisionByZero,
))),
Err(ValueError::IntegerOverflow) => Ok(ControlFlow::Trap(CraneliftTrap::User(
TrapCode::IntegerOverflow,
))),
Err(e) => Err(e),
};

View File

@@ -71,6 +71,8 @@ pub enum ValueError {
InvalidDataValueCast(#[from] DataValueCastFailure),
#[error("performed a division by zero")]
IntegerDivisionByZero,
#[error("performed a operation that overflowed this integer type")]
IntegerOverflow,
}
#[derive(Debug, PartialEq)]
@@ -350,7 +352,15 @@ impl Value for DataValue {
}
fn div(self, other: Self) -> ValueResult<Self> {
if other.clone().into_int()? == 0 {
let denominator = other.clone().into_int()?;
// Check if we are dividing INT_MIN / -1. This causes an integer overflow trap.
let min = Value::int(1i128 << (self.ty().bits() - 1), self.ty())?;
if self == min && denominator == -1 {
return Err(ValueError::IntegerOverflow);
}
if denominator == 0 {
return Err(ValueError::IntegerDivisionByZero);
}