@@ -12,7 +12,7 @@ use rustc_span::DUMMY_SP;
1212use rustc_target:: abi:: { Align , HasDataLayout , Size } ;
1313
1414use super :: {
15- read_target_uint, write_target_uint, AllocId , InterpError , InterpResult , Pointer ,
15+ read_target_uint, write_target_uint, AllocId , InterpError , InterpResult , Pointer , Provenance ,
1616 ResourceExhaustionInfo , Scalar , ScalarMaybeUninit , UndefinedBehaviorInfo , UninitBytesAccess ,
1717 UnsupportedOpInfo ,
1818} ;
@@ -53,18 +53,22 @@ pub struct Allocation<Tag = AllocId, Extra = ()> {
5353pub enum AllocError {
5454 /// Encountered a pointer where we needed raw bytes.
5555 ReadPointerAsBytes ,
56+ /// Partially overwriting a pointer.
57+ PartialPointerOverwrite ( Size ) ,
5658 /// Using uninitialized data where it is not allowed.
5759 InvalidUninitBytes ( Option < UninitBytesAccess > ) ,
5860}
5961pub type AllocResult < T = ( ) > = Result < T , AllocError > ;
6062
6163impl AllocError {
6264 pub fn to_interp_error < ' tcx > ( self , alloc_id : AllocId ) -> InterpError < ' tcx > {
65+ use AllocError :: * ;
6366 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 (
6872 UndefinedBehaviorInfo :: InvalidUninitBytes ( info. map ( |b| ( alloc_id, b) ) ) ,
6973 ) ,
7074 }
@@ -218,7 +222,7 @@ impl<Tag, Extra> Allocation<Tag, Extra> {
218222}
219223
220224/// Byte accessors.
221- impl < Tag : Copy , Extra > Allocation < Tag , Extra > {
225+ impl < Tag : Provenance , Extra > Allocation < Tag , Extra > {
222226 /// The last argument controls whether we error out when there are uninitialized
223227 /// or pointer bytes. You should never call this, call `get_bytes` or
224228 /// `get_bytes_with_uninit_and_ptr` instead,
@@ -275,30 +279,35 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
275279 /// It is the caller's responsibility to check bounds and alignment beforehand.
276280 /// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods
277281 /// 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 ] > {
279287 self . mark_init ( range, true ) ;
280- self . clear_relocations ( cx, range) ;
288+ self . clear_relocations ( cx, range) ? ;
281289
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 ( ) ] )
283291 }
284292
285293 /// 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 ] > {
287299 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) ?;
292301
293302 assert ! ( range. end( ) . bytes_usize( ) <= self . bytes. len( ) ) ; // need to do our own bounds-check
294303 let begin_ptr = self . bytes . as_mut_ptr ( ) . wrapping_add ( range. start . bytes_usize ( ) ) ;
295304 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) )
297306 }
298307}
299308
300309/// Reading and writing.
301- impl < Tag : Copy , Extra > Allocation < Tag , Extra > {
310+ impl < Tag : Provenance , Extra > Allocation < Tag , Extra > {
302311 /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a
303312 /// relocation. If `allow_uninit_and_ptr` is `false`, also enforces that the memory in the
304313 /// given range contains neither relocations nor uninitialized bytes.
@@ -395,7 +404,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
395404 } ;
396405
397406 let endian = cx. data_layout ( ) . endian ;
398- let dst = self . get_bytes_mut ( cx, range) ;
407+ let dst = self . get_bytes_mut ( cx, range) ? ;
399408 write_target_uint ( endian, dst, bytes) . unwrap ( ) ;
400409
401410 // See if we have to also write a relocation.
@@ -433,13 +442,16 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
433442 /// uninitialized. This is a somewhat odd "spooky action at a distance",
434443 /// but it allows strictly more code to run than if we would just error
435444 /// 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+ {
437449 // Find the start and end of the given range and its outermost relocations.
438450 let ( first, last) = {
439451 // Find all relocations overlapping the given range.
440452 let relocations = self . get_relocations ( cx, range) ;
441453 if relocations. is_empty ( ) {
442- return ;
454+ return Ok ( ( ) ) ;
443455 }
444456
445457 (
@@ -450,17 +462,27 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
450462 let start = range. start ;
451463 let end = range. end ( ) ;
452464
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 .
455467 if first < start {
468+ if Tag :: ERR_ON_PARTIAL_PTR_OVERWRITE {
469+ return Err ( AllocError :: PartialPointerOverwrite ( first) ) ;
470+ }
456471 self . init_mask . set_range ( first, start, false ) ;
457472 }
458473 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+ }
459479 self . init_mask . set_range ( end, last, false ) ;
460480 }
461481
462482 // Forget all the relocations.
463483 self . relocations . 0 . remove_range ( first..last) ;
484+
485+ Ok ( ( ) )
464486 }
465487
466488 /// Errors if there are relocations overlapping with the edges of the
0 commit comments