@@ -12,7 +12,7 @@ use rustc_span::DUMMY_SP;
12
12
use rustc_target:: abi:: { Align , HasDataLayout , Size } ;
13
13
14
14
use super :: {
15
- read_target_uint, write_target_uint, AllocId , InterpError , InterpResult , Pointer ,
15
+ read_target_uint, write_target_uint, AllocId , InterpError , InterpResult , Pointer , Provenance ,
16
16
ResourceExhaustionInfo , Scalar , ScalarMaybeUninit , UndefinedBehaviorInfo , UninitBytesAccess ,
17
17
UnsupportedOpInfo ,
18
18
} ;
@@ -53,18 +53,22 @@ pub struct Allocation<Tag = AllocId, Extra = ()> {
53
53
pub enum AllocError {
54
54
/// Encountered a pointer where we needed raw bytes.
55
55
ReadPointerAsBytes ,
56
+ /// Partially overwriting a pointer.
57
+ PartialPointerOverwrite ( Size ) ,
56
58
/// Using uninitialized data where it is not allowed.
57
59
InvalidUninitBytes ( Option < UninitBytesAccess > ) ,
58
60
}
59
61
pub type AllocResult < T = ( ) > = Result < T , AllocError > ;
60
62
61
63
impl AllocError {
62
64
pub fn to_interp_error < ' tcx > ( self , alloc_id : AllocId ) -> InterpError < ' tcx > {
65
+ use AllocError :: * ;
63
66
match self {
64
- AllocError :: ReadPointerAsBytes => {
65
- InterpError :: Unsupported ( UnsupportedOpInfo :: ReadPointerAsBytes )
66
- }
67
- AllocError :: InvalidUninitBytes ( info) => InterpError :: UndefinedBehavior (
67
+ ReadPointerAsBytes => InterpError :: Unsupported ( UnsupportedOpInfo :: ReadPointerAsBytes ) ,
68
+ PartialPointerOverwrite ( offset) => InterpError :: Unsupported (
69
+ UnsupportedOpInfo :: PartialPointerOverwrite ( Pointer :: new ( alloc_id, offset) ) ,
70
+ ) ,
71
+ InvalidUninitBytes ( info) => InterpError :: UndefinedBehavior (
68
72
UndefinedBehaviorInfo :: InvalidUninitBytes ( info. map ( |b| ( alloc_id, b) ) ) ,
69
73
) ,
70
74
}
@@ -218,7 +222,7 @@ impl<Tag, Extra> Allocation<Tag, Extra> {
218
222
}
219
223
220
224
/// Byte accessors.
221
- impl < Tag : Copy , Extra > Allocation < Tag , Extra > {
225
+ impl < Tag : Provenance , Extra > Allocation < Tag , Extra > {
222
226
/// The last argument controls whether we error out when there are uninitialized
223
227
/// or pointer bytes. You should never call this, call `get_bytes` or
224
228
/// `get_bytes_with_uninit_and_ptr` instead,
@@ -275,30 +279,35 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
275
279
/// It is the caller's responsibility to check bounds and alignment beforehand.
276
280
/// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods
277
281
/// on `InterpCx` instead.
278
- pub fn get_bytes_mut ( & mut self , cx : & impl HasDataLayout , range : AllocRange ) -> & mut [ u8 ] {
282
+ pub fn get_bytes_mut (
283
+ & mut self ,
284
+ cx : & impl HasDataLayout ,
285
+ range : AllocRange ,
286
+ ) -> AllocResult < & mut [ u8 ] > {
279
287
self . mark_init ( range, true ) ;
280
- self . clear_relocations ( cx, range) ;
288
+ self . clear_relocations ( cx, range) ? ;
281
289
282
- & mut self . bytes [ range. start . bytes_usize ( ) ..range. end ( ) . bytes_usize ( ) ]
290
+ Ok ( & mut self . bytes [ range. start . bytes_usize ( ) ..range. end ( ) . bytes_usize ( ) ] )
283
291
}
284
292
285
293
/// A raw pointer variant of `get_bytes_mut` that avoids invalidating existing aliases into this memory.
286
- pub fn get_bytes_mut_ptr ( & mut self , cx : & impl HasDataLayout , range : AllocRange ) -> * mut [ u8 ] {
294
+ pub fn get_bytes_mut_ptr (
295
+ & mut self ,
296
+ cx : & impl HasDataLayout ,
297
+ range : AllocRange ,
298
+ ) -> AllocResult < * mut [ u8 ] > {
287
299
self . mark_init ( range, true ) ;
288
- // This also clears relocations that just overlap with the written range. So writing to some
289
- // byte can de-initialize its neighbors! See
290
- // <https://github.com/rust-lang/rust/issues/87184> for details.
291
- self . clear_relocations ( cx, range) ;
300
+ self . clear_relocations ( cx, range) ?;
292
301
293
302
assert ! ( range. end( ) . bytes_usize( ) <= self . bytes. len( ) ) ; // need to do our own bounds-check
294
303
let begin_ptr = self . bytes . as_mut_ptr ( ) . wrapping_add ( range. start . bytes_usize ( ) ) ;
295
304
let len = range. end ( ) . bytes_usize ( ) - range. start . bytes_usize ( ) ;
296
- ptr:: slice_from_raw_parts_mut ( begin_ptr, len)
305
+ Ok ( ptr:: slice_from_raw_parts_mut ( begin_ptr, len) )
297
306
}
298
307
}
299
308
300
309
/// Reading and writing.
301
- impl < Tag : Copy , Extra > Allocation < Tag , Extra > {
310
+ impl < Tag : Provenance , Extra > Allocation < Tag , Extra > {
302
311
/// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a
303
312
/// relocation. If `allow_uninit_and_ptr` is `false`, also enforces that the memory in the
304
313
/// given range contains neither relocations nor uninitialized bytes.
@@ -395,7 +404,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
395
404
} ;
396
405
397
406
let endian = cx. data_layout ( ) . endian ;
398
- let dst = self . get_bytes_mut ( cx, range) ;
407
+ let dst = self . get_bytes_mut ( cx, range) ? ;
399
408
write_target_uint ( endian, dst, bytes) . unwrap ( ) ;
400
409
401
410
// See if we have to also write a relocation.
@@ -433,13 +442,16 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
433
442
/// uninitialized. This is a somewhat odd "spooky action at a distance",
434
443
/// but it allows strictly more code to run than if we would just error
435
444
/// immediately in that case.
436
- fn clear_relocations ( & mut self , cx : & impl HasDataLayout , range : AllocRange ) {
445
+ fn clear_relocations ( & mut self , cx : & impl HasDataLayout , range : AllocRange ) -> AllocResult
446
+ where
447
+ Tag : Provenance ,
448
+ {
437
449
// Find the start and end of the given range and its outermost relocations.
438
450
let ( first, last) = {
439
451
// Find all relocations overlapping the given range.
440
452
let relocations = self . get_relocations ( cx, range) ;
441
453
if relocations. is_empty ( ) {
442
- return ;
454
+ return Ok ( ( ) ) ;
443
455
}
444
456
445
457
(
@@ -450,17 +462,27 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
450
462
let start = range. start ;
451
463
let end = range. end ( ) ;
452
464
453
- // Mark parts of the outermost relocations as uninitialized if they partially fall outside the
454
- // given range .
465
+ // We need to handle clearing the relocations from parts of a pointer. See
466
+ // <https://github.com/rust-lang/rust/issues/87184> for details .
455
467
if first < start {
468
+ if Tag :: ERR_ON_PARTIAL_PTR_OVERWRITE {
469
+ return Err ( AllocError :: PartialPointerOverwrite ( first) ) ;
470
+ }
456
471
self . init_mask . set_range ( first, start, false ) ;
457
472
}
458
473
if last > end {
474
+ if Tag :: ERR_ON_PARTIAL_PTR_OVERWRITE {
475
+ return Err ( AllocError :: PartialPointerOverwrite (
476
+ last - cx. data_layout ( ) . pointer_size ,
477
+ ) ) ;
478
+ }
459
479
self . init_mask . set_range ( end, last, false ) ;
460
480
}
461
481
462
482
// Forget all the relocations.
463
483
self . relocations . 0 . remove_range ( first..last) ;
484
+
485
+ Ok ( ( ) )
464
486
}
465
487
466
488
/// Errors if there are relocations overlapping with the edges of the
0 commit comments