@@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Ty};
66use self :: foreign_access_skipping:: IdempotentForeignAccess ;
77use self :: tree:: LocationState ;
88use crate :: borrow_tracker:: { AccessKind , GlobalState , GlobalStateInner , ProtectorKind } ;
9- use crate :: concurrency:: data_race:: NaReadType ;
9+ use crate :: concurrency:: data_race:: { NaReadType , NaWriteType } ;
1010use crate :: * ;
1111
1212pub mod diagnostics;
@@ -109,13 +109,8 @@ impl<'tcx> Tree {
109109pub struct NewPermission {
110110 /// Permission for the frozen part of the range.
111111 freeze_perm : Permission ,
112- /// Whether a read access should be performed on the frozen part on a retag.
113- freeze_access : bool ,
114112 /// Permission for the non-frozen part of the range.
115113 nonfreeze_perm : Permission ,
116- /// Whether a read access should be performed on the non-frozen
117- /// part on a retag.
118- nonfreeze_access : bool ,
119114 /// Permission for memory outside the range.
120115 outside_perm : Permission ,
121116 /// Whether this pointer is part of the arguments of a function call.
@@ -138,36 +133,78 @@ impl<'tcx> NewPermission {
138133 let ty_is_freeze = pointee. is_freeze ( * cx. tcx , cx. typing_env ( ) ) ;
139134 let is_protected = retag_kind == RetagKind :: FnEntry ;
140135
136+ // Check if the implicit writes check has been enabled for this function using the `-Zmiri-tree-borrows-implicit-writes` flag
137+ let implicit_writes = cx
138+ . machine
139+ . borrow_tracker
140+ . as_ref ( )
141+ . unwrap ( )
142+ . borrow ( )
143+ . borrow_tracker_method
144+ . get_tree_borrows_params ( )
145+ . implicit_writes ;
146+
141147 if matches ! ( ref_mutability, Some ( Mutability :: Mut ) | None if !ty_is_unpin) {
142148 // Mutable reference / Box to pinning type: retagging is a NOP.
143149 // FIXME: with `UnsafePinned`, this should do proper per-byte tracking.
144150 return None ;
145151 }
146152
147- let freeze_perm = match ref_mutability {
148- // Shared references are frozen.
149- Some ( Mutability :: Not ) => Permission :: new_frozen ( ) ,
150- // Mutable references and Boxes are reserved.
151- _ => Permission :: new_reserved_frz ( ) ,
152- } ;
153- let nonfreeze_perm = match ref_mutability {
154- // Shared references are "transparent".
155- Some ( Mutability :: Not ) => Permission :: new_cell ( ) ,
156- // *Protected* mutable references and boxes are reserved without regarding for interior mutability.
157- _ if is_protected => Permission :: new_reserved_frz ( ) ,
158- // Unprotected mutable references and boxes start in `ReservedIm`.
159- _ => Permission :: new_reserved_im ( ) ,
153+ enum Part {
154+ InsideFrozen ,
155+ InsideUnsafeCell ,
156+ Outside ,
157+ }
158+ use Part :: * ;
159+
160+ let perm = |part : Part | {
161+ // Whether we should consider this byte to be frozen.
162+ // Outside bytes are frozen only if the entire type is frozen.
163+ let frozen = match part {
164+ InsideFrozen => true ,
165+ InsideUnsafeCell => false ,
166+ Outside => ty_is_freeze,
167+ } ;
168+ match ref_mutability {
169+ // Shared references
170+ Some ( Mutability :: Not ) =>
171+ if frozen {
172+ Permission :: new_frozen ( )
173+ } else {
174+ Permission :: new_cell ( )
175+ } ,
176+ // Mutable references
177+ Some ( Mutability :: Mut ) => {
178+ if is_protected && implicit_writes && !matches ! ( part, Outside ) {
179+ // We cannot use `Unique` for the outside part.
180+ Permission :: new_unique ( )
181+ } else if is_protected || frozen {
182+ // We also use this for protected `&mut UnsafeCell` as otherwise adding
183+ // `noalias` would not be sound.
184+ Permission :: new_reserved_frz ( )
185+ } else {
186+ Permission :: new_reserved_im ( )
187+ }
188+ }
189+ // Boxes
190+ None =>
191+ if is_protected && implicit_writes && !matches ! ( part, Outside ) {
192+ // Boxes are treated the same as mutable references.
193+ Permission :: new_unique ( )
194+ } else if is_protected || frozen {
195+ // We also use this for protected `Box<UnsafeCell>` as otherwise adding
196+ // `noalias` would not be sound.
197+ Permission :: new_reserved_frz ( )
198+ } else {
199+ Permission :: new_reserved_im ( )
200+ } ,
201+ }
160202 } ;
161203
162- // Everything except for `Cell` gets an initial access.
163- let initial_access = |perm : & Permission | !perm. is_cell ( ) ;
164-
165204 Some ( NewPermission {
166- freeze_perm,
167- freeze_access : initial_access ( & freeze_perm) ,
168- nonfreeze_perm,
169- nonfreeze_access : initial_access ( & nonfreeze_perm) ,
170- outside_perm : if ty_is_freeze { freeze_perm } else { nonfreeze_perm } ,
205+ freeze_perm : perm ( InsideFrozen ) ,
206+ nonfreeze_perm : perm ( InsideUnsafeCell ) ,
207+ outside_perm : perm ( Outside ) ,
171208 protector : is_protected. then_some ( if ref_mutability. is_some ( ) {
172209 // Strong protector for references
173210 ProtectorKind :: StrongProtector
@@ -288,13 +325,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
288325
289326 // Compute initial "inside" permissions.
290327 let loc_state = |frozen : bool | -> LocationState {
291- let ( perm, access) = if frozen {
292- ( new_perm. freeze_perm , new_perm. freeze_access )
293- } else {
294- ( new_perm. nonfreeze_perm , new_perm. nonfreeze_access )
295- } ;
328+ let perm = if frozen { new_perm. freeze_perm } else { new_perm. nonfreeze_perm } ;
296329 let sifa = perm. strongest_idempotent_foreign_access ( protected) ;
297- if access {
330+
331+ if perm. associated_access ( ) . is_some ( ) {
298332 LocationState :: new_accessed ( perm, sifa)
299333 } else {
300334 LocationState :: new_non_accessed ( perm, sifa)
@@ -303,11 +337,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
303337 let inside_perms = if !precise_interior_mut {
304338 // For `!Freeze` types, just pretend the entire thing is an `UnsafeCell`.
305339 let ty_is_freeze = place. layout . ty . is_freeze ( * this. tcx , this. typing_env ( ) ) ;
306- let state = loc_state ( ty_is_freeze) ;
307- DedupRangeMap :: new ( ptr_size, state)
340+ DedupRangeMap :: new ( ptr_size, loc_state ( ty_is_freeze) )
308341 } else {
309342 // The initial state will be overwritten by the visitor below.
310- let mut perms_map: DedupRangeMap < LocationState > = DedupRangeMap :: new (
343+ let mut perms_map = DedupRangeMap :: new (
311344 ptr_size,
312345 LocationState :: new_accessed (
313346 Permission :: new_disabled ( ) ,
@@ -327,9 +360,18 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
327360 let alloc_extra = this. get_alloc_extra ( alloc_id) ?;
328361 let mut tree_borrows = alloc_extra. borrow_tracker_tb ( ) . borrow_mut ( ) ;
329362
330- for ( perm_range, perm) in inside_perms. iter_all ( ) {
331- if perm. accessed ( ) {
332- // Some reborrows incur a read access to the parent.
363+ for ( perm_range, loc_state) in inside_perms. iter_all ( ) {
364+ if let Some ( access) = loc_state. permission ( ) . associated_access ( ) {
365+ // Some reborrows incur a read/write access to the parent.
366+ // As a write also implies a read, a single write is performed instead of a read and a write.
367+
368+ // writing to an immutable allocation (static variables) is UB, check this here
369+ if access == AccessKind :: Write
370+ && this. get_alloc_mutability ( alloc_id) . unwrap ( ) . is_not ( )
371+ {
372+ throw_ub ! ( WriteToReadOnly ( alloc_id) )
373+ }
374+
333375 // Adjust range to be relative to allocation start (rather than to `place`).
334376 let range_in_alloc = AllocRange {
335377 start : Size :: from_bytes ( perm_range. start ) + base_offset,
@@ -339,8 +381,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
339381 tree_borrows. perform_access (
340382 parent_prov,
341383 range_in_alloc,
342- AccessKind :: Read ,
343- diagnostics:: AccessCause :: Reborrow ,
384+ access ,
385+ diagnostics:: AccessCause :: Reborrow ( access ) ,
344386 this. machine . borrow_tracker . as_ref ( ) . unwrap ( ) ,
345387 alloc_id,
346388 this. machine . current_user_relevant_span ( ) ,
@@ -349,17 +391,29 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
349391 // Also inform the data race model (but only if any bytes are actually affected).
350392 if range_in_alloc. size . bytes ( ) > 0 {
351393 if let Some ( data_race) = alloc_extra. data_race . as_vclocks_ref ( ) {
352- data_race. read_non_atomic (
353- alloc_id,
354- range_in_alloc,
355- NaReadType :: Retag ,
356- Some ( place. layout . ty ) ,
357- & this. machine ,
358- ) ?
394+ match access {
395+ AccessKind :: Read =>
396+ data_race. read_non_atomic (
397+ alloc_id,
398+ range_in_alloc,
399+ NaReadType :: Retag ,
400+ Some ( place. layout . ty ) ,
401+ & this. machine ,
402+ ) ?,
403+ AccessKind :: Write =>
404+ data_race. write_non_atomic (
405+ alloc_id,
406+ range_in_alloc,
407+ NaWriteType :: Retag ,
408+ Some ( place. layout . ty ) ,
409+ & this. machine ,
410+ ) ?,
411+ } ;
359412 }
360413 }
361414 }
362415 }
416+
363417 // Record the parent-child pair in the tree.
364418 tree_borrows. new_child (
365419 base_offset,
@@ -370,7 +424,6 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
370424 protected,
371425 this. machine . current_user_relevant_span ( ) ,
372426 ) ?;
373- drop ( tree_borrows) ;
374427
375428 interp_ok ( Some ( new_prov) )
376429 }
@@ -550,9 +603,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
550603 // argument doesn't matter
551604 // (`ty_is_freeze || true` in `new_reserved` will always be `true`).
552605 freeze_perm : Permission :: new_reserved_frz ( ) ,
553- freeze_access : true ,
554606 nonfreeze_perm : Permission :: new_reserved_frz ( ) ,
555- nonfreeze_access : true ,
556607 outside_perm : Permission :: new_reserved_frz ( ) ,
557608 protector : Some ( ProtectorKind :: StrongProtector ) ,
558609 } ;
0 commit comments