11
11
use std:: fmt:: Write ;
12
12
13
13
use syntax_pos:: symbol:: Symbol ;
14
- use rustc:: ty:: layout:: { self , Size , Primitive } ;
14
+ use rustc:: ty:: layout:: { self , Size } ;
15
15
use rustc:: ty:: { self , Ty } ;
16
16
use rustc_data_structures:: fx:: FxHashSet ;
17
17
use rustc:: mir:: interpret:: {
18
18
Scalar , AllocType , EvalResult , EvalErrorKind , PointerArithmetic
19
19
} ;
20
20
21
21
use super :: {
22
- OpTy , Machine , EvalContext , ScalarMaybeUndef
22
+ OpTy , MPlaceTy , Machine , EvalContext , ScalarMaybeUndef
23
23
} ;
24
24
25
25
macro_rules! validation_failure{
@@ -63,6 +63,23 @@ pub enum PathElem {
63
63
Tag ,
64
64
}
65
65
66
+ /// State for tracking recursive validation of references
67
+ pub struct RefTracking < ' tcx > {
68
+ pub seen : FxHashSet < ( OpTy < ' tcx > ) > ,
69
+ pub todo : Vec < ( OpTy < ' tcx > , Vec < PathElem > ) > ,
70
+ }
71
+
72
+ impl < ' tcx > RefTracking < ' tcx > {
73
+ pub fn new ( op : OpTy < ' tcx > ) -> Self {
74
+ let mut ref_tracking = RefTracking {
75
+ seen : FxHashSet ( ) ,
76
+ todo : vec ! [ ( op, Vec :: new( ) ) ] ,
77
+ } ;
78
+ ref_tracking. seen . insert ( op) ;
79
+ ref_tracking
80
+ }
81
+ }
82
+
66
83
// Adding a Deref and making a copy of the path to be put into the queue
67
84
// always go together. This one does it with only new allocation.
68
85
fn path_clone_and_deref ( path : & Vec < PathElem > ) -> Vec < PathElem > {
@@ -205,6 +222,41 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
205
222
}
206
223
}
207
224
225
+ /// Validate a reference, potentially recursively. `place` is assumed to already be
226
+ /// dereferenced, i.e. it describes the target.
227
+ fn validate_ref (
228
+ & self ,
229
+ place : MPlaceTy < ' tcx > ,
230
+ path : & mut Vec < PathElem > ,
231
+ ref_tracking : Option < & mut RefTracking < ' tcx > > ,
232
+ ) -> EvalResult < ' tcx > {
233
+ // Skip recursion for some external statics
234
+ if let Scalar :: Ptr ( ptr) = place. ptr {
235
+ let alloc_kind = self . tcx . alloc_map . lock ( ) . get ( ptr. alloc_id ) ;
236
+ if let Some ( AllocType :: Static ( did) ) = alloc_kind {
237
+ // statics from other crates are already checked.
238
+ // they might be checked at a different type, but for now we want
239
+ // to avoid recursing too deeply.
240
+ // extern statics cannot be validated as they have no body.
241
+ if !did. is_local ( ) || self . tcx . is_foreign_item ( did) {
242
+ return Ok ( ( ) ) ;
243
+ }
244
+ }
245
+ }
246
+ // Check if we have encountered this pointer+layout combination
247
+ // before. Proceed recursively even for integer pointers, no
248
+ // reason to skip them! They are valid for some ZST, but not for others
249
+ // (e.g. `!` is a ZST).
250
+ let op = place. into ( ) ;
251
+ if let Some ( ref_tracking) = ref_tracking {
252
+ if ref_tracking. seen . insert ( op) {
253
+ trace ! ( "Recursing below ptr {:#?}" , * op) ;
254
+ ref_tracking. todo . push ( ( op, path_clone_and_deref ( path) ) ) ;
255
+ }
256
+ }
257
+ Ok ( ( ) )
258
+ }
259
+
208
260
/// This function checks the data at `op`.
209
261
/// It will error if the bits at the destination do not match the ones described by the layout.
210
262
/// The `path` may be pushed to, but the part that is present when the function
@@ -213,8 +265,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
213
265
& self ,
214
266
dest : OpTy < ' tcx > ,
215
267
path : & mut Vec < PathElem > ,
216
- seen : & mut FxHashSet < ( OpTy < ' tcx > ) > ,
217
- todo : & mut Vec < ( OpTy < ' tcx > , Vec < PathElem > ) > ,
268
+ mut ref_tracking : Option < & mut RefTracking < ' tcx > > ,
218
269
) -> EvalResult < ' tcx > {
219
270
trace ! ( "validate_operand: {:?}, {:#?}" , * dest, dest. layout) ;
220
271
@@ -273,7 +324,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
273
324
274
325
// Validate all fields
275
326
match dest. layout . fields {
276
- // primitives are unions with zero fields
327
+ // Primitives appear as Union with 0 fields -- except for fat pointers.
277
328
// We still check `layout.fields`, not `layout.abi`, because `layout.abi`
278
329
// is `Scalar` for newtypes around scalars, but we want to descend through the
279
330
// fields to get a proper `path`.
@@ -302,32 +353,61 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
302
353
} ;
303
354
let scalar = value. to_scalar_or_undef ( ) ;
304
355
self . validate_scalar ( scalar, size, scalar_layout, & path, dest. layout . ty ) ?;
305
- if scalar_layout. value == Primitive :: Pointer {
306
- // ignore integer pointers, we can't reason about the final hardware
307
- if let Scalar :: Ptr ( ptr) = scalar. not_undef ( ) ? {
308
- let alloc_kind = self . tcx . alloc_map . lock ( ) . get ( ptr. alloc_id ) ;
309
- if let Some ( AllocType :: Static ( did) ) = alloc_kind {
310
- // statics from other crates are already checked.
311
- // extern statics cannot be validated as they have no body.
312
- if !did. is_local ( ) || self . tcx . is_foreign_item ( did) {
313
- return Ok ( ( ) ) ;
314
- }
315
- }
316
- if value. layout . ty . builtin_deref ( false ) . is_some ( ) {
317
- let ptr_op = self . ref_to_mplace ( value) ?. into ( ) ;
318
- // we have not encountered this pointer+layout combination
319
- // before.
320
- if seen. insert ( ptr_op) {
321
- trace ! ( "Recursing below ptr {:#?}" , * value) ;
322
- todo. push ( ( ptr_op, path_clone_and_deref ( path) ) ) ;
323
- }
324
- }
325
- }
356
+ // Recursively check *safe* references
357
+ if dest. layout . ty . builtin_deref ( true ) . is_some ( ) &&
358
+ !dest. layout . ty . is_unsafe_ptr ( )
359
+ {
360
+ self . validate_ref ( self . ref_to_mplace ( value) ?, path, ref_tracking) ?;
326
361
}
327
362
} ,
328
363
_ => bug ! ( "bad abi for FieldPlacement::Union(0): {:#?}" , dest. layout. abi) ,
329
364
}
330
365
}
366
+ layout:: FieldPlacement :: Arbitrary { .. }
367
+ if dest. layout . ty . builtin_deref ( true ) . is_some ( ) =>
368
+ {
369
+ // This is a fat pointer.
370
+ let ptr = match self . read_value ( dest. into ( ) )
371
+ . and_then ( |val| self . ref_to_mplace ( val) )
372
+ {
373
+ Ok ( ptr) => ptr,
374
+ Err ( _) =>
375
+ return validation_failure ! (
376
+ "undefined location or metadata in fat pointer" , path
377
+ ) ,
378
+ } ;
379
+ // check metadata early, for better diagnostics
380
+ match self . tcx . struct_tail ( ptr. layout . ty ) . sty {
381
+ ty:: Dynamic ( ..) => {
382
+ match ptr. extra . unwrap ( ) . to_ptr ( ) {
383
+ Ok ( _) => { } ,
384
+ Err ( _) =>
385
+ return validation_failure ! (
386
+ "non-pointer vtable in fat pointer" , path
387
+ ) ,
388
+ }
389
+ // FIXME: More checks for the vtable.
390
+ }
391
+ ty:: Slice ( ..) | ty:: Str => {
392
+ match ptr. extra . unwrap ( ) . to_usize ( self ) {
393
+ Ok ( _) => { } ,
394
+ Err ( _) =>
395
+ return validation_failure ! (
396
+ "non-integer slice length in fat pointer" , path
397
+ ) ,
398
+ }
399
+ }
400
+ _ =>
401
+ bug ! ( "Unexpected unsized type tail: {:?}" ,
402
+ self . tcx. struct_tail( ptr. layout. ty)
403
+ ) ,
404
+ }
405
+ // for safe ptrs, recursively check it
406
+ if !dest. layout . ty . is_unsafe_ptr ( ) {
407
+ self . validate_ref ( ptr, path, ref_tracking) ?;
408
+ }
409
+ }
410
+ // Compound data structures
331
411
layout:: FieldPlacement :: Union ( _) => {
332
412
// We can't check unions, their bits are allowed to be anything.
333
413
// The fields don't need to correspond to any bit pattern of the union's fields.
@@ -411,7 +491,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
411
491
for ( i, field) in self . mplace_array_fields ( dest) ?. enumerate ( ) {
412
492
let field = field?;
413
493
path. push ( PathElem :: ArrayElem ( i) ) ;
414
- self . validate_operand ( field. into ( ) , path, seen, todo) ?;
494
+ self . validate_operand (
495
+ field. into ( ) ,
496
+ path,
497
+ ref_tracking. as_mut ( ) . map ( |r| & mut * * r)
498
+ ) ?;
415
499
path. truncate ( path_len) ;
416
500
}
417
501
}
@@ -421,60 +505,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
421
505
// An empty array. Nothing to do.
422
506
}
423
507
layout:: FieldPlacement :: Arbitrary { ref offsets, .. } => {
424
- // Fat pointers are treated like pointers, not aggregates.
425
- if dest. layout . ty . builtin_deref ( true ) . is_some ( ) {
426
- // This is a fat pointer.
427
- let ptr = match self . read_value ( dest. into ( ) )
428
- . and_then ( |val| self . ref_to_mplace ( val) )
429
- {
430
- Ok ( ptr) => ptr,
431
- Err ( _) =>
432
- return validation_failure ! (
433
- "undefined location or metadata in fat pointer" , path
434
- ) ,
435
- } ;
436
- // check metadata early, for better diagnostics
437
- match self . tcx . struct_tail ( ptr. layout . ty ) . sty {
438
- ty:: Dynamic ( ..) => {
439
- match ptr. extra . unwrap ( ) . to_ptr ( ) {
440
- Ok ( _) => { } ,
441
- Err ( _) =>
442
- return validation_failure ! (
443
- "non-pointer vtable in fat pointer" , path
444
- ) ,
445
- }
446
- // FIXME: More checks for the vtable.
447
- }
448
- ty:: Slice ( ..) | ty:: Str => {
449
- match ptr. extra . unwrap ( ) . to_usize ( self ) {
450
- Ok ( _) => { } ,
451
- Err ( _) =>
452
- return validation_failure ! (
453
- "non-integer slice length in fat pointer" , path
454
- ) ,
455
- }
456
- }
457
- _ =>
458
- bug ! ( "Unexpected unsized type tail: {:?}" ,
459
- self . tcx. struct_tail( ptr. layout. ty)
460
- ) ,
461
- }
462
- // for safe ptrs, recursively check it
463
- if !dest. layout . ty . is_unsafe_ptr ( ) {
464
- let ptr = ptr. into ( ) ;
465
- if seen. insert ( ptr) {
466
- trace ! ( "Recursing below fat ptr {:?}" , ptr) ;
467
- todo. push ( ( ptr, path_clone_and_deref ( path) ) ) ;
468
- }
469
- }
470
- } else {
471
- // Not a pointer, perform regular aggregate handling below
472
- for i in 0 ..offsets. len ( ) {
473
- let field = self . operand_field ( dest, i as u64 ) ?;
474
- path. push ( self . aggregate_field_path_elem ( dest. layout . ty , variant, i) ) ;
475
- self . validate_operand ( field, path, seen, todo) ?;
476
- path. truncate ( path_len) ;
477
- }
508
+ for i in 0 ..offsets. len ( ) {
509
+ let field = self . operand_field ( dest, i as u64 ) ?;
510
+ path. push ( self . aggregate_field_path_elem ( dest. layout . ty , variant, i) ) ;
511
+ self . validate_operand ( field, path, ref_tracking. as_mut ( ) . map ( |r| & mut * * r) ) ?;
512
+ path. truncate ( path_len) ;
478
513
}
479
514
}
480
515
}
0 commit comments