@@ -19,7 +19,6 @@ use crate::kani_middle::transform::check_values::SourceOp::UnsupportedCheck;
19
19
use crate :: kani_middle:: transform:: { TransformPass , TransformationType } ;
20
20
use crate :: kani_queries:: QueryDb ;
21
21
use rustc_middle:: ty:: TyCtxt ;
22
- use rustc_smir:: rustc_internal;
23
22
use stable_mir:: abi:: { FieldsShape , Scalar , TagEncoding , ValueAbi , VariantsShape , WrappingRange } ;
24
23
use stable_mir:: mir:: mono:: { Instance , InstanceKind } ;
25
24
use stable_mir:: mir:: visit:: { Location , PlaceContext , PlaceRef } ;
@@ -31,19 +30,14 @@ use stable_mir::mir::{
31
30
use stable_mir:: target:: { MachineInfo , MachineSize } ;
32
31
use stable_mir:: ty:: { AdtKind , Const , IndexedVal , RigidTy , Ty , TyKind , UintTy } ;
33
32
use stable_mir:: CrateDef ;
34
- use std:: fmt:: { Debug , Formatter } ;
33
+ use std:: fmt:: Debug ;
35
34
use strum_macros:: AsRefStr ;
36
35
use tracing:: { debug, trace} ;
37
36
38
37
/// Instrument the code with checks for invalid values.
38
+ #[ derive( Debug ) ]
39
39
pub 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 ,
47
41
}
48
42
49
43
impl TransformPass for ValidValuePass {
@@ -81,13 +75,6 @@ impl TransformPass for ValidValuePass {
81
75
}
82
76
}
83
77
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
-
91
78
impl ValidValuePass {
92
79
fn build_check ( & self , tcx : TyCtxt , body : & mut MutableBody , instruction : UnsafeInstruction ) {
93
80
debug ! ( ?instruction, "build_check" ) ;
@@ -98,89 +85,30 @@ impl ValidValuePass {
98
85
let value = body. new_assignment ( rvalue, & mut source) ;
99
86
let rvalue_ptr = Rvalue :: AddressOf ( Mutability :: Not , Place :: from ( value) ) ;
100
87
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}`" , ) ;
108
91
body. add_check ( tcx, & self . check_type , & mut source, result, & msg) ;
109
92
}
110
93
}
111
94
SourceOp :: DerefValidity { pointee_ty, rvalue, ranges } => {
112
95
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}`" , ) ;
119
99
body. add_check ( tcx, & self . check_type , & mut source, result, & msg) ;
120
100
}
121
101
}
122
102
SourceOp :: UnsupportedCheck { check, ty } => {
123
103
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}`" ,
126
105
) ;
127
106
self . unsupported_check ( tcx, body, & mut source, & reason) ;
128
107
}
129
108
}
130
109
}
131
110
}
132
111
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
-
184
112
fn unsupported_check (
185
113
& self ,
186
114
tcx : TyCtxt ,
@@ -216,7 +144,7 @@ fn uint_ty(bytes: usize) -> UintTy {
216
144
217
145
/// Represent a requirement for the value stored in the given offset.
218
146
#[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
219
- struct ValidValueReq {
147
+ pub struct ValidValueReq {
220
148
/// Offset in bytes.
221
149
offset : usize ,
222
150
/// Size of this requirement.
@@ -384,9 +312,13 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
384
312
} else if self . target . is_none ( ) {
385
313
// Leave it as an exhaustive match to be notified when a new kind is added.
386
314
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
+ } ) ;
390
322
}
391
323
StatementKind :: Assign ( place, rvalue) => {
392
324
// First check rvalue.
@@ -573,11 +505,27 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
573
505
// We only care about *mut T as *mut U
574
506
return ;
575
507
} ;
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
+
576
514
let src_ty = op. ty ( self . locals ) . unwrap ( ) ;
577
515
debug ! ( ?src_ty, ?dest_ty, "visit_rvalue mutcast" ) ;
578
516
let TyKind :: RigidTy ( RigidTy :: RawPtr ( src_pointee_ty, _) ) = src_ty. kind ( ) else {
579
517
unreachable ! ( )
580
518
} ;
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
+
581
529
if let Ok ( src_validity) =
582
530
ty_validity_per_offset ( & self . machine , src_pointee_ty, 0 )
583
531
{
@@ -626,6 +574,8 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
626
574
} )
627
575
}
628
576
}
577
+ // `DynStar` is not currently supported in Kani.
578
+ // TODO: https://github.com/model-checking/kani/issues/1784
629
579
CastKind :: DynStar => self . push_target ( UnsupportedCheck {
630
580
check : "Dyn*" . to_string ( ) ,
631
581
ty : ( rvalue. ty ( self . locals ) . unwrap ( ) ) ,
@@ -784,10 +734,58 @@ fn expect_instance(locals: &[LocalDecl], func: &Operand) -> Instance {
784
734
}
785
735
}
786
736
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
+
787
785
/// Traverse the type and find all invalid values and their location in memory.
788
786
///
789
787
/// 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 (
791
789
machine_info : & MachineInfo ,
792
790
ty : Ty ,
793
791
current_offset : usize ,
0 commit comments