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(3, -2) == -1
|
||||||
; run: %sdiv_i64(-19, 7) == -2
|
; run: %sdiv_i64(-19, 7) == -2
|
||||||
; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0xF81FFDDD_DBD96000
|
; 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 {
|
function %sdiv_i32(i32, i32) -> i32 {
|
||||||
block0(v0: i32,v1: i32):
|
block0(v0: i32,v1: i32):
|
||||||
@@ -212,6 +213,7 @@ block0(v0: i32,v1: i32):
|
|||||||
; run: %sdiv_i32(-19, 7) == -2
|
; run: %sdiv_i32(-19, 7) == -2
|
||||||
; run: %sdiv_i32(0xC0FFEEEE, 8) == 0xF81FFDDE
|
; run: %sdiv_i32(0xC0FFEEEE, 8) == 0xF81FFDDE
|
||||||
; run: %sdiv_i32(0xC0FFEEEE, -8) == 0x7E00222
|
; run: %sdiv_i32(0xC0FFEEEE, -8) == 0x7E00222
|
||||||
|
; run: %sdiv_i32(0x80000000, -2) == 0x40000000
|
||||||
|
|
||||||
function %sdiv_i16(i16, i16) -> i16 {
|
function %sdiv_i16(i16, i16) -> i16 {
|
||||||
block0(v0: i16,v1: i16):
|
block0(v0: i16,v1: i16):
|
||||||
@@ -227,6 +229,7 @@ block0(v0: i16,v1: i16):
|
|||||||
; run: %sdiv_i16(-19, 7) == -2
|
; run: %sdiv_i16(-19, 7) == -2
|
||||||
; run: %sdiv_i16(0xC0FF, 8) == 0xF820
|
; run: %sdiv_i16(0xC0FF, 8) == 0xF820
|
||||||
; run: %sdiv_i16(0xC0FF, -8) == 0x07E0
|
; run: %sdiv_i16(0xC0FF, -8) == 0x07E0
|
||||||
|
; run: %sdiv_i16(0x8000, -2) == 0x4000
|
||||||
|
|
||||||
function %sdiv_i8(i8, i8) -> i8 {
|
function %sdiv_i8(i8, i8) -> i8 {
|
||||||
block0(v0: i8,v1: i8):
|
block0(v0: i8,v1: i8):
|
||||||
@@ -242,6 +245,7 @@ block0(v0: i8,v1: i8):
|
|||||||
; run: %sdiv_i8(-19, 7) == -2
|
; run: %sdiv_i8(-19, 7) == -2
|
||||||
; run: %sdiv_i8(0xC0, 8) == 0xF8
|
; run: %sdiv_i8(0xC0, 8) == 0xF8
|
||||||
; run: %sdiv_i8(0xC0, -8) == 0x08
|
; run: %sdiv_i8(0xC0, -8) == 0x08
|
||||||
|
; run: %sdiv_i8(0x80, -2) == 0x40
|
||||||
|
|
||||||
|
|
||||||
function %udiv_i64(i64, i64) -> i64 {
|
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(-19, 7) == 0x24924924_9249248F
|
||||||
; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0x181FFDDD_DBD95FFF
|
; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0x181FFDDD_DBD95FFF
|
||||||
; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0
|
; 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 {
|
function %udiv_i32(i32, i32) -> i32 {
|
||||||
block0(v0: i32,v1: i32):
|
block0(v0: i32,v1: i32):
|
||||||
@@ -273,6 +279,8 @@ block0(v0: i32,v1: i32):
|
|||||||
; run: %udiv_i32(-19, 7) == 0x24924921
|
; run: %udiv_i32(-19, 7) == 0x24924921
|
||||||
; run: %udiv_i32(0xC0FFEEEE, 8) == 0x181FFDDD
|
; run: %udiv_i32(0xC0FFEEEE, 8) == 0x181FFDDD
|
||||||
; run: %udiv_i32(0xC0FFEEEE, -8) == 0
|
; run: %udiv_i32(0xC0FFEEEE, -8) == 0
|
||||||
|
; run: %udiv_i32(0x80000000, -1) == 0
|
||||||
|
; run: %udiv_i32(0x80000000, -2) == 0
|
||||||
|
|
||||||
function %udiv_i16(i16, i16) -> i16 {
|
function %udiv_i16(i16, i16) -> i16 {
|
||||||
block0(v0: i16,v1: i16):
|
block0(v0: i16,v1: i16):
|
||||||
@@ -288,6 +296,8 @@ block0(v0: i16,v1: i16):
|
|||||||
; run: %udiv_i16(-19, 7) == 0x248F
|
; run: %udiv_i16(-19, 7) == 0x248F
|
||||||
; run: %udiv_i16(0xC0FF, 8) == 0x181F
|
; run: %udiv_i16(0xC0FF, 8) == 0x181F
|
||||||
; run: %udiv_i16(0xC0FF, -8) == 0
|
; run: %udiv_i16(0xC0FF, -8) == 0
|
||||||
|
; run: %udiv_i16(0x8000, -1) == 0
|
||||||
|
; run: %udiv_i16(0x8000, -2) == 0
|
||||||
|
|
||||||
function %udiv_i8(i8, i8) -> i8 {
|
function %udiv_i8(i8, i8) -> i8 {
|
||||||
block0(v0: i8,v1: i8):
|
block0(v0: i8,v1: i8):
|
||||||
@@ -303,3 +313,5 @@ block0(v0: i8,v1: i8):
|
|||||||
; run: %udiv_i8(-19, 7) == 0x21
|
; run: %udiv_i8(-19, 7) == 0x21
|
||||||
; run: %udiv_i8(0xC0, 8) == 0x18
|
; run: %udiv_i8(0xC0, 8) == 0x18
|
||||||
; run: %udiv_i8(0xC0, -8) == 0
|
; 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
|
// 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
|
// 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
|
// 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(
|
Err(ValueError::IntegerDivisionByZero) => Ok(ControlFlow::Trap(CraneliftTrap::User(
|
||||||
TrapCode::IntegerDivisionByZero,
|
TrapCode::IntegerDivisionByZero,
|
||||||
))),
|
))),
|
||||||
|
Err(ValueError::IntegerOverflow) => Ok(ControlFlow::Trap(CraneliftTrap::User(
|
||||||
|
TrapCode::IntegerOverflow,
|
||||||
|
))),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ pub enum ValueError {
|
|||||||
InvalidDataValueCast(#[from] DataValueCastFailure),
|
InvalidDataValueCast(#[from] DataValueCastFailure),
|
||||||
#[error("performed a division by zero")]
|
#[error("performed a division by zero")]
|
||||||
IntegerDivisionByZero,
|
IntegerDivisionByZero,
|
||||||
|
#[error("performed a operation that overflowed this integer type")]
|
||||||
|
IntegerOverflow,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@@ -350,7 +352,15 @@ impl Value for DataValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn div(self, other: Self) -> ValueResult<Self> {
|
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);
|
return Err(ValueError::IntegerDivisionByZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user