cranelift: Prevent panics when dividing INT_MIN / -1 in interpreter
This commit is contained in:
committed by
Andrew Brown
parent
7b8ab065c6
commit
3f6b889067
@@ -196,7 +196,8 @@ block0(v0: i64,v1: i64):
|
||||
; run: %sdiv_i64(3, -2) == -1
|
||||
; run: %sdiv_i64(-19, 7) == -2
|
||||
; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0xF81FFDDD_DBD96000
|
||||
; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0x7E00222_2426A000
|
||||
; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0x7E002222_426A000
|
||||
; run: %sdiv_i64(0x80000000_00000000, -2) == 0x40000000_00000000
|
||||
|
||||
function %sdiv_i32(i32, i32) -> i32 {
|
||||
block0(v0: i32,v1: i32):
|
||||
@@ -212,6 +213,7 @@ block0(v0: i32,v1: i32):
|
||||
; run: %sdiv_i32(-19, 7) == -2
|
||||
; run: %sdiv_i32(0xC0FFEEEE, 8) == 0xF81FFDDE
|
||||
; run: %sdiv_i32(0xC0FFEEEE, -8) == 0x7E00222
|
||||
; run: %sdiv_i32(0x80000000, -2) == 0x40000000
|
||||
|
||||
function %sdiv_i16(i16, i16) -> i16 {
|
||||
block0(v0: i16,v1: i16):
|
||||
@@ -227,6 +229,7 @@ block0(v0: i16,v1: i16):
|
||||
; run: %sdiv_i16(-19, 7) == -2
|
||||
; run: %sdiv_i16(0xC0FF, 8) == 0xF820
|
||||
; run: %sdiv_i16(0xC0FF, -8) == 0x07E0
|
||||
; run: %sdiv_i16(0x8000, -2) == 0x4000
|
||||
|
||||
function %sdiv_i8(i8, i8) -> i8 {
|
||||
block0(v0: i8,v1: i8):
|
||||
@@ -242,6 +245,7 @@ block0(v0: i8,v1: i8):
|
||||
; run: %sdiv_i8(-19, 7) == -2
|
||||
; run: %sdiv_i8(0xC0, 8) == 0xF8
|
||||
; run: %sdiv_i8(0xC0, -8) == 0x08
|
||||
; run: %sdiv_i8(0x80, -2) == 0x40
|
||||
|
||||
|
||||
function %udiv_i64(i64, i64) -> i64 {
|
||||
@@ -258,6 +262,8 @@ block0(v0: i64,v1: i64):
|
||||
; run: %udiv_i64(-19, 7) == 0x24924924_9249248F
|
||||
; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0x181FFDDD_DBD95FFF
|
||||
; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0
|
||||
; run: %udiv_i64(0x80000000_00000000, -1) == 0
|
||||
; run: %udiv_i64(0x80000000_00000000, -2) == 0
|
||||
|
||||
function %udiv_i32(i32, i32) -> i32 {
|
||||
block0(v0: i32,v1: i32):
|
||||
@@ -273,6 +279,8 @@ block0(v0: i32,v1: i32):
|
||||
; run: %udiv_i32(-19, 7) == 0x24924921
|
||||
; run: %udiv_i32(0xC0FFEEEE, 8) == 0x181FFDDD
|
||||
; run: %udiv_i32(0xC0FFEEEE, -8) == 0
|
||||
; run: %udiv_i32(0x80000000, -1) == 0
|
||||
; run: %udiv_i32(0x80000000, -2) == 0
|
||||
|
||||
function %udiv_i16(i16, i16) -> i16 {
|
||||
block0(v0: i16,v1: i16):
|
||||
@@ -288,6 +296,8 @@ block0(v0: i16,v1: i16):
|
||||
; run: %udiv_i16(-19, 7) == 0x248F
|
||||
; run: %udiv_i16(0xC0FF, 8) == 0x181F
|
||||
; run: %udiv_i16(0xC0FF, -8) == 0
|
||||
; run: %udiv_i16(0x8000, -1) == 0
|
||||
; run: %udiv_i16(0x8000, -2) == 0
|
||||
|
||||
function %udiv_i8(i8, i8) -> i8 {
|
||||
block0(v0: i8,v1: i8):
|
||||
@@ -303,3 +313,5 @@ block0(v0: i8,v1: i8):
|
||||
; run: %udiv_i8(-19, 7) == 0x21
|
||||
; run: %udiv_i8(0xC0, 8) == 0x18
|
||||
; run: %udiv_i8(0xC0, -8) == 0
|
||||
; run: %udiv_i8(0x80, -1) == 0
|
||||
; run: %udiv_i8(0x80, -2) == 0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user