From fc901244890cf2c0218027189d9d2311026dabbd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Jun 2019 10:51:08 +0200 Subject: [PATCH 1/3] test exact_div UB detection --- src/intrinsic.rs | 8 +++++++- tests/compile-fail/exact_div1.rs | 5 +++++ tests/compile-fail/exact_div2.rs | 5 +++++ tests/compile-fail/exact_div3.rs | 5 +++++ tests/compile-fail/exact_div4.rs | 5 +++++ 5 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/exact_div1.rs create mode 100644 tests/compile-fail/exact_div2.rs create mode 100644 tests/compile-fail/exact_div3.rs create mode 100644 tests/compile-fail/exact_div4.rs diff --git a/src/intrinsic.rs b/src/intrinsic.rs index cd89055b46..cf2afe0a81 100644 --- a/src/intrinsic.rs +++ b/src/intrinsic.rs @@ -272,7 +272,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let b = this.read_immediate(args[1])?; // check x % y != 0 if this.binary_op(mir::BinOp::Rem, a, b)?.0.to_bits(dest.layout.size)? != 0 { - return err!(ValidationFailure(format!("exact_div: {:?} cannot be divided by {:?}", a, b))); + // Check if `b` is -1, which is the "min_value / -1" case. + let minus1 = Scalar::from_int(-1, dest.layout.size); + return if b.to_scalar().unwrap() == minus1 { + err!(Intrinsic(format!("exact_div: result of dividing MIN by -1 cannot be represented"))) + } else { + err!(Intrinsic(format!("exact_div: {:?} cannot be divided by {:?} without remainder", *a, *b))) + }; } this.binop_ignore_overflow(mir::BinOp::Div, a, b, dest)?; }, diff --git a/tests/compile-fail/exact_div1.rs b/tests/compile-fail/exact_div1.rs new file mode 100644 index 0000000000..171bedeadc --- /dev/null +++ b/tests/compile-fail/exact_div1.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // divison by 0 + unsafe { std::intrinsics::exact_div(2, 0); } //~ ERROR divisor of zero +} diff --git a/tests/compile-fail/exact_div2.rs b/tests/compile-fail/exact_div2.rs new file mode 100644 index 0000000000..22bcf027dd --- /dev/null +++ b/tests/compile-fail/exact_div2.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // divison with a remainder + unsafe { std::intrinsics::exact_div(2u16, 3); } //~ ERROR Scalar(0x0002) cannot be divided by Scalar(0x0003) without remainder +} diff --git a/tests/compile-fail/exact_div3.rs b/tests/compile-fail/exact_div3.rs new file mode 100644 index 0000000000..2db62e0092 --- /dev/null +++ b/tests/compile-fail/exact_div3.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // signed divison with a remainder + unsafe { std::intrinsics::exact_div(-19i8, 2); } //~ ERROR Scalar(0xed) cannot be divided by Scalar(0x02) without remainder +} diff --git a/tests/compile-fail/exact_div4.rs b/tests/compile-fail/exact_div4.rs new file mode 100644 index 0000000000..736d4f2516 --- /dev/null +++ b/tests/compile-fail/exact_div4.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // divison of min_value by -1 + unsafe { std::intrinsics::exact_div(i64::min_value(), -1); } //~ ERROR result of dividing MIN by -1 cannot be represented +} From cf748149ce6e4a8af1dcb387b6f5a19a856a6579 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Jun 2019 10:53:49 +0200 Subject: [PATCH 2/3] test unchecked_rem --- tests/compile-fail/div-by-zero-3.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/compile-fail/div-by-zero-3.rs diff --git a/tests/compile-fail/div-by-zero-3.rs b/tests/compile-fail/div-by-zero-3.rs new file mode 100644 index 0000000000..3200504ddb --- /dev/null +++ b/tests/compile-fail/div-by-zero-3.rs @@ -0,0 +1,11 @@ +#![feature(core_intrinsics)] + +use std::intrinsics::*; + +//error-pattern: Division by 0 in unchecked_rem + +fn main() { + unsafe { + let _n = unchecked_rem(3u32, 0); + } +} From 7ce36226e6156e7aaf6da77833fd80cd83d8d623 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Jun 2019 11:10:14 +0200 Subject: [PATCH 3/3] implement and test unchecked_{add,sub,mul} intrinsics --- src/intrinsic.rs | 16 +++++++++++ tests/compile-fail/unchecked_add1.rs | 5 ++++ tests/compile-fail/unchecked_add2.rs | 5 ++++ tests/compile-fail/unchecked_mul1.rs | 5 ++++ tests/compile-fail/unchecked_mul2.rs | 5 ++++ tests/compile-fail/unchecked_sub1.rs | 5 ++++ tests/compile-fail/unchecked_sub2.rs | 5 ++++ tests/run-pass/intrinsics-integer.rs | 40 ++++++++++++++++++---------- 8 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 tests/compile-fail/unchecked_add1.rs create mode 100644 tests/compile-fail/unchecked_add2.rs create mode 100644 tests/compile-fail/unchecked_mul1.rs create mode 100644 tests/compile-fail/unchecked_mul2.rs create mode 100644 tests/compile-fail/unchecked_sub1.rs create mode 100644 tests/compile-fail/unchecked_sub2.rs diff --git a/src/intrinsic.rs b/src/intrinsic.rs index cf2afe0a81..0ecccd02d7 100644 --- a/src/intrinsic.rs +++ b/src/intrinsic.rs @@ -465,6 +465,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx )?; } + "unchecked_add" | "unchecked_sub" | "unchecked_mul" => { + let l = this.read_immediate(args[0])?; + let r = this.read_immediate(args[1])?; + let op = match intrinsic_name.get() { + "unchecked_add" => mir::BinOp::Add, + "unchecked_sub" => mir::BinOp::Sub, + "unchecked_mul" => mir::BinOp::Mul, + _ => bug!(), + }; + let (res, overflowed) = this.binary_op(op, l, r)?; + if overflowed { + return err!(Intrinsic(format!("Overflowing arithmetic in {}", intrinsic_name.get()))); + } + this.write_scalar(res, dest)?; + } + "uninit" => { // Check fast path: we don't want to force an allocation in case the destination is a simple value, // but we also do not want to create a new allocation with 0s and then copy that over. diff --git a/tests/compile-fail/unchecked_add1.rs b/tests/compile-fail/unchecked_add1.rs new file mode 100644 index 0000000000..2447c8ba4a --- /dev/null +++ b/tests/compile-fail/unchecked_add1.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // MAX overflow + unsafe { std::intrinsics::unchecked_add(40000u16, 30000); } //~ ERROR Overflowing arithmetic in unchecked_add +} diff --git a/tests/compile-fail/unchecked_add2.rs b/tests/compile-fail/unchecked_add2.rs new file mode 100644 index 0000000000..e292cdf6d9 --- /dev/null +++ b/tests/compile-fail/unchecked_add2.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // MIN overflow + unsafe { std::intrinsics::unchecked_add(-30000i16, -8000); } //~ ERROR Overflowing arithmetic in unchecked_add +} diff --git a/tests/compile-fail/unchecked_mul1.rs b/tests/compile-fail/unchecked_mul1.rs new file mode 100644 index 0000000000..57bfaf124c --- /dev/null +++ b/tests/compile-fail/unchecked_mul1.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // MAX overflow + unsafe { std::intrinsics::unchecked_mul(300u16, 250u16); } //~ ERROR Overflowing arithmetic in unchecked_mul +} diff --git a/tests/compile-fail/unchecked_mul2.rs b/tests/compile-fail/unchecked_mul2.rs new file mode 100644 index 0000000000..690f2dc762 --- /dev/null +++ b/tests/compile-fail/unchecked_mul2.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // MIN overflow + unsafe { std::intrinsics::unchecked_mul(1_000_000_000i32, -4); } //~ ERROR Overflowing arithmetic in unchecked_mul +} diff --git a/tests/compile-fail/unchecked_sub1.rs b/tests/compile-fail/unchecked_sub1.rs new file mode 100644 index 0000000000..0be8afa2c3 --- /dev/null +++ b/tests/compile-fail/unchecked_sub1.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // MIN overflow + unsafe { std::intrinsics::unchecked_sub(14u32, 22); } //~ ERROR Overflowing arithmetic in unchecked_sub +} diff --git a/tests/compile-fail/unchecked_sub2.rs b/tests/compile-fail/unchecked_sub2.rs new file mode 100644 index 0000000000..bc23fa37c3 --- /dev/null +++ b/tests/compile-fail/unchecked_sub2.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + // MAX overflow + unsafe { std::intrinsics::unchecked_sub(30000i16, -7000); } //~ ERROR Overflowing arithmetic in unchecked_sub +} diff --git a/tests/run-pass/intrinsics-integer.rs b/tests/run-pass/intrinsics-integer.rs index de59314eff..af3517af6f 100644 --- a/tests/run-pass/intrinsics-integer.rs +++ b/tests/run-pass/intrinsics-integer.rs @@ -8,23 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(intrinsics)] - -mod rusti { - extern "rust-intrinsic" { - pub fn ctpop(x: T) -> T; - pub fn ctlz(x: T) -> T; - pub fn ctlz_nonzero(x: T) -> T; - pub fn cttz(x: T) -> T; - pub fn cttz_nonzero(x: T) -> T; - pub fn bswap(x: T) -> T; - } -} +#![feature(core_intrinsics)] +use std::intrinsics::*; pub fn main() { unsafe { - use crate::rusti::*; - assert_eq!(ctpop(0u8), 0); assert_eq!(ctpop(0i8), 0); assert_eq!(ctpop(0u16), 0); assert_eq!(ctpop(0i16), 0); assert_eq!(ctpop(0u32), 0); assert_eq!(ctpop(0i32), 0); @@ -138,5 +126,29 @@ pub fn main() { assert_eq!(bswap(0x0ABBCC0Di32), 0x0DCCBB0A); assert_eq!(bswap(0x0122334455667708u64), 0x0877665544332201); assert_eq!(bswap(0x0122334455667708i64), 0x0877665544332201); + + assert_eq!(exact_div(9*9u32, 3), 27); + assert_eq!(exact_div(-9*9i32, 3), -27); + assert_eq!(exact_div(9*9i8, -3), -27); + assert_eq!(exact_div(-9*9i64, -3), 27); + + assert_eq!(unchecked_div(9*9u32, 2), 40); + assert_eq!(unchecked_div(-9*9i32, 2), -40); + assert_eq!(unchecked_div(9*9i8, -2), -40); + assert_eq!(unchecked_div(-9*9i64, -2), 40); + + assert_eq!(unchecked_rem(9*9u32, 2), 1); + assert_eq!(unchecked_rem(-9*9i32, 2), -1); + assert_eq!(unchecked_rem(9*9i8, -2), 1); + assert_eq!(unchecked_rem(-9*9i64, -2), -1); + + assert_eq!(unchecked_add(23u8, 19), 42); + assert_eq!(unchecked_add(5, -10), -5); + + assert_eq!(unchecked_sub(23u8, 19), 4); + assert_eq!(unchecked_sub(-17, -27), 10); + + assert_eq!(unchecked_mul(6u8, 7), 42); + assert_eq!(unchecked_mul(13, -5), -65); } }