Skip to content

Commit a00de3d

Browse files
committed
now we do not need weak protectors any more
1 parent 4c9b6f4 commit a00de3d

21 files changed

+62
-105
lines changed

src/tools/miri/src/borrow_tracker/mod.rs

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub struct GlobalStateInner {
9595
/// We add tags to this when they are created with a protector in `reborrow`, and
9696
/// we remove tags from this when the call which is protecting them returns, in
9797
/// `GlobalStateInner::end_call`. See `Stack::item_popped` for more details.
98-
pub protected_tags: FxHashMap<BorTag, ProtectorKind>,
98+
pub protected_tags: FxHashSet<BorTag>,
9999
/// The pointer ids to trace
100100
pub tracked_pointer_tags: FxHashSet<BorTag>,
101101
/// The call ids to trace
@@ -142,26 +142,6 @@ pub enum RetagFields {
142142
OnlyScalar,
143143
}
144144

145-
/// The flavor of the protector.
146-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
147-
pub enum ProtectorKind {
148-
/// Protected against aliasing violations from other pointers.
149-
///
150-
/// Items protected like this cause UB when they are invalidated, *but* the pointer itself may
151-
/// still be used to issue a deallocation.
152-
///
153-
/// This is required for LLVM IR pointers that are `noalias` but *not* `dereferenceable`.
154-
WeakProtector,
155-
156-
/// Protected against any kind of invalidation.
157-
///
158-
/// Items protected like this cause UB when they are invalidated or the memory is deallocated.
159-
/// This is strictly stronger protection than `WeakProtector`.
160-
///
161-
/// This is required for LLVM IR pointers that are `dereferenceable` (and also allows `noalias`).
162-
StrongProtector,
163-
}
164-
165145
/// Utilities for initialization and ID generation
166146
impl GlobalStateInner {
167147
pub fn new(
@@ -175,7 +155,7 @@ impl GlobalStateInner {
175155
next_ptr_tag: BorTag::one(),
176156
base_ptr_tags: FxHashMap::default(),
177157
next_call_id: NonZeroU64::new(1).unwrap(),
178-
protected_tags: FxHashMap::default(),
158+
protected_tags: FxHashSet::default(),
179159
tracked_pointer_tags,
180160
tracked_call_ids,
181161
retag_fields,

src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use rustc_span::{Span, SpanData};
66
use rustc_target::abi::Size;
77

88
use crate::borrow_tracker::{
9-
stacked_borrows::{err_sb_ub, Permission},
10-
AccessKind, GlobalStateInner, ProtectorKind,
9+
stacked_borrows::{err_sb_ub, Permission}, AccessKind, GlobalStateInner,
1110
};
1211
use crate::*;
1312

@@ -398,11 +397,7 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
398397
}
399398

400399
#[inline(never)] // This is only called on fatal code paths
401-
pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpError<'tcx> {
402-
let protected = match kind {
403-
ProtectorKind::WeakProtector => "weakly protected",
404-
ProtectorKind::StrongProtector => "strongly protected",
405-
};
400+
pub(super) fn protector_error(&self, item: &Item) -> InterpError<'tcx> {
406401
let call_id = self
407402
.machine
408403
.threads
@@ -417,15 +412,15 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
417412
match self.operation {
418413
Operation::Dealloc(_) =>
419414
err_sb_ub(
420-
format!("deallocating while item {item:?} is {protected} by call {call_id:?}",),
415+
format!("deallocating while item {item:?} is protected by call {call_id:?}",),
421416
None,
422417
None,
423418
),
424419
Operation::Retag(RetagOp { orig_tag: tag, .. })
425420
| Operation::Access(AccessOp { tag, .. }) =>
426421
err_sb_ub(
427422
format!(
428-
"not granting access to tag {tag:?} because that would remove {item:?} which is {protected} because it is an argument of call {call_id:?}",
423+
"not granting access to tag {tag:?} because that would remove {item:?} which is protected because it is an argument of call {call_id:?}",
429424
),
430425
None,
431426
tag.and_then(|tag| self.get_logs_relevant_to(tag, Some(item.tag()))),

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 26 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_target::abi::{Abi, Size};
2020

2121
use crate::borrow_tracker::{
2222
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder, TagHistory},
23-
AccessKind, GlobalStateInner, ProtectorKind, RetagFields,
23+
AccessKind, GlobalStateInner, RetagFields,
2424
};
2525
use crate::*;
2626

@@ -51,12 +51,12 @@ enum NewPermission {
5151
/// If true, indicates that the stack should be cleared of all other tags.
5252
clear: bool,
5353
access: Option<AccessKind>,
54-
protector: Option<ProtectorKind>,
54+
protector: bool,
5555
},
5656
FreezeSensitive {
5757
freeze_perm: Permission,
5858
freeze_access: Option<AccessKind>,
59-
freeze_protector: Option<ProtectorKind>,
59+
freeze_protector: bool,
6060
nonfreeze_perm: Permission,
6161
nonfreeze_access: Option<AccessKind>,
6262
// nonfreeze_protector must always be None
@@ -71,25 +71,23 @@ impl NewPermission {
7171
kind: RetagKind,
7272
cx: &crate::MiriInterpCx<'_, 'tcx>,
7373
) -> Self {
74-
let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
7574
match ty.kind() {
7675
ty::Ref(_, pointee, Mutability::Mut) => {
7776
if kind == RetagKind::TwoPhase {
7877
// We mostly just give up on 2phase-borrows, and treat these exactly like raw pointers.
79-
assert!(protector.is_none()); // RetagKind can't be both FnEntry and TwoPhase.
8078
NewPermission::Uniform {
8179
perm: Permission::SharedReadWrite,
8280
clear: false,
8381
access: None,
84-
protector: None,
82+
protector: false,
8583
}
8684
} else if pointee.is_unpin(*cx.tcx, cx.param_env()) {
8785
// A regular full mutable reference.
8886
NewPermission::Uniform {
8987
perm: Permission::Unique,
9088
clear: false,
9189
access: Some(AccessKind::Write),
92-
protector,
90+
protector: kind == RetagKind::FnEntry,
9391
}
9492
} else {
9593
NewPermission::Uniform {
@@ -100,25 +98,24 @@ impl NewPermission {
10098
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
10199
// we don't do that.
102100
access: None,
103-
protector,
101+
protector: kind == RetagKind::FnEntry,
104102
}
105103
}
106104
}
107105
ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Mut, .. }) => {
108-
assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw.
109106
// Mutable raw pointer. No access, not protected.
110107
NewPermission::Uniform {
111108
perm: Permission::SharedReadWrite,
112109
clear: false,
113110
access: None,
114-
protector: None,
111+
protector: false,
115112
}
116113
}
117114
ty::Ref(_, _pointee, Mutability::Not) => {
118115
NewPermission::FreezeSensitive {
119116
freeze_perm: Permission::SharedReadOnly,
120117
freeze_access: Some(AccessKind::Read),
121-
freeze_protector: protector,
118+
freeze_protector: kind == RetagKind::FnEntry,
122119
nonfreeze_perm: Permission::SharedReadWrite,
123120
// Inside UnsafeCell, this does *not* count as an access, as there
124121
// might actually be mutable references further up the stack that
@@ -129,12 +126,11 @@ impl NewPermission {
129126
}
130127
}
131128
ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Not, .. }) => {
132-
assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw.
133129
// `*const T`, when freshly created, are read-only in the frozen part.
134130
NewPermission::FreezeSensitive {
135131
freeze_perm: Permission::SharedReadOnly,
136132
freeze_access: Some(AccessKind::Read),
137-
freeze_protector: None,
133+
freeze_protector: false,
138134
nonfreeze_perm: Permission::SharedReadWrite,
139135
nonfreeze_access: None,
140136
}
@@ -143,7 +139,7 @@ impl NewPermission {
143139
}
144140
}
145141

146-
fn protector(&self) -> Option<ProtectorKind> {
142+
fn protector(&self) -> bool {
147143
match self {
148144
NewPermission::Uniform { protector, .. } => *protector,
149145
NewPermission::FreezeSensitive { freeze_protector, .. } => *freeze_protector,
@@ -186,13 +182,6 @@ impl Permission {
186182
}
187183
}
188184

189-
/// Determines whether an item was invalidated by a conflicting access, or by deallocation.
190-
#[derive(Copy, Clone, Debug)]
191-
enum ItemInvalidationCause {
192-
Conflict,
193-
Dealloc,
194-
}
195-
196185
/// Core per-location operations: access, dealloc, reborrow.
197186
impl<'tcx> Stack {
198187
/// Find the first write-incompatible item above the given one --
@@ -228,7 +217,6 @@ impl<'tcx> Stack {
228217
item: &Item,
229218
global: &GlobalStateInner,
230219
dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
231-
cause: ItemInvalidationCause,
232220
) -> InterpResult<'tcx> {
233221
if !global.tracked_pointer_tags.is_empty() {
234222
dcx.check_tracked_tag_popped(item, global);
@@ -251,14 +239,8 @@ impl<'tcx> Stack {
251239
// 2. Most frames protect only one or two tags. So this duplicative global turns a search
252240
// which ends up about linear in the number of protected tags in the program into a
253241
// constant time check (and a slow linear, because the tags in the frames aren't contiguous).
254-
if let Some(&protector_kind) = global.protected_tags.get(&item.tag()) {
255-
// The only way this is okay is if the protector is weak and we are deallocating with
256-
// the right pointer.
257-
let allowed = matches!(cause, ItemInvalidationCause::Dealloc)
258-
&& matches!(protector_kind, ProtectorKind::WeakProtector);
259-
if !allowed {
260-
return Err(dcx.protector_error(item, protector_kind).into());
261-
}
242+
if global.protected_tags.contains(&item.tag()) {
243+
return Err(dcx.protector_error(item).into());
262244
}
263245
Ok(())
264246
}
@@ -298,7 +280,7 @@ impl<'tcx> Stack {
298280
0
299281
};
300282
self.pop_items_after(first_incompatible_idx, |item| {
301-
Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
283+
Stack::item_invalidated(&item, global, dcx)?;
302284
dcx.log_invalidation(item.tag());
303285
Ok(())
304286
})?;
@@ -319,7 +301,7 @@ impl<'tcx> Stack {
319301
0
320302
};
321303
self.disable_uniques_starting_at(first_incompatible_idx, |item| {
322-
Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
304+
Stack::item_invalidated(&item, global, dcx)?;
323305
dcx.log_invalidation(item.tag());
324306
Ok(())
325307
})?;
@@ -366,10 +348,10 @@ impl<'tcx> Stack {
366348
// As part of this we do regular protector checking, i.e. even weakly protected items cause UB when popped.
367349
self.access(AccessKind::Write, tag, global, dcx, exposed_tags)?;
368350

369-
// Step 2: Pretend we remove the remaining items, checking if any are strongly protected.
351+
// Step 2: Pretend we remove the remaining items, checking if any are protected.
370352
for idx in (0..self.len()).rev() {
371353
let item = self.get(idx).unwrap();
372-
Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Dealloc)?;
354+
Stack::item_invalidated(&item, global, dcx)?;
373355
}
374356

375357
Ok(())
@@ -404,7 +386,7 @@ impl<'tcx> Stack {
404386
// Remove all items, also checking their protectors.
405387
for idx in (0..self.len()).rev() {
406388
let item = self.get(idx).unwrap();
407-
Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
389+
Stack::item_invalidated(&item, global, dcx)?;
408390
}
409391
self.clear();
410392
}
@@ -669,7 +651,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
669651
);
670652
let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
671653
dcx.log_creation();
672-
if new_perm.protector().is_some() {
654+
if new_perm.protector() {
673655
dcx.log_protector();
674656
}
675657
},
@@ -727,7 +709,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
727709
size.bytes()
728710
);
729711

730-
if let Some(protect) = new_perm.protector() {
712+
if new_perm.protector() {
731713
// See comment in `Stack::item_invalidated` for why we store the tag twice.
732714
this.frame_mut().extra.borrow_tracker.as_mut().unwrap().protected_tags.push(new_tag);
733715
this.machine
@@ -736,7 +718,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
736718
.unwrap()
737719
.get_mut()
738720
.protected_tags
739-
.insert(new_tag, protect);
721+
.insert(new_tag);
740722
}
741723

742724
// Update the stacks, according to the new permission information we are given.
@@ -748,7 +730,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
748730
// mutable pointer, that seems reasonable.
749731
let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?;
750732
let stacked_borrows = alloc_extra.borrow_tracker_sb_mut().get_mut();
751-
let item = Item::new(new_tag, perm, protector.is_some());
733+
let item = Item::new(new_tag, perm, protector);
752734
let range = alloc_range(base_offset, size);
753735
let global = machine.borrow_tracker.as_ref().unwrap().borrow();
754736
let dcx = DiagnosticCxBuilder::retag(
@@ -790,9 +772,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
790772
let (perm, access, protector) = if frozen {
791773
(freeze_perm, freeze_access, freeze_protector)
792774
} else {
793-
(nonfreeze_perm, nonfreeze_access, None)
775+
(nonfreeze_perm, nonfreeze_access, /*protector*/ false)
794776
};
795-
let item = Item::new(new_tag, perm, protector.is_some());
777+
let item = Item::new(new_tag, perm, protector);
796778
let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
797779
let dcx = DiagnosticCxBuilder::retag(
798780
&this.machine,
@@ -937,14 +919,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
937919
// We cannot protect boxes since they might get invalidated if passed to an `id`
938920
// function (due to the return-position `noalias`). However, doing a `clear` on
939921
// `FnEntry` also has the effect that using any other pointer to access this memory
940-
// is *immediate* UB.
922+
// is *immediate* UB, thus ensuring `noalias` scoped for this function.
941923
// FIXME: should we `clear` on *all* `Box` retags? Currently (2022-12-27) this leads
942924
// to UB somewhere in thread-local dtor handling.
943925
let new_perm = NewPermission::Uniform {
944926
perm: Permission::Unique,
945927
clear: matches!(self.kind, RetagKind::FnEntry | RetagKind::FnReturn),
946928
access: Some(AccessKind::Write),
947-
protector: None,
929+
protector: false,
948930
};
949931
self.retag_ptr_inplace(place, new_perm, self.retag_cause)
950932
}
@@ -1021,7 +1003,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10211003
perm: Permission::Unique,
10221004
clear: false,
10231005
access: Some(AccessKind::Write),
1024-
protector: Some(ProtectorKind::StrongProtector),
1006+
protector: true,
10251007
};
10261008
let val = this.sb_retag_reference(&val, new_perm, RetagCause::FnReturnPlace)?;
10271009
// And use reborrowed pointer for return place.

src/tools/miri/tests/fail/stacked_borrows/aliasing_mut1.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
1+
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
22
--> $DIR/aliasing_mut1.rs:LL:CC
33
|
44
LL | pub fn safe(_x: &mut i32, _y: &mut i32) {}
5-
| ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
5+
| ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
66
|
77
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
88
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

src/tools/miri/tests/fail/stacked_borrows/aliasing_mut2.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
1+
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
22
--> $DIR/aliasing_mut2.rs:LL:CC
33
|
44
LL | pub fn safe(_x: &i32, _y: &mut i32) {}
5-
| ^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
5+
| ^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
66
|
77
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
88
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

src/tools/miri/tests/fail/stacked_borrows/aliasing_mut4.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
1+
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
22
--> $DIR/aliasing_mut4.rs:LL:CC
33
|
44
LL | pub fn safe(_x: &i32, _y: &mut Cell<i32>) {}
5-
| ^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
5+
| ^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
66
|
77
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
88
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@error-pattern: /deallocating while item \[Unique for .*\] is strongly protected/
1+
//@error-pattern: /deallocating while item \[Unique for .*\] is protected/
22
use std::alloc::{dealloc, Layout};
33

44
fn inner(x: &mut i32, f: fn(&mut i32)) {

0 commit comments

Comments
 (0)