Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions compiler/cbmc/src/goto_program/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ pub enum UnaryOperand {
UnaryMinus,
}

/// The return type for `__builtin_op_overflow` operations
/// The return type for `__CPROVER_overflow_op` operations
pub struct ArithmeticOverflowResult {
/// If overflow did not occur, the result of the operation. Otherwise undefined.
pub result: Expr,
Expand Down Expand Up @@ -849,8 +849,13 @@ impl Expr {
// Floating Point Equalities
IeeeFloatEqual | IeeeFloatNotequal => lhs.typ == rhs.typ && lhs.typ.is_floating_point(),
// Overflow flags
OverflowMinus | OverflowMult | OverflowPlus => {
lhs.typ == rhs.typ && lhs.typ.is_integer()
OverflowMinus => {
(lhs.typ == rhs.typ && (lhs.typ.is_pointer() || lhs.typ.is_numeric()))
|| (lhs.typ.is_pointer() && rhs.typ.is_integer())
}
OverflowMult | OverflowPlus => {
(lhs.typ == rhs.typ && lhs.typ.is_integer())
|| (lhs.typ.is_pointer() && rhs.typ.is_integer())
}
}
}
Expand Down
77 changes: 66 additions & 11 deletions compiler/rustc_codegen_rmc/src/codegen/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,60 @@ impl<'tcx> GotocCtx<'tcx> {
}};
}

// Intrinsics which encode a simple arithmetic operation with overflow check
macro_rules! codegen_op_with_overflow_check {
($f:ident) => {{
let a = fargs.remove(0);
let b = fargs.remove(0);
let res = a.$f(b);
let check = Stmt::assert(
res.overflowed.not(),
format!("attempt to compute {} which would overflow", intrinsic).as_str(),
loc,
);
let expr_place = self.codegen_expr_to_place(p, res.result);
Stmt::block(vec![expr_place, check], loc)
}};
}

// Intrinsics which encode a SIMD arithmetic operation with overflow check.
// We expand the overflow check because CBMC overflow operations don't accept array as
// argument.
macro_rules! codegen_simd_with_overflow_check {
($op:ident, $overflow:ident) => {{
let a = fargs.remove(0);
let b = fargs.remove(0);
let mut check = Expr::bool_false();
if let Type::Vector { size, .. } = a.typ() {
let a_size = size;
if let Type::Vector { size, .. } = b.typ() {
let b_size = size;
assert_eq!(a_size, b_size, "Expected same length vectors",);
for i in 0..*a_size {
// create expression
let index = Expr::int_constant(i, Type::ssize_t());
let v_a = a.clone().index_array(index.clone());
let v_b = b.clone().index_array(index);
check = check.or(v_a.$overflow(v_b));
}
}
}
let check_stmt = Stmt::assert(
check.not(),
format!("attempt to compute {} which would overflow", intrinsic).as_str(),
loc,
);
let res = a.$op(b);
let expr_place = self.codegen_expr_to_place(p, res);
Stmt::block(vec![expr_place, check_stmt], loc)
}};
}

// Intrinsics which encode a simple wrapping arithmetic operation
macro_rules! codegen_wrapping_op {
($f:ident) => {{ codegen_intrinsic_binop!($f) }};
}

// Intrinsics which encode a simple binary operation
macro_rules! codegen_intrinsic_boolean_binop {
($f:ident) => {{ self.binop(p, fargs, |a, b| a.$f(b).cast_to(Type::c_bool())) }};
Expand Down Expand Up @@ -203,6 +257,7 @@ impl<'tcx> GotocCtx<'tcx> {
// *var1 = op(*var1, var2);
// var = tmp;
// -------------------------
// Note: Atomic arithmetic operations wrap around on overflow.
macro_rules! codegen_atomic_binop {
($op: ident) => {{
warn!("RMC does not support concurrency for now. {} treated as a sequential operation.", intrinsic);
Expand All @@ -227,7 +282,7 @@ impl<'tcx> GotocCtx<'tcx> {
match intrinsic {
"abort" => Stmt::assert_false("abort intrinsic", loc),
"add_with_overflow" => codegen_op_with_overflow!(add_overflow),
"arith_offset" => codegen_intrinsic_binop!(plus),
"arith_offset" => codegen_wrapping_op!(plus),
"assert_inhabited" => {
let ty = instance.substs.type_at(0);
let layout = self.layout_of(ty);
Expand Down Expand Up @@ -356,7 +411,7 @@ impl<'tcx> GotocCtx<'tcx> {
"nearbyintf32" => codegen_simple_intrinsic!(Nearbyintf),
"nearbyintf64" => codegen_simple_intrinsic!(Nearbyint),
"needs_drop" => codegen_intrinsic_const!(),
"offset" => codegen_intrinsic_binop!(plus),
"offset" => codegen_op_with_overflow_check!(add_overflow),
"powf32" => codegen_simple_intrinsic!(Powf),
"powf64" => codegen_simple_intrinsic!(Pow),
"powif32" => codegen_simple_intrinsic!(Powif),
Expand All @@ -376,7 +431,7 @@ impl<'tcx> GotocCtx<'tcx> {
"saturating_sub" => codegen_intrinsic_binop_with_mm!(saturating_sub),
"sinf32" => codegen_simple_intrinsic!(Sinf),
"sinf64" => codegen_simple_intrinsic!(Sin),
"simd_add" => codegen_intrinsic_binop!(plus),
"simd_add" => codegen_simd_with_overflow_check!(plus, add_overflow_p),
"simd_and" => codegen_intrinsic_binop!(bitand),
"simd_div" => codegen_intrinsic_binop!(div),
"simd_eq" => codegen_intrinsic_binop!(eq),
Expand All @@ -390,7 +445,7 @@ impl<'tcx> GotocCtx<'tcx> {
"simd_insert" => self.codegen_intrinsic_simd_insert(fargs, p, cbmc_ret_ty, loc),
"simd_le" => codegen_intrinsic_binop!(le),
"simd_lt" => codegen_intrinsic_binop!(lt),
"simd_mul" => codegen_intrinsic_binop!(mul),
"simd_mul" => codegen_simd_with_overflow_check!(mul, mul_overflow_p),
"simd_ne" => codegen_intrinsic_binop!(neq),
"simd_or" => codegen_intrinsic_binop!(bitor),
"simd_rem" => codegen_intrinsic_binop!(rem),
Expand All @@ -404,7 +459,7 @@ impl<'tcx> GotocCtx<'tcx> {
}
}
// "simd_shuffle#" => handled in an `if` preceding this match
"simd_sub" => codegen_intrinsic_binop!(sub),
"simd_sub" => codegen_simd_with_overflow_check!(sub, sub_overflow_p),
"simd_xor" => codegen_intrinsic_binop!(bitxor),
"size_of" => codegen_intrinsic_const!(),
"size_of_val" => codegen_size_align!(size),
Expand All @@ -422,9 +477,9 @@ impl<'tcx> GotocCtx<'tcx> {
"unaligned_volatile_load" => {
self.codegen_expr_to_place(p, fargs.remove(0).dereference())
}
"unchecked_add" => codegen_intrinsic_binop!(plus),
"unchecked_add" => codegen_op_with_overflow_check!(add_overflow),
"unchecked_div" => codegen_intrinsic_binop!(div),
"unchecked_mul" => codegen_intrinsic_binop!(mul),
"unchecked_mul" => codegen_op_with_overflow_check!(mul_overflow),
"unchecked_rem" => codegen_intrinsic_binop!(rem),
"unchecked_shl" => codegen_intrinsic_binop!(shl),
"unchecked_shr" => {
Expand All @@ -434,15 +489,15 @@ impl<'tcx> GotocCtx<'tcx> {
codegen_intrinsic_binop!(lshr)
}
}
"unchecked_sub" => codegen_intrinsic_binop!(sub),
"unchecked_sub" => codegen_op_with_overflow_check!(sub_overflow),
"unlikely" => self.codegen_expr_to_place(p, fargs.remove(0)),
"unreachable" => Stmt::assert_false("unreachable", loc),
"volatile_copy_memory" => codegen_intrinsic_copy!(Memmove),
"volatile_copy_nonoverlapping_memory" => codegen_intrinsic_copy!(Memcpy),
"volatile_load" => self.codegen_expr_to_place(p, fargs.remove(0).dereference()),
"wrapping_add" => codegen_intrinsic_binop!(plus),
"wrapping_mul" => codegen_intrinsic_binop!(mul),
"wrapping_sub" => codegen_intrinsic_binop!(sub),
"wrapping_add" => codegen_wrapping_op!(plus),
"wrapping_mul" => codegen_wrapping_op!(mul),
"wrapping_sub" => codegen_wrapping_op!(sub),
"write_bytes" => {
let dst = fargs.remove(0).cast_to(Type::void_pointer());
let val = fargs.remove(0).cast_to(Type::c_int());
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_rmc/src/codegen/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ impl<'tcx> GotocCtx<'tcx> {
let relative_discr = if *niche_start == 0 {
niche_val
} else {
// This should be a wrapping sub.
niche_val.sub(Expr::int_constant(*niche_start, discr_ty.clone()))
};
let relative_max =
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_rmc/src/compiler_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ impl CodegenBackend for GotocCodegenBackend {
) -> Box<dyn Any> {
use rustc_hir::def_id::LOCAL_CRATE;

if !tcx.sess.overflow_checks() {
tcx.sess.err("RMC requires overflow checks in order to provide a sound analysis.")
}

super::utils::init();

let codegen_units: &'tcx [CodegenUnit<'_>] = tcx.collect_and_partition_mono_items(()).1;
Expand Down
1 change: 1 addition & 0 deletions scripts/rmc-rustc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ then
else
set_rmc_lib_path
RMC_FLAGS="-Z codegen-backend=gotoc \
-C overflow-checks=on \
-Z trim-diagnostic-paths=no \
-Z human_readable_cgu_names \
--cfg=rmc \
Expand Down
9 changes: 6 additions & 3 deletions scripts/rmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
MEMORY_SAFETY_CHECKS = ["--bounds-check",
"--pointer-check",
"--pointer-primitive-check"]

# We no longer use --(un)signed-overflow-check" by default since rust already add assertions for places where wrapping
# is an error.
OVERFLOW_CHECKS = ["--conversion-check",
"--div-by-zero-check",
"--float-overflow-check",
"--nan-check",
"--pointer-overflow-check",
"--signed-overflow-check",
"--undefined-shift-check",
"--unsigned-overflow-check"]
"--undefined-shift-check"]
UNWINDING_CHECKS = ["--unwinding-assertions"]


Expand Down Expand Up @@ -172,6 +173,8 @@ def run_cmd(

process = subprocess.CompletedProcess(None, EXIT_CODE_SUCCESS, stdout=cmd_line)
else:
if verbose:
print(' '.join(cmd))
process = subprocess.run(
cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
env=env, cwd=cwd)
Expand Down
2 changes: 1 addition & 1 deletion src/test/expected/dry-run/expected
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
rmc-rustc -Z symbol-mangling-version=v0 -Z symbol_table_passes=
symtab2gb
goto-cc --function main
cbmc --bounds-check --pointer-check --pointer-primitive-check --conversion-check --div-by-zero-check --float-overflow-check --nan-check --pointer-overflow-check --signed-overflow-check --undefined-shift-check --unsigned-overflow-check --unwinding-assertions --object-bits 16 --function main
cbmc --bounds-check --pointer-check --pointer-primitive-check --conversion-check --div-by-zero-check --float-overflow-check --nan-check --pointer-overflow-check --undefined-shift-check --unwinding-assertions --object-bits 16 --function main
7 changes: 0 additions & 7 deletions src/test/firecracker/virtio-block-parse/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

// rmc-flags: --no-overflow-checks

// We use `--no-overflow-checks` in this test to avoid getting
// a verification failure:
// [overflow.1] arithmetic overflow on unsigned + in var_6 + var_7: FAILURE
// Tracking issue: https://github.com/model-checking/rmc/issues/307

// Example from Firecracker virtio block device
// We test the parse function against an arbitrary guest memory

Expand Down
22 changes: 22 additions & 0 deletions src/test/rmc/ArithOperators/unchecked.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that none of these operations trigger spurious overflow checks.
#![feature(unchecked_math)]

macro_rules! verify_no_overflow {
($cf: ident, $uf: ident) => {{
let a: u8 = rmc::nondet();
let b: u8 = rmc::nondet();
let checked = a.$cf(b);
rmc::assume(checked.is_some());
let unchecked = unsafe { a.$uf(b) };
assert!(checked.unwrap() == unchecked);
}};
}

fn main() {
verify_no_overflow!(checked_add, unchecked_add);
verify_no_overflow!(checked_sub, unchecked_sub);
verify_no_overflow!(checked_mul, unchecked_mul);
}
13 changes: 13 additions & 0 deletions src/test/rmc/ArithOperators/unchecked_add_fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that unchecked add trigger overflow checks.
// rmc-verify-fail

#![feature(unchecked_math)]

pub fn main() {
let a: u8 = rmc::nondet();
let b: u8 = rmc::nondet();
unsafe { a.unchecked_add(b) };
}
13 changes: 13 additions & 0 deletions src/test/rmc/ArithOperators/unchecked_mul_fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that unchecked mul trigger overflow checks.
// rmc-verify-fail

#![feature(unchecked_math)]

pub fn main() {
let a: u8 = rmc::nondet();
let b: u8 = rmc::nondet();
unsafe { a.unchecked_mul(b) };
}
13 changes: 13 additions & 0 deletions src/test/rmc/ArithOperators/unchecked_sub_fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that unchecked sub trigger overflow checks.
// rmc-verify-fail

#![feature(unchecked_math)]

fn main() {
let a: u8 = rmc::nondet();
let b: u8 = rmc::nondet();
unsafe { a.unchecked_sub(b) };
}
21 changes: 21 additions & 0 deletions src/test/rmc/ArithOperators/unsafe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that none of these operations trigger spurious overflow checks.

macro_rules! verify_no_overflow {
($cf: ident, $uf: tt) => {{
let a: u8 = rmc::nondet();
let b: u8 = rmc::nondet();
let checked = a.$cf(b);
rmc::assume(checked.is_some());
let unchecked = unsafe { a $uf b };
assert!(checked.unwrap() == unchecked);
}};
}

fn main() {
verify_no_overflow!(checked_add, +);
verify_no_overflow!(checked_sub, -);
verify_no_overflow!(checked_mul, *);
}
13 changes: 13 additions & 0 deletions src/test/rmc/ArithOperators/unsafe_add_fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that regular arithmetic operations in unsafe blocks still trigger overflow checks.
// rmc-verify-fail
// rmc-flags: --function check_add
// compile-flags: --crate-type lib

pub fn check_add(a: u8, b: u8) {
unsafe {
a + b;
}
}
13 changes: 13 additions & 0 deletions src/test/rmc/ArithOperators/unsafe_mul_fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that regular arithmetic operations in unsafe blocks still trigger overflow checks.
// rmc-verify-fail
// rmc-flags: --function check_mul
// compile-flags: --crate-type lib

pub fn check_add(a: u8, b: u8) {
unsafe {
a * b;
}
}
13 changes: 13 additions & 0 deletions src/test/rmc/ArithOperators/unsafe_sub_fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that regular arithmetic operations in unsafe blocks still trigger overflow checks.
// rmc-verify-fail
// rmc-flags: --function check_sub
// compile-flags: --crate-type lib

pub fn check_sub(a: u8, b: u8) {
unsafe {
a - b;
}
}
16 changes: 16 additions & 0 deletions src/test/rmc/ArithOperators/wrapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Check that none of these operations trigger spurious overflow checks.
#![feature(core_intrinsics)]

fn main() {
let a: u8 = rmc::nondet();
let b: u8 = rmc::nondet();
let sum0 = core::intrinsics::wrapping_add(a, b);
let sum1 = a.wrapping_add(b);
let sum2 = a.checked_add(b);
assert!(sum0 == sum1);
assert!(sum1 >= b || sum2.is_none());
assert!(sum1 >= a || sum2.is_none());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to add sum1 >= b as well?

}
Loading