Skip to content

Commit 64a123d

Browse files
authored
Audit for arith_offset intrinsic (rust-lang#1228)
* Implement `arith_offset` with `codegen_offset` * Extend `codegen_offset` args with `intrinsic` * Add tests for `arith_offset`/`wrapping_offset` * Improve documentation for `codegen_offset` * Link issue and extend comment
1 parent b0c4a4e commit 64a123d

File tree

11 files changed

+156
-5
lines changed

11 files changed

+156
-5
lines changed

kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ impl<'tcx> GotocCtx<'tcx> {
383383

384384
match intrinsic {
385385
"add_with_overflow" => codegen_op_with_overflow!(add_overflow),
386-
"arith_offset" => unstable_codegen!(codegen_wrapping_op!(plus)),
386+
"arith_offset" => self.codegen_offset(intrinsic, instance, fargs, p, loc),
387387
"assert_inhabited" => self.codegen_assert_intrinsic(instance, intrinsic, span),
388388
"assert_uninit_valid" => self.codegen_assert_intrinsic(instance, intrinsic, span),
389389
"assert_zero_valid" => self.codegen_assert_intrinsic(instance, intrinsic, span),
@@ -562,7 +562,7 @@ impl<'tcx> GotocCtx<'tcx> {
562562
"https://github.com/model-checking/kani/issues/1025"
563563
),
564564
"needs_drop" => codegen_intrinsic_const!(),
565-
"offset" => self.codegen_offset(instance, fargs, p, loc),
565+
"offset" => self.codegen_offset(intrinsic, instance, fargs, p, loc),
566566
"powf32" => unstable_codegen!(codegen_simple_intrinsic!(Powf)),
567567
"powf64" => unstable_codegen!(codegen_simple_intrinsic!(Pow)),
568568
"powif32" => unstable_codegen!(codegen_simple_intrinsic!(Powif)),
@@ -992,10 +992,24 @@ impl<'tcx> GotocCtx<'tcx> {
992992
Stmt::block(vec![src_align_check, dst_align_check, overflow_check, copy_expr], loc)
993993
}
994994

995-
/// Computes the offset from a pointer
996-
/// https://doc.rust-lang.org/std/intrinsics/fn.offset.html
995+
/// Computes the offset from a pointer.
996+
///
997+
/// Note that this function handles code generation for:
998+
/// 1. The `offset` intrinsic.
999+
/// https://doc.rust-lang.org/std/intrinsics/fn.offset.html
1000+
/// 2. The `arith_offset` intrinsic.
1001+
/// https://doc.rust-lang.org/std/intrinsics/fn.arith_offset.html
1002+
///
1003+
/// Note(std): We don't check that the starting or resulting pointer stay
1004+
/// within bounds of the object they point to. Doing so causes spurious
1005+
/// failures due to the usage of these intrinsics in the standard library.
1006+
/// See https://github.com/model-checking/kani/issues/1233 for more details.
1007+
/// Also, note that this isn't a requirement for `arith_offset`, but it's
1008+
/// one of the safety conditions specified for `offset`:
1009+
/// https://doc.rust-lang.org/std/primitive.pointer.html#safety-2
9971010
fn codegen_offset(
9981011
&mut self,
1012+
intrinsic: &str,
9991013
instance: Instance<'tcx>,
10001014
mut fargs: Vec<Expr>,
10011015
p: &Place<'tcx>,
@@ -1007,7 +1021,7 @@ impl<'tcx> GotocCtx<'tcx> {
10071021
// Check that computing `offset` in bytes would not overflow
10081022
let ty = self.monomorphize(instance.substs.type_at(0));
10091023
let (offset_bytes, bytes_overflow_check) =
1010-
self.count_in_bytes(offset.clone(), ty, Type::ssize_t(), "offset", loc);
1024+
self.count_in_bytes(offset.clone(), ty, Type::ssize_t(), intrinsic, loc);
10111025

10121026
// Check that the computation would not overflow an `isize`
10131027
// These checks may allow a wrapping-around behavior in CBMC:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
dereference failure: pointer outside object bounds
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that the pointer computed with `arith_offset` causes a failure if it's
5+
// dereferenced outside the object bounds
6+
#![feature(core_intrinsics)]
7+
use std::intrinsics::arith_offset;
8+
9+
#[kani::proof]
10+
fn test_arith_offset() {
11+
let arr: [i32; 3] = [1, 2, 3];
12+
let ptr: *const i32 = arr.as_ptr();
13+
14+
unsafe {
15+
let x = *arith_offset(ptr, 3);
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
attempt to compute offset which would overflow
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `arith_offset` fails if the offset computation would
5+
// result in an arithmetic overflow
6+
#![feature(core_intrinsics)]
7+
use std::intrinsics::arith_offset;
8+
9+
#[kani::proof]
10+
fn test_arith_offset_overflow() {
11+
let s: &str = "123";
12+
let ptr: *const u8 = s.as_ptr();
13+
14+
unsafe {
15+
let _ = arith_offset(ptr, isize::MAX);
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
dereference failure: pointer outside object bounds
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that the pointer computed with `arith_offset` causes a failure if it's
5+
// dereferenced outside the object bounds
6+
#![feature(core_intrinsics)]
7+
use std::intrinsics::arith_offset;
8+
9+
#[kani::proof]
10+
fn test_arith_offset() {
11+
let s: &str = "123";
12+
let ptr: *const u8 = s.as_ptr();
13+
14+
unsafe {
15+
let x = *arith_offset(ptr, 4) as char;
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
arith_offset: attempt to compute number in bytes which would overflow
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Check that an offset (computed with `wrapping_offset`) that overflows an
5+
// `isize::MAX` triggers a verification failure.
6+
use std::convert::TryInto;
7+
8+
#[kani::proof]
9+
unsafe fn check_wrap_offset() {
10+
let v: &[u128] = &[0; 200];
11+
let v_100: *const u128 = &v[100];
12+
let max_offset = usize::MAX / std::mem::size_of::<u128>();
13+
let v_wrap: *const u128 = v_100.wrapping_offset((max_offset + 1).try_into().unwrap());
14+
assert_eq!(v_100, v_wrap);
15+
assert_eq!(*v_100, *v_wrap);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `arith_offset` returns the expected addresses
5+
#![feature(core_intrinsics)]
6+
use std::intrinsics::arith_offset;
7+
8+
#[kani::proof]
9+
fn test_arith_offset() {
10+
let arr: [i32; 3] = [1, 2, 3];
11+
let ptr: *const i32 = arr.as_ptr();
12+
13+
unsafe {
14+
assert_eq!(*arith_offset(ptr, 0), 1);
15+
assert_eq!(*arith_offset(ptr, 1), 2);
16+
assert_eq!(*arith_offset(ptr, 2), 3);
17+
assert_eq!(*arith_offset(ptr, 2).sub(1), 2);
18+
19+
// This wouldn't be okay because it's
20+
// more than one byte past the object
21+
// let x = *arith_offset(ptr, 3);
22+
23+
// Check that the results are the same with a pointer
24+
// that goes 1 element behind the original one
25+
let other_ptr: *const i32 = ptr.add(1);
26+
27+
assert_eq!(*arith_offset(other_ptr, 0), 2);
28+
assert_eq!(*arith_offset(other_ptr, 1), 3);
29+
assert_eq!(*arith_offset(other_ptr, 1).sub(1), 2);
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `arith_offset` returns the expected addresses
5+
#![feature(core_intrinsics)]
6+
use std::intrinsics::arith_offset;
7+
8+
#[kani::proof]
9+
fn test_arith_offset() {
10+
let s: &str = "123";
11+
let ptr: *const u8 = s.as_ptr();
12+
13+
unsafe {
14+
assert_eq!(*arith_offset(ptr, 0) as char, '1');
15+
assert_eq!(*arith_offset(ptr, 1) as char, '2');
16+
assert_eq!(*arith_offset(ptr, 2) as char, '3');
17+
assert_eq!(*arith_offset(ptr, 2).sub(1) as char, '2');
18+
19+
// This is okay because it's one byte past the object,
20+
// but there is nothing we can assert about it
21+
let x = *arith_offset(ptr, 3);
22+
23+
// Check that the results are the same with a pointer
24+
// that goes 1 element behind the original one
25+
let other_ptr: *const u8 = ptr.add(1);
26+
27+
assert_eq!(*arith_offset(other_ptr, 0) as char, '2');
28+
assert_eq!(*arith_offset(other_ptr, 1) as char, '3');
29+
assert_eq!(*arith_offset(other_ptr, 1).sub(1) as char, '2');
30+
}
31+
}

0 commit comments

Comments
 (0)