|
4 | 4 | use super::typ::pointee_type;
|
5 | 5 | use super::PropertyClass;
|
6 | 6 | use crate::codegen_cprover_gotoc::GotocCtx;
|
7 |
| -use cbmc::goto_program::{BuiltinFn, Expr, Location, Stmt, Type}; |
| 7 | +use cbmc::goto_program::{ArithmeticOverflowResult, BuiltinFn, Expr, Location, Stmt, Type}; |
8 | 8 | use rustc_middle::mir::{BasicBlock, Operand, Place};
|
9 | 9 | use rustc_middle::ty::layout::LayoutOf;
|
10 | 10 | use rustc_middle::ty::{self, Ty};
|
@@ -551,6 +551,7 @@ impl<'tcx> GotocCtx<'tcx> {
|
551 | 551 | "ptr_guaranteed_eq" => codegen_ptr_guaranteed_cmp!(eq),
|
552 | 552 | "ptr_guaranteed_ne" => codegen_ptr_guaranteed_cmp!(neq),
|
553 | 553 | "ptr_offset_from" => self.codegen_ptr_offset_from(fargs, p, loc),
|
| 554 | + "ptr_offset_from_unsigned" => self.codegen_ptr_offset_from_unsigned(fargs, p, loc), |
554 | 555 | "raw_eq" => self.codegen_intrinsic_raw_eq(instance, fargs, p, loc),
|
555 | 556 | "rintf32" => codegen_unimplemented_intrinsic!(
|
556 | 557 | "https://github.com/model-checking/kani/issues/1025"
|
@@ -1009,33 +1010,77 @@ impl<'tcx> GotocCtx<'tcx> {
|
1009 | 1010 | /// https://doc.rust-lang.org/std/intrinsics/fn.ptr_offset_from.html
|
1010 | 1011 | fn codegen_ptr_offset_from(
|
1011 | 1012 | &mut self,
|
1012 |
| - mut fargs: Vec<Expr>, |
| 1013 | + fargs: Vec<Expr>, |
1013 | 1014 | p: &Place<'tcx>,
|
1014 | 1015 | loc: Location,
|
1015 | 1016 | ) -> Stmt {
|
1016 |
| - let dst_ptr = fargs.remove(0); |
1017 |
| - let src_ptr = fargs.remove(0); |
1018 |
| - |
1019 |
| - // Compute the offset with standard substraction using `isize` |
1020 |
| - let cast_dst_ptr = dst_ptr.clone().cast_to(Type::ssize_t()); |
1021 |
| - let cast_src_ptr = src_ptr.clone().cast_to(Type::ssize_t()); |
1022 |
| - let offset = cast_dst_ptr.sub_overflow(cast_src_ptr); |
| 1017 | + let (offset_expr, offset_overflow) = self.codegen_ptr_offset_from_expr(fargs); |
1023 | 1018 |
|
1024 | 1019 | // Check that computing `offset` in bytes would not overflow an `isize`
|
1025 | 1020 | // These checks may allow a wrapping-around behavior in CBMC:
|
1026 | 1021 | // https://github.com/model-checking/kani/issues/1150
|
1027 | 1022 | let overflow_check = self.codegen_assert(
|
1028 |
| - offset.overflowed.not(), |
| 1023 | + offset_overflow.overflowed.not(), |
1029 | 1024 | PropertyClass::ArithmeticOverflow,
|
1030 | 1025 | "attempt to compute offset in bytes which would overflow an `isize`",
|
1031 | 1026 | loc,
|
1032 | 1027 | );
|
1033 | 1028 |
|
1034 |
| - // Re-compute the offset with standard substraction (no casts this time) |
1035 |
| - let offset_expr = self.codegen_expr_to_place(p, dst_ptr.sub(src_ptr)); |
| 1029 | + let offset_expr = self.codegen_expr_to_place(p, offset_expr); |
1036 | 1030 | Stmt::block(vec![overflow_check, offset_expr], loc)
|
1037 | 1031 | }
|
1038 | 1032 |
|
| 1033 | + /// `ptr_offset_from_unsigned` returns the offset between two pointers where the order is known. |
| 1034 | + /// The logic is similar to `ptr_offset_from` but the return value is a `usize`. |
| 1035 | + /// See https://github.com/rust-lang/rust/issues/95892 for more details |
| 1036 | + fn codegen_ptr_offset_from_unsigned( |
| 1037 | + &mut self, |
| 1038 | + fargs: Vec<Expr>, |
| 1039 | + p: &Place<'tcx>, |
| 1040 | + loc: Location, |
| 1041 | + ) -> Stmt { |
| 1042 | + let (offset_expr, offset_overflow) = self.codegen_ptr_offset_from_expr(fargs); |
| 1043 | + |
| 1044 | + // Check that computing `offset` in bytes would not overflow an `isize` |
| 1045 | + // These checks may allow a wrapping-around behavior in CBMC: |
| 1046 | + // https://github.com/model-checking/kani/issues/1150 |
| 1047 | + let overflow_check = self.codegen_assert_assume( |
| 1048 | + offset_overflow.overflowed.not(), |
| 1049 | + PropertyClass::ArithmeticOverflow, |
| 1050 | + "attempt to compute offset in bytes which would overflow an `isize`", |
| 1051 | + loc, |
| 1052 | + ); |
| 1053 | + |
| 1054 | + let non_negative_check = self.codegen_assert_assume( |
| 1055 | + offset_overflow.result.is_non_negative(), |
| 1056 | + PropertyClass::KaniCheck, |
| 1057 | + "attempt to compute unsigned offset with negative distance", |
| 1058 | + loc, |
| 1059 | + ); |
| 1060 | + |
| 1061 | + let offset_expr = self.codegen_expr_to_place(p, offset_expr.cast_to(Type::size_t())); |
| 1062 | + Stmt::block(vec![overflow_check, non_negative_check, offset_expr], loc) |
| 1063 | + } |
| 1064 | + |
| 1065 | + /// Both `ptr_offset_from` and `ptr_offset_from_unsigned` return the offset between two pointers. |
| 1066 | + /// This function implements the common logic between them. |
| 1067 | + fn codegen_ptr_offset_from_expr( |
| 1068 | + &mut self, |
| 1069 | + mut fargs: Vec<Expr>, |
| 1070 | + ) -> (Expr, ArithmeticOverflowResult) { |
| 1071 | + let dst_ptr = fargs.remove(0); |
| 1072 | + let src_ptr = fargs.remove(0); |
| 1073 | + |
| 1074 | + // Compute the offset with standard substraction using `isize` |
| 1075 | + let cast_dst_ptr = dst_ptr.clone().cast_to(Type::ssize_t()); |
| 1076 | + let cast_src_ptr = src_ptr.clone().cast_to(Type::ssize_t()); |
| 1077 | + let offset_overflow = cast_dst_ptr.sub_overflow(cast_src_ptr); |
| 1078 | + |
| 1079 | + // Re-compute the offset with standard substraction (no casts this time) |
| 1080 | + let ptr_offset_expr = dst_ptr.sub(src_ptr); |
| 1081 | + (ptr_offset_expr, offset_overflow) |
| 1082 | + } |
| 1083 | + |
1039 | 1084 | /// A transmute is a bitcast from the argument type to the return type.
|
1040 | 1085 | /// https://doc.rust-lang.org/std/intrinsics/fn.transmute.html
|
1041 | 1086 | ///
|
|
0 commit comments