@@ -29,8 +29,17 @@ mod uninit_visitor;
29
29
pub use ty_layout:: { PointeeInfo , PointeeLayout } ;
30
30
use uninit_visitor:: { CheckUninitVisitor , InitRelevantInstruction , MemoryInitOp } ;
31
31
32
- const SKIPPED_DIAGNOSTIC_ITEMS : & [ & str ] =
33
- & [ "KaniIsUnitPtrInitialized" , "KaniSetUnitPtrInitialized" ] ;
32
+ // Function bodies of those functions will not be instrumented as not to cause infinite recursion.
33
+ const SKIPPED_DIAGNOSTIC_ITEMS : & [ & str ] = & [
34
+ "KaniIsPtrInitialized" ,
35
+ "KaniSetPtrInitialized" ,
36
+ "KaniIsSliceChunkPtrInitialized" ,
37
+ "KaniSetSliceChunkPtrInitialized" ,
38
+ "KaniIsSlicePtrInitialized" ,
39
+ "KaniSetSlicePtrInitialized" ,
40
+ "KaniIsStrPtrInitialized" ,
41
+ "KaniSetStrPtrInitialized" ,
42
+ ] ;
34
43
35
44
/// Instrument the code with checks for uninitialized memory.
36
45
#[ derive( Debug ) ]
@@ -59,8 +68,8 @@ impl TransformPass for UninitPass {
59
68
fn transform ( & mut self , tcx : TyCtxt , body : Body , instance : Instance ) -> ( bool , Body ) {
60
69
trace ! ( function=?instance. name( ) , "transform" ) ;
61
70
62
- // Need to break infinite recursion when shadow memory checks are inserted,
63
- // so the internal function responsible for shadow memory checks are skipped.
71
+ // Need to break infinite recursion when memory initialization checks are inserted, so the
72
+ // internal functions responsible for memory initialization are skipped.
64
73
if tcx
65
74
. get_diagnostic_name ( rustc_internal:: internal ( tcx, instance. def . def_id ( ) ) )
66
75
. map ( |diagnostic_name| {
@@ -74,6 +83,11 @@ impl TransformPass for UninitPass {
74
83
let mut new_body = MutableBody :: from ( body) ;
75
84
let orig_len = new_body. blocks ( ) . len ( ) ;
76
85
86
+ // Inject a call to set-up memory initialization state if the function is a harness.
87
+ if is_harness ( instance, tcx) {
88
+ inject_memory_init_setup ( & mut new_body, tcx, & mut self . mem_init_fn_cache ) ;
89
+ }
90
+
77
91
// Set of basic block indices for which analyzing first statement should be skipped.
78
92
//
79
93
// This is necessary because some checks are inserted before the source instruction, which, in
@@ -159,10 +173,12 @@ impl UninitPass {
159
173
} ;
160
174
161
175
match operation {
162
- MemoryInitOp :: Check { .. } => {
176
+ MemoryInitOp :: CheckSliceChunk { .. } | MemoryInitOp :: Check { .. } => {
163
177
self . build_get_and_check ( tcx, body, source, operation, pointee_ty_info, skip_first)
164
178
}
165
- MemoryInitOp :: Set { .. } | MemoryInitOp :: SetRef { .. } => {
179
+ MemoryInitOp :: SetSliceChunk { .. }
180
+ | MemoryInitOp :: Set { .. }
181
+ | MemoryInitOp :: SetRef { .. } => {
166
182
self . build_set ( tcx, body, source, operation, pointee_ty_info, skip_first)
167
183
}
168
184
MemoryInitOp :: Unsupported { .. } => {
@@ -171,8 +187,8 @@ impl UninitPass {
171
187
}
172
188
}
173
189
174
- /// Inject a load from shadow memory tracking memory initialization and an assertion that all
175
- /// non-padding bytes are initialized.
190
+ /// Inject a load from memory initialization state and an assertion that all non-padding bytes
191
+ /// are initialized.
176
192
fn build_get_and_check (
177
193
& mut self ,
178
194
tcx : TyCtxt ,
@@ -189,18 +205,34 @@ impl UninitPass {
189
205
let ptr_operand = operation. mk_operand ( body, source) ;
190
206
match pointee_info. layout ( ) {
191
207
PointeeLayout :: Sized { layout } => {
208
+ let layout_operand = mk_layout_operand ( body, source, operation. position ( ) , & layout) ;
209
+ // Depending on whether accessing the known number of elements in the slice, need to
210
+ // pass is as an argument.
211
+ let ( diagnostic, args) = match & operation {
212
+ MemoryInitOp :: Check { .. } => {
213
+ let diagnostic = "KaniIsPtrInitialized" ;
214
+ let args = vec ! [ ptr_operand. clone( ) , layout_operand] ;
215
+ ( diagnostic, args)
216
+ }
217
+ MemoryInitOp :: CheckSliceChunk { .. } => {
218
+ let diagnostic = "KaniIsSliceChunkPtrInitialized" ;
219
+ let args =
220
+ vec ! [ ptr_operand. clone( ) , layout_operand, operation. expect_count( ) ] ;
221
+ ( diagnostic, args)
222
+ }
223
+ _ => unreachable ! ( ) ,
224
+ } ;
192
225
let is_ptr_initialized_instance = resolve_mem_init_fn (
193
- get_mem_init_fn_def ( tcx, "KaniIsPtrInitialized" , & mut self . mem_init_fn_cache ) ,
226
+ get_mem_init_fn_def ( tcx, diagnostic , & mut self . mem_init_fn_cache ) ,
194
227
layout. len ( ) ,
195
228
* pointee_info. ty ( ) ,
196
229
) ;
197
- let layout_operand = mk_layout_operand ( body, source, operation. position ( ) , & layout) ;
198
230
collect_skipped ( & operation, body, skip_first) ;
199
231
body. add_call (
200
232
& is_ptr_initialized_instance,
201
233
source,
202
234
operation. position ( ) ,
203
- vec ! [ ptr_operand . clone ( ) , layout_operand , operation . expect_count ( ) ] ,
235
+ args ,
204
236
ret_place. clone ( ) ,
205
237
) ;
206
238
}
@@ -252,8 +284,8 @@ impl UninitPass {
252
284
)
253
285
}
254
286
255
- /// Inject a store into shadow memory tracking memory initialization to initialize or
256
- /// deinitialize all non-padding bytes.
287
+ /// Inject a store into memory initialization state to initialize or deinitialize all
288
+ /// non-padding bytes.
257
289
fn build_set (
258
290
& mut self ,
259
291
tcx : TyCtxt ,
@@ -272,27 +304,50 @@ impl UninitPass {
272
304
273
305
match pointee_info. layout ( ) {
274
306
PointeeLayout :: Sized { layout } => {
307
+ let layout_operand = mk_layout_operand ( body, source, operation. position ( ) , & layout) ;
308
+ // Depending on whether writing to the known number of elements in the slice, need to
309
+ // pass is as an argument.
310
+ let ( diagnostic, args) = match & operation {
311
+ MemoryInitOp :: Set { .. } | MemoryInitOp :: SetRef { .. } => {
312
+ let diagnostic = "KaniSetPtrInitialized" ;
313
+ let args = vec ! [
314
+ ptr_operand,
315
+ layout_operand,
316
+ Operand :: Constant ( ConstOperand {
317
+ span: source. span( body. blocks( ) ) ,
318
+ user_ty: None ,
319
+ const_: MirConst :: from_bool( value) ,
320
+ } ) ,
321
+ ] ;
322
+ ( diagnostic, args)
323
+ }
324
+ MemoryInitOp :: SetSliceChunk { .. } => {
325
+ let diagnostic = "KaniSetSliceChunkPtrInitialized" ;
326
+ let args = vec ! [
327
+ ptr_operand,
328
+ layout_operand,
329
+ operation. expect_count( ) ,
330
+ Operand :: Constant ( ConstOperand {
331
+ span: source. span( body. blocks( ) ) ,
332
+ user_ty: None ,
333
+ const_: MirConst :: from_bool( value) ,
334
+ } ) ,
335
+ ] ;
336
+ ( diagnostic, args)
337
+ }
338
+ _ => unreachable ! ( ) ,
339
+ } ;
275
340
let set_ptr_initialized_instance = resolve_mem_init_fn (
276
- get_mem_init_fn_def ( tcx, "KaniSetPtrInitialized" , & mut self . mem_init_fn_cache ) ,
341
+ get_mem_init_fn_def ( tcx, diagnostic , & mut self . mem_init_fn_cache ) ,
277
342
layout. len ( ) ,
278
343
* pointee_info. ty ( ) ,
279
344
) ;
280
- let layout_operand = mk_layout_operand ( body, source, operation. position ( ) , & layout) ;
281
345
collect_skipped ( & operation, body, skip_first) ;
282
346
body. add_call (
283
347
& set_ptr_initialized_instance,
284
348
source,
285
349
operation. position ( ) ,
286
- vec ! [
287
- ptr_operand,
288
- layout_operand,
289
- operation. expect_count( ) ,
290
- Operand :: Constant ( ConstOperand {
291
- span: source. span( body. blocks( ) ) ,
292
- user_ty: None ,
293
- const_: MirConst :: from_bool( value) ,
294
- } ) ,
295
- ] ,
350
+ args,
296
351
ret_place,
297
352
) ;
298
353
}
@@ -426,3 +481,59 @@ pub fn resolve_mem_init_fn(fn_def: FnDef, layout_size: usize, associated_type: T
426
481
)
427
482
. unwrap ( )
428
483
}
484
+
485
+ /// Checks if the instance is a harness -- an entry point of Kani analysis.
486
+ fn is_harness ( instance : Instance , tcx : TyCtxt ) -> bool {
487
+ let harness_identifiers = [
488
+ vec ! [
489
+ rustc_span:: symbol:: Symbol :: intern( "kanitool" ) ,
490
+ rustc_span:: symbol:: Symbol :: intern( "proof_for_contract" ) ,
491
+ ] ,
492
+ vec ! [
493
+ rustc_span:: symbol:: Symbol :: intern( "kanitool" ) ,
494
+ rustc_span:: symbol:: Symbol :: intern( "proof" ) ,
495
+ ] ,
496
+ ] ;
497
+ harness_identifiers. iter ( ) . any ( |attr_path| {
498
+ tcx. has_attrs_with_path ( rustc_internal:: internal ( tcx, instance. def . def_id ( ) ) , attr_path)
499
+ } )
500
+ }
501
+
502
+ /// Inject an initial call to set-up memory initialization tracking.
503
+ fn inject_memory_init_setup (
504
+ new_body : & mut MutableBody ,
505
+ tcx : TyCtxt ,
506
+ mem_init_fn_cache : & mut HashMap < & ' static str , FnDef > ,
507
+ ) {
508
+ // First statement or terminator in the harness.
509
+ let mut source = if !new_body. blocks ( ) [ 0 ] . statements . is_empty ( ) {
510
+ SourceInstruction :: Statement { idx : 0 , bb : 0 }
511
+ } else {
512
+ SourceInstruction :: Terminator { bb : 0 }
513
+ } ;
514
+
515
+ // Dummy return place.
516
+ let ret_place = Place {
517
+ local : new_body. new_local (
518
+ Ty :: new_tuple ( & [ ] ) ,
519
+ source. span ( new_body. blocks ( ) ) ,
520
+ Mutability :: Not ,
521
+ ) ,
522
+ projection : vec ! [ ] ,
523
+ } ;
524
+
525
+ // Resolve the instance and inject a call to set-up the memory initialization state.
526
+ let memory_initialization_init = Instance :: resolve (
527
+ get_mem_init_fn_def ( tcx, "KaniInitializeMemoryInitializationState" , mem_init_fn_cache) ,
528
+ & GenericArgs ( vec ! [ ] ) ,
529
+ )
530
+ . unwrap ( ) ;
531
+
532
+ new_body. add_call (
533
+ & memory_initialization_init,
534
+ & mut source,
535
+ InsertPosition :: Before ,
536
+ vec ! [ ] ,
537
+ ret_place,
538
+ ) ;
539
+ }
0 commit comments