@@ -19,7 +19,6 @@ use crate::kani_middle::transform::check_values::SourceOp::UnsupportedCheck;
1919use crate :: kani_middle:: transform:: { TransformPass , TransformationType } ;
2020use crate :: kani_queries:: QueryDb ;
2121use rustc_middle:: ty:: TyCtxt ;
22- use rustc_smir:: rustc_internal;
2322use stable_mir:: abi:: { FieldsShape , Scalar , TagEncoding , ValueAbi , VariantsShape , WrappingRange } ;
2423use stable_mir:: mir:: mono:: { Instance , InstanceKind } ;
2524use stable_mir:: mir:: visit:: { Location , PlaceContext , PlaceRef } ;
@@ -31,19 +30,14 @@ use stable_mir::mir::{
3130use stable_mir:: target:: { MachineInfo , MachineSize } ;
3231use stable_mir:: ty:: { AdtKind , Const , IndexedVal , RigidTy , Ty , TyKind , UintTy } ;
3332use stable_mir:: CrateDef ;
34- use std:: fmt:: { Debug , Formatter } ;
33+ use std:: fmt:: Debug ;
3534use strum_macros:: AsRefStr ;
3635use tracing:: { debug, trace} ;
3736
3837/// Instrument the code with checks for invalid values.
38+ #[ derive( Debug ) ]
3939pub struct ValidValuePass {
40- check_type : CheckType ,
41- }
42-
43- impl ValidValuePass {
44- pub fn new ( tcx : TyCtxt ) -> Self {
45- ValidValuePass { check_type : CheckType :: new ( tcx) }
46- }
40+ pub check_type : CheckType ,
4741}
4842
4943impl TransformPass for ValidValuePass {
@@ -81,13 +75,6 @@ impl TransformPass for ValidValuePass {
8175 }
8276}
8377
84- impl Debug for ValidValuePass {
85- /// Implement manually since MachineInfo doesn't currently derive Debug.
86- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
87- "ValidValuePass" . fmt ( f)
88- }
89- }
90-
9178impl ValidValuePass {
9279 fn build_check ( & self , tcx : TyCtxt , body : & mut MutableBody , instruction : UnsafeInstruction ) {
9380 debug ! ( ?instruction, "build_check" ) ;
@@ -98,89 +85,30 @@ impl ValidValuePass {
9885 let value = body. new_assignment ( rvalue, & mut source) ;
9986 let rvalue_ptr = Rvalue :: AddressOf ( Mutability :: Not , Place :: from ( value) ) ;
10087 for range in ranges {
101- let result =
102- self . build_limits ( body, & range, rvalue_ptr. clone ( ) , & mut source) ;
103- let msg = format ! (
104- "Undefined Behavior: Invalid value of type `{}`" ,
105- // TODO: Fix pretty_ty
106- rustc_internal:: internal( tcx, target_ty)
107- ) ;
88+ let result = build_limits ( body, & range, rvalue_ptr. clone ( ) , & mut source) ;
89+ let msg =
90+ format ! ( "Undefined Behavior: Invalid value of type `{target_ty}`" , ) ;
10891 body. add_check ( tcx, & self . check_type , & mut source, result, & msg) ;
10992 }
11093 }
11194 SourceOp :: DerefValidity { pointee_ty, rvalue, ranges } => {
11295 for range in ranges {
113- let result = self . build_limits ( body, & range, rvalue. clone ( ) , & mut source) ;
114- let msg = format ! (
115- "Undefined Behavior: Invalid value of type `{}`" ,
116- // TODO: Fix pretty_ty
117- rustc_internal:: internal( tcx, pointee_ty)
118- ) ;
96+ let result = build_limits ( body, & range, rvalue. clone ( ) , & mut source) ;
97+ let msg =
98+ format ! ( "Undefined Behavior: Invalid value of type `{pointee_ty}`" , ) ;
11999 body. add_check ( tcx, & self . check_type , & mut source, result, & msg) ;
120100 }
121101 }
122102 SourceOp :: UnsupportedCheck { check, ty } => {
123103 let reason = format ! (
124- "Kani currently doesn't support checking validity of `{check}` for `{}` type" ,
125- rustc_internal:: internal( tcx, ty)
104+ "Kani currently doesn't support checking validity of `{check}` for `{ty}`" ,
126105 ) ;
127106 self . unsupported_check ( tcx, body, & mut source, & reason) ;
128107 }
129108 }
130109 }
131110 }
132111
133- fn build_limits (
134- & self ,
135- body : & mut MutableBody ,
136- req : & ValidValueReq ,
137- rvalue_ptr : Rvalue ,
138- source : & mut SourceInstruction ,
139- ) -> Local {
140- let span = source. span ( body. blocks ( ) ) ;
141- debug ! ( ?req, ?rvalue_ptr, ?span, "build_limits" ) ;
142- let primitive_ty = uint_ty ( req. size . bytes ( ) ) ;
143- let start_const = body. new_const_operand ( req. valid_range . start , primitive_ty, span) ;
144- let end_const = body. new_const_operand ( req. valid_range . end , primitive_ty, span) ;
145- let orig_ptr = if req. offset != 0 {
146- let start_ptr = move_local ( body. new_assignment ( rvalue_ptr, source) ) ;
147- let byte_ptr = move_local ( body. new_cast_ptr (
148- start_ptr,
149- Ty :: unsigned_ty ( UintTy :: U8 ) ,
150- Mutability :: Not ,
151- source,
152- ) ) ;
153- let offset_const = body. new_const_operand ( req. offset as _ , UintTy :: Usize , span) ;
154- let offset = move_local ( body. new_assignment ( Rvalue :: Use ( offset_const) , source) ) ;
155- move_local ( body. new_binary_op ( BinOp :: Offset , byte_ptr, offset, source) )
156- } else {
157- move_local ( body. new_assignment ( rvalue_ptr, source) )
158- } ;
159- let value_ptr =
160- body. new_cast_ptr ( orig_ptr, Ty :: unsigned_ty ( primitive_ty) , Mutability :: Not , source) ;
161- let value =
162- Operand :: Copy ( Place { local : value_ptr, projection : vec ! [ ProjectionElem :: Deref ] } ) ;
163- let start_result = body. new_binary_op ( BinOp :: Ge , value. clone ( ) , start_const, source) ;
164- let end_result = body. new_binary_op ( BinOp :: Le , value, end_const, source) ;
165- if req. valid_range . wraps_around ( ) {
166- // valid >= start || valid <= end
167- body. new_binary_op (
168- BinOp :: BitOr ,
169- move_local ( start_result) ,
170- move_local ( end_result) ,
171- source,
172- )
173- } else {
174- // valid >= start && valid <= end
175- body. new_binary_op (
176- BinOp :: BitAnd ,
177- move_local ( start_result) ,
178- move_local ( end_result) ,
179- source,
180- )
181- }
182- }
183-
184112 fn unsupported_check (
185113 & self ,
186114 tcx : TyCtxt ,
@@ -216,7 +144,7 @@ fn uint_ty(bytes: usize) -> UintTy {
216144
217145/// Represent a requirement for the value stored in the given offset.
218146#[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
219- struct ValidValueReq {
147+ pub struct ValidValueReq {
220148 /// Offset in bytes.
221149 offset : usize ,
222150 /// Size of this requirement.
@@ -384,9 +312,13 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
384312 } else if self . target . is_none ( ) {
385313 // Leave it as an exhaustive match to be notified when a new kind is added.
386314 match & stmt. kind {
387- StatementKind :: Intrinsic ( NonDivergingIntrinsic :: CopyNonOverlapping ( _) ) => {
388- // Source and destination have the same type, so no invalid value cannot be
389- // generated.
315+ StatementKind :: Intrinsic ( NonDivergingIntrinsic :: CopyNonOverlapping ( copy) ) => {
316+ // Source is a *const T and it must be safe for read.
317+ // TODO: Implement value check.
318+ self . push_target ( SourceOp :: UnsupportedCheck {
319+ check : "copy_nonoverlapping" . to_string ( ) ,
320+ ty : copy. src . ty ( self . locals ) . unwrap ( ) ,
321+ } ) ;
390322 }
391323 StatementKind :: Assign ( place, rvalue) => {
392324 // First check rvalue.
@@ -573,11 +505,27 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
573505 // We only care about *mut T as *mut U
574506 return ;
575507 } ;
508+ if dest_pointee_ty. kind ( ) . is_unit ( ) {
509+ // Ignore cast to *mut () since nothing can be written to it.
510+ // This is a common pattern
511+ return ;
512+ }
513+
576514 let src_ty = op. ty ( self . locals ) . unwrap ( ) ;
577515 debug ! ( ?src_ty, ?dest_ty, "visit_rvalue mutcast" ) ;
578516 let TyKind :: RigidTy ( RigidTy :: RawPtr ( src_pointee_ty, _) ) = src_ty. kind ( ) else {
579517 unreachable ! ( )
580518 } ;
519+
520+ if src_pointee_ty. kind ( ) . is_unit ( ) {
521+ // We cannot track what was the initial type. Thus, fail.
522+ self . push_target ( SourceOp :: UnsupportedCheck {
523+ check : "mutable cast" . to_string ( ) ,
524+ ty : src_ty,
525+ } ) ;
526+ return ;
527+ }
528+
581529 if let Ok ( src_validity) =
582530 ty_validity_per_offset ( & self . machine , src_pointee_ty, 0 )
583531 {
@@ -626,6 +574,8 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
626574 } )
627575 }
628576 }
577+ // `DynStar` is not currently supported in Kani.
578+ // TODO: https://github.com/model-checking/kani/issues/1784
629579 CastKind :: DynStar => self . push_target ( UnsupportedCheck {
630580 check : "Dyn*" . to_string ( ) ,
631581 ty : ( rvalue. ty ( self . locals ) . unwrap ( ) ) ,
@@ -784,10 +734,58 @@ fn expect_instance(locals: &[LocalDecl], func: &Operand) -> Instance {
784734 }
785735}
786736
737+ /// Instrument MIR to check the value pointed by `rvalue_ptr` satisfies requirement `req`.
738+ ///
739+ /// The MIR will do something equivalent to:
740+ /// ```rust
741+ /// let ptr = rvalue_ptr.byte_offset(req.offset);
742+ /// let typed_ptr = ptr as *const Unsigned<req.size>; // Some unsigned type with length req.size
743+ /// let value = unsafe { *typed_ptr };
744+ /// req.valid_range.contains(value)
745+ /// ```
746+ pub fn build_limits (
747+ body : & mut MutableBody ,
748+ req : & ValidValueReq ,
749+ rvalue_ptr : Rvalue ,
750+ source : & mut SourceInstruction ,
751+ ) -> Local {
752+ let span = source. span ( body. blocks ( ) ) ;
753+ debug ! ( ?req, ?rvalue_ptr, ?span, "build_limits" ) ;
754+ let primitive_ty = uint_ty ( req. size . bytes ( ) ) ;
755+ let start_const = body. new_const_operand ( req. valid_range . start , primitive_ty, span) ;
756+ let end_const = body. new_const_operand ( req. valid_range . end , primitive_ty, span) ;
757+ let orig_ptr = if req. offset != 0 {
758+ let start_ptr = move_local ( body. new_assignment ( rvalue_ptr, source) ) ;
759+ let byte_ptr = move_local ( body. new_cast_ptr (
760+ start_ptr,
761+ Ty :: unsigned_ty ( UintTy :: U8 ) ,
762+ Mutability :: Not ,
763+ source,
764+ ) ) ;
765+ let offset_const = body. new_const_operand ( req. offset as _ , UintTy :: Usize , span) ;
766+ let offset = move_local ( body. new_assignment ( Rvalue :: Use ( offset_const) , source) ) ;
767+ move_local ( body. new_binary_op ( BinOp :: Offset , byte_ptr, offset, source) )
768+ } else {
769+ move_local ( body. new_assignment ( rvalue_ptr, source) )
770+ } ;
771+ let value_ptr =
772+ body. new_cast_ptr ( orig_ptr, Ty :: unsigned_ty ( primitive_ty) , Mutability :: Not , source) ;
773+ let value = Operand :: Copy ( Place { local : value_ptr, projection : vec ! [ ProjectionElem :: Deref ] } ) ;
774+ let start_result = body. new_binary_op ( BinOp :: Ge , value. clone ( ) , start_const, source) ;
775+ let end_result = body. new_binary_op ( BinOp :: Le , value, end_const, source) ;
776+ if req. valid_range . wraps_around ( ) {
777+ // valid >= start || valid <= end
778+ body. new_binary_op ( BinOp :: BitOr , move_local ( start_result) , move_local ( end_result) , source)
779+ } else {
780+ // valid >= start && valid <= end
781+ body. new_binary_op ( BinOp :: BitAnd , move_local ( start_result) , move_local ( end_result) , source)
782+ }
783+ }
784+
787785/// Traverse the type and find all invalid values and their location in memory.
788786///
789787/// Not all values are currently supported. For those not supported, we return Error.
790- fn ty_validity_per_offset (
788+ pub fn ty_validity_per_offset (
791789 machine_info : & MachineInfo ,
792790 ty : Ty ,
793791 current_offset : usize ,
0 commit comments