Support retagging of wildcard references in tree borrows
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
index c454bb4..a91f35a 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
@@ -488,6 +488,8 @@ struct DisplayFmtPadding {
indent_middle: S,
/// Indentation for the last child.
indent_last: S,
+ /// Replaces `join_last` for a wildcard root.
+ wildcard_root: S,
}
/// How to show whether a location has been accessed
///
@@ -561,6 +563,11 @@ fn print_protector(&self, protector: Option<&ProtectorKind>) -> &'static str {
})
.unwrap_or("")
}
+
+ /// Print extra text if the tag is exposed.
+ fn print_exposed(&self, exposed: bool) -> S {
+ if exposed { " (exposed)" } else { "" }
+ }
}
/// Track the indentation of the tree.
@@ -607,23 +614,21 @@ fn char_repeat(c: char, n: usize) -> String {
struct DisplayRepr {
tag: BorTag,
name: Option<String>,
+ exposed: bool,
rperm: Vec<Option<LocationState>>,
children: Vec<DisplayRepr>,
}
impl DisplayRepr {
- fn from(tree: &Tree, show_unnamed: bool) -> Option<Self> {
+ fn from(tree: &Tree, root: UniIndex, show_unnamed: bool) -> Option<Self> {
let mut v = Vec::new();
- extraction_aux(tree, tree.root, show_unnamed, &mut v);
+ extraction_aux(tree, root, show_unnamed, &mut v);
let Some(root) = v.pop() else {
if show_unnamed {
unreachable!(
"This allocation contains no tags, not even a root. This should not happen."
);
}
- eprintln!(
- "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."
- );
return None;
};
assert!(v.is_empty());
@@ -637,6 +642,7 @@ fn extraction_aux(
) {
let node = tree.nodes.get(idx).unwrap();
let name = node.debug_info.name.clone();
+ let exposed = node.is_exposed;
let children_sorted = {
let mut children = node.children.iter().cloned().collect::<Vec<_>>();
children.sort_by_key(|idx| tree.nodes.get(*idx).unwrap().tag);
@@ -661,12 +667,13 @@ fn extraction_aux(
for child_idx in children_sorted {
extraction_aux(tree, child_idx, show_unnamed, &mut children);
}
- acc.push(DisplayRepr { tag: node.tag, name, rperm, children });
+ acc.push(DisplayRepr { tag: node.tag, name, rperm, children, exposed });
}
}
}
fn print(
- &self,
+ main_root: &Option<DisplayRepr>,
+ wildcard_subtrees: &[DisplayRepr],
fmt: &DisplayFmt,
indenter: &mut DisplayIndent,
protected_tags: &FxHashMap<BorTag, ProtectorKind>,
@@ -703,15 +710,41 @@ fn print(
block.push(s);
}
// This is the actual work
- print_aux(
- self,
- &range_padding,
- fmt,
- indenter,
- protected_tags,
- true, /* root _is_ the last child */
- &mut block,
- );
+ if let Some(root) = main_root {
+ print_aux(
+ root,
+ &range_padding,
+ fmt,
+ indenter,
+ protected_tags,
+ true, /* root _is_ the last child */
+ false, /* not a wildcard_root*/
+ &mut block,
+ );
+ }
+ for tree in wildcard_subtrees.iter() {
+ let mut gap_line = String::new();
+ gap_line.push_str(fmt.perm.open);
+ for (i, &pad) in range_padding.iter().enumerate() {
+ if i > 0 {
+ gap_line.push_str(fmt.perm.sep);
+ }
+ gap_line.push_str(&format!("{}{}", char_repeat(' ', pad), " "));
+ }
+ gap_line.push_str(fmt.perm.close);
+ block.push(gap_line);
+
+ print_aux(
+ tree,
+ &range_padding,
+ fmt,
+ indenter,
+ protected_tags,
+ true, /* root _is_ the last child */
+ true, /* wildcard_root*/
+ &mut block,
+ );
+ }
// Then it's just prettifying it with a border of dashes.
{
let wr = &fmt.wrapper;
@@ -741,6 +774,7 @@ fn print_aux(
indent: &mut DisplayIndent,
protected_tags: &FxHashMap<BorTag, ProtectorKind>,
is_last_child: bool,
+ is_wildcard_root: bool,
acc: &mut Vec<String>,
) {
let mut line = String::new();
@@ -760,7 +794,9 @@ fn print_aux(
indent.write(&mut line);
{
// padding
- line.push_str(if is_last_child {
+ line.push_str(if is_wildcard_root {
+ fmt.padding.wildcard_root
+ } else if is_last_child {
fmt.padding.join_last
} else {
fmt.padding.join_middle
@@ -777,12 +813,22 @@ fn print_aux(
line.push_str(&fmt.print_tag(tree.tag, &tree.name));
let protector = protected_tags.get(&tree.tag);
line.push_str(fmt.print_protector(protector));
+ line.push_str(fmt.print_exposed(tree.exposed));
// Push the line to the accumulator then recurse.
acc.push(line);
let nb_children = tree.children.len();
for (i, child) in tree.children.iter().enumerate() {
indent.increment(fmt, is_last_child);
- print_aux(child, padding, fmt, indent, protected_tags, i + 1 == nb_children, acc);
+ print_aux(
+ child,
+ padding,
+ fmt,
+ indent,
+ protected_tags,
+ /* is_last_child */ i + 1 == nb_children,
+ /* is_wildcard_root */ false,
+ acc,
+ );
indent.decrement(fmt);
}
}
@@ -803,6 +849,7 @@ fn print_aux(
indent_last: " ",
join_haschild: "┬",
join_default: "─",
+ wildcard_root: "*",
},
accessed: DisplayFmtAccess { yes: " ", no: "?", meh: "-" },
};
@@ -816,15 +863,27 @@ pub fn print_tree(
) -> InterpResult<'tcx> {
let mut indenter = DisplayIndent::new();
let ranges = self.locations.iter_all().map(|(range, _loc)| range).collect::<Vec<_>>();
- if let Some(repr) = DisplayRepr::from(self, show_unnamed) {
- repr.print(
- &DEFAULT_FORMATTER,
- &mut indenter,
- protected_tags,
- ranges,
- /* print warning message about tags not shown */ !show_unnamed,
+ let main_tree = DisplayRepr::from(self, self.roots[0], show_unnamed);
+ let wildcard_subtrees = self.roots[1..]
+ .iter()
+ .filter_map(|root| DisplayRepr::from(self, *root, show_unnamed))
+ .collect::<Vec<_>>();
+
+ if main_tree.is_none() && wildcard_subtrees.is_empty() {
+ eprintln!(
+ "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."
);
}
+
+ DisplayRepr::print(
+ &main_tree,
+ wildcard_subtrees.as_slice(),
+ &DEFAULT_FORMATTER,
+ &mut indenter,
+ protected_tags,
+ ranges,
+ /* print warning message about tags not shown */ !show_unnamed,
+ );
interp_ok(())
}
}
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
index 2a1c98e..e1da122 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
@@ -239,18 +239,14 @@ fn tb_reborrow(
return interp_ok(new_prov);
}
};
+ let new_prov = Provenance::Concrete { alloc_id, tag: new_tag };
log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
- let orig_tag = match parent_prov {
- ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle retagging wildcard pointers
- ProvenanceExtra::Concrete(tag) => tag,
- };
-
trace!(
"reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
new_tag,
- orig_tag,
+ parent_prov,
place.layout.ty,
interpret::Pointer::new(alloc_id, base_offset),
ptr_size.bytes()
@@ -281,7 +277,7 @@ fn tb_reborrow(
assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
// There's not actually any bytes here where accesses could even be tracked.
// Just produce the new provenance, nothing else to do.
- return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
+ return interp_ok(Some(new_prov));
}
let protected = new_perm.protector.is_some();
@@ -367,11 +363,10 @@ fn tb_reborrow(
}
}
}
-
// Record the parent-child pair in the tree.
tree_borrows.new_child(
base_offset,
- orig_tag,
+ parent_prov,
new_tag,
inside_perms,
new_perm.outside_perm,
@@ -380,7 +375,7 @@ fn tb_reborrow(
)?;
drop(tree_borrows);
- interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
+ interp_ok(Some(new_prov))
}
fn tb_retag_place(
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
index 07edf20..7fc9fa7 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -91,11 +91,11 @@ fn perform_transition(
nodes: &mut UniValMap<Node>,
wildcard_accesses: &mut UniValMap<WildcardState>,
access_kind: AccessKind,
- access_cause: AccessCause,
- access_range: Option<AllocRange>,
+ access_cause: AccessCause, //diagnostics
+ access_range: Option<AllocRange>, //diagnostics
relatedness: AccessRelatedness,
- span: Span,
- location_range: Range<u64>,
+ span: Span, //diagnostics
+ location_range: Range<u64>, //diagnostics
protected: bool,
) -> Result<(), TransitionError> {
// Call this function now (i.e. only if we know `relatedness`), which
@@ -294,8 +294,22 @@ pub struct Tree {
pub(super) nodes: UniValMap<Node>,
/// Associates with each location its state and wildcard access tracking.
pub(super) locations: DedupRangeMap<LocationTree>,
- /// The index of the root node.
- pub(super) root: UniIndex,
+ /// Contains both the root of the main tree as well as the roots of the wildcard subtrees.
+ ///
+ /// If we reborrow a reference which has wildcard provenance, then we do not know where in
+ /// the tree to attach them. Instead we create a new additional tree for this allocation
+ /// with this new reference as a root. We call this additional tree a wildcard subtree.
+ ///
+ /// The actual structure should be a single tree but with wildcard provenance we approximate
+ /// this with this ordered set of trees. Each wildcard subtree is the direct child of *some* exposed
+ /// tag (that is smaller than the root), but we do not know which. This also means that it can only be the
+ /// child of a tree that comes before it in the vec ensuring we don't have any cycles in our
+ /// approximated tree.
+ ///
+ /// Sorted according to `BorTag` from low to high. This also means the main root is `root[0]`.
+ ///
+ /// Has array size 2 because that still ensures the minimum size for SmallVec.
+ pub(super) roots: SmallVec<[UniIndex; 2]>,
}
/// A node in the borrow tree. Each node is uniquely identified by a tag via
@@ -345,12 +359,13 @@ struct TreeVisitor<'tree> {
}
/// Whether to continue exploring the children recursively or not.
+#[derive(Debug)]
enum ContinueTraversal {
Recurse,
SkipSelfAndChildren,
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
pub enum ChildrenVisitMode {
VisitChildrenOfAccessed,
SkipChildrenOfAccessed,
@@ -384,7 +399,7 @@ struct TreeVisitorStack<NodeContinue, NodeApp> {
impl<NodeContinue, NodeApp, Err> TreeVisitorStack<NodeContinue, NodeApp>
where
NodeContinue: Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
- NodeApp: Fn(NodeAppArgs<'_>) -> Result<(), Err>,
+ NodeApp: FnMut(NodeAppArgs<'_>) -> Result<(), Err>,
{
fn should_continue_at(
&self,
@@ -405,12 +420,13 @@ fn propagate_at(
(self.f_propagate)(NodeAppArgs { idx, rel_pos, nodes: this.nodes, loc: this.loc })
}
+ /// Returns the root of this tree.
fn go_upwards_from_accessed(
&mut self,
this: &mut TreeVisitor<'_>,
accessed_node: UniIndex,
visit_children: ChildrenVisitMode,
- ) -> Result<(), Err> {
+ ) -> Result<UniIndex, Err> {
// We want to visit the accessed node's children first.
// However, we will below walk up our parents and push their children (our cousins)
// onto the stack. To ensure correct iteration order, this method thus finishes
@@ -455,7 +471,7 @@ fn go_upwards_from_accessed(
}
// Reverse the stack, as discussed above.
self.stack.reverse();
- Ok(())
+ Ok(last_node)
}
fn finish_foreign_accesses(&mut self, this: &mut TreeVisitor<'_>) -> Result<(), Err> {
@@ -536,18 +552,20 @@ impl<'tree> TreeVisitor<'tree> {
/// Finally, remember that the iteration order is not relevant for UB, it only affects
/// diagnostics. It also affects tree traversal optimizations built on top of this, so
/// those need to be reviewed carefully as well whenever this changes.
+ ///
+ /// Returns the index of the root of the accessed tree.
fn traverse_this_parents_children_other<Err>(
mut self,
start_idx: UniIndex,
f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
- f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<(), Err>,
- ) -> Result<(), Err> {
+ f_propagate: impl FnMut(NodeAppArgs<'_>) -> Result<(), Err>,
+ ) -> Result<UniIndex, Err> {
let mut stack = TreeVisitorStack::new(f_continue, f_propagate);
// Visits the accessed node itself, and all its parents, i.e. all nodes
// undergoing a child access. Also pushes the children and the other
// cousin nodes (i.e. all nodes undergoing a foreign access) to the stack
// to be processed later.
- stack.go_upwards_from_accessed(
+ let root = stack.go_upwards_from_accessed(
&mut self,
start_idx,
ChildrenVisitMode::VisitChildrenOfAccessed,
@@ -555,21 +573,24 @@ fn traverse_this_parents_children_other<Err>(
// Now visit all the foreign nodes we remembered earlier.
// For this we go bottom-up, but also allow f_continue to skip entire
// subtrees from being visited if it would be a NOP.
- stack.finish_foreign_accesses(&mut self)
+ stack.finish_foreign_accesses(&mut self)?;
+ Ok(root)
}
/// Like `traverse_this_parents_children_other`, but skips the children of `start_idx`.
+ ///
+ /// Returns the index of the root of the accessed tree.
fn traverse_nonchildren<Err>(
mut self,
start_idx: UniIndex,
f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
- f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<(), Err>,
- ) -> Result<(), Err> {
+ f_propagate: impl FnMut(NodeAppArgs<'_>) -> Result<(), Err>,
+ ) -> Result<UniIndex, Err> {
let mut stack = TreeVisitorStack::new(f_continue, f_propagate);
// Visits the accessed node itself, and all its parents, i.e. all nodes
// undergoing a child access. Also pushes the other cousin nodes to the
// stack, but not the children of the accessed node.
- stack.go_upwards_from_accessed(
+ let root = stack.go_upwards_from_accessed(
&mut self,
start_idx,
ChildrenVisitMode::SkipChildrenOfAccessed,
@@ -577,6 +598,27 @@ fn traverse_nonchildren<Err>(
// Now visit all the foreign nodes we remembered earlier.
// For this we go bottom-up, but also allow f_continue to skip entire
// subtrees from being visited if it would be a NOP.
+ stack.finish_foreign_accesses(&mut self)?;
+ Ok(root)
+ }
+
+ /// Traverses all children of `start_idx` including `start_idx` itself.
+ /// Uses `f_continue` to filter out subtrees and then processes each node
+ /// with `f_propagate` so that the children get processed before their
+ /// parents.
+ fn traverse_children_this<Err>(
+ mut self,
+ start_idx: UniIndex,
+ f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
+ f_propagate: impl FnMut(NodeAppArgs<'_>) -> Result<(), Err>,
+ ) -> Result<(), Err> {
+ let mut stack = TreeVisitorStack::new(f_continue, f_propagate);
+
+ stack.stack.push((
+ start_idx,
+ AccessRelatedness::ForeignAccess,
+ RecursionState::BeforeChildren,
+ ));
stack.finish_foreign_accesses(&mut self)
}
}
@@ -625,7 +667,7 @@ pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self {
let wildcard_accesses = UniValMap::default();
DedupRangeMap::new(size, LocationTree { perms, wildcard_accesses })
};
- Self { root: root_idx, nodes, locations, tag_mapping }
+ Self { roots: SmallVec::from_slice(&[root_idx]), nodes, locations, tag_mapping }
}
}
@@ -639,7 +681,7 @@ impl<'tcx> Tree {
pub(super) fn new_child(
&mut self,
base_offset: Size,
- parent_tag: BorTag,
+ parent_prov: ProvenanceExtra,
new_tag: BorTag,
inside_perms: DedupRangeMap<LocationState>,
outside_perm: Permission,
@@ -647,7 +689,11 @@ pub(super) fn new_child(
span: Span,
) -> InterpResult<'tcx> {
let idx = self.tag_mapping.insert(new_tag);
- let parent_idx = self.tag_mapping.get(&parent_tag).unwrap();
+ let parent_idx = match parent_prov {
+ ProvenanceExtra::Concrete(parent_tag) =>
+ Some(self.tag_mapping.get(&parent_tag).unwrap()),
+ ProvenanceExtra::Wildcard => None,
+ };
assert!(outside_perm.is_initial());
let default_strongest_idempotent =
@@ -657,7 +703,7 @@ pub(super) fn new_child(
idx,
Node {
tag: new_tag,
- parent: Some(parent_idx),
+ parent: parent_idx,
children: SmallVec::default(),
default_initial_perm: outside_perm,
default_initial_idempotent_foreign_access: default_strongest_idempotent,
@@ -665,9 +711,17 @@ pub(super) fn new_child(
debug_info: NodeDebugInfo::new(new_tag, outside_perm, span),
},
);
- let parent_node = self.nodes.get_mut(parent_idx).unwrap();
- // Register new_tag as a child of parent_tag
- parent_node.children.push(idx);
+ if let Some(parent_idx) = parent_idx {
+ let parent_node = self.nodes.get_mut(parent_idx).unwrap();
+ // Register new_tag as a child of parent_tag
+ parent_node.children.push(idx);
+ } else {
+ // If the parent had wildcard provenance, then register the idx
+ // as a new wildcard root.
+ // This preserves the orderedness of `roots` because a newly created
+ // tag is greater than all previous tags.
+ self.roots.push(idx);
+ }
// We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`.
let mut min_sifa = default_strongest_idempotent;
@@ -691,19 +745,27 @@ pub(super) fn new_child(
// We need to ensure the consistency of the wildcard access tracking data structure.
// For this, we insert the correct entry for this tag based on its parent, if it exists.
+ // If we are inserting a new wildcard root (with Wildcard as parent_prov) then we insert
+ // the special wildcard root initial state instead.
for (_range, loc) in self.locations.iter_mut_all() {
- if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) {
- loc.wildcard_accesses.insert(idx, parent_access.for_new_child());
+ if let Some(parent_idx) = parent_idx {
+ if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) {
+ loc.wildcard_accesses.insert(idx, parent_access.for_new_child());
+ }
+ } else {
+ loc.wildcard_accesses.insert(idx, WildcardState::for_wildcard_root());
}
}
-
- // Inserting the new perms might have broken the SIFA invariant (see
- // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent.
- // We now weaken the recorded SIFA for our parents, until the invariant is restored. We
- // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new
- // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs
- // more fixup than `Write`).
- self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa);
+ // If the parent is a wildcard pointer, then it doesn't track SIFA and doesn't need to be updated.
+ if let Some(parent_idx) = parent_idx {
+ // Inserting the new perms might have broken the SIFA invariant (see
+ // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent.
+ // We now weaken the recorded SIFA for our parents, until the invariant is restored. We
+ // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new
+ // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs
+ // more fixup than `Write`).
+ self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa);
+ }
interp_ok(())
}
@@ -772,52 +834,67 @@ pub fn dealloc(
span,
)?;
- // The order in which we check if any nodes are invalidated only
- // matters to diagnostics, so we use the root as a default tag.
let start_idx = match prov {
- ProvenanceExtra::Concrete(tag) => self.tag_mapping.get(&tag).unwrap(),
- ProvenanceExtra::Wildcard => self.root,
+ ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()),
+ ProvenanceExtra::Wildcard => None,
};
// Check if this breaks any strong protector.
// (Weak protectors are already handled by `perform_access`.)
for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
- TreeVisitor { nodes: &mut self.nodes, loc }.traverse_this_parents_children_other(
- start_idx,
- // Visit all children, skipping none.
- |_| ContinueTraversal::Recurse,
- |args: NodeAppArgs<'_>| {
- let node = args.nodes.get(args.idx).unwrap();
- let perm = args.loc.perms.entry(args.idx);
+ // Checks the tree containing `idx` for strong protector violations.
+ // It does this in traversal order.
+ let mut check_tree = |idx| {
+ TreeVisitor { nodes: &mut self.nodes, loc }.traverse_this_parents_children_other(
+ idx,
+ // Visit all children, skipping none.
+ |_| ContinueTraversal::Recurse,
+ |args: NodeAppArgs<'_>| {
+ let node = args.nodes.get(args.idx).unwrap();
- let perm = perm.get().copied().unwrap_or_else(|| node.default_location_state());
- if global.borrow().protected_tags.get(&node.tag)
- == Some(&ProtectorKind::StrongProtector)
- // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`).
- // Related to https://github.com/rust-lang/rust/issues/55005.
- && !perm.permission.is_cell()
- // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579.
- && perm.accessed
- {
- Err(TbError {
- conflicting_info: &node.debug_info,
- access_cause: diagnostics::AccessCause::Dealloc,
- alloc_id,
- error_offset: loc_range.start,
- error_kind: TransitionError::ProtectedDealloc,
- accessed_info: match prov {
- ProvenanceExtra::Concrete(_) =>
- Some(&args.nodes.get(start_idx).unwrap().debug_info),
- // We don't know from where the access came during a wildcard access.
- ProvenanceExtra::Wildcard => None,
- },
+ let perm = args
+ .loc
+ .perms
+ .get(args.idx)
+ .copied()
+ .unwrap_or_else(|| node.default_location_state());
+ if global.borrow().protected_tags.get(&node.tag)
+ == Some(&ProtectorKind::StrongProtector)
+ // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`).
+ // Related to https://github.com/rust-lang/rust/issues/55005.
+ && !perm.permission.is_cell()
+ // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579.
+ && perm.accessed
+ {
+ Err(TbError {
+ conflicting_info: &node.debug_info,
+ access_cause: diagnostics::AccessCause::Dealloc,
+ alloc_id,
+ error_offset: loc_range.start,
+ error_kind: TransitionError::ProtectedDealloc,
+ accessed_info: start_idx
+ .map(|idx| &args.nodes.get(idx).unwrap().debug_info),
+ }
+ .build())
+ } else {
+ Ok(())
}
- .build())
- } else {
- Ok(())
- }
- },
- )?;
+ },
+ )
+ };
+ // If we have a start index we first check its subtree in traversal order.
+ // This results in us showing the error of the closest node instead of an
+ // arbitrary one.
+ let accessed_root = start_idx.map(&mut check_tree).transpose()?;
+ // Afterwards we check all other trees.
+ // We iterate over the list in reverse order to ensure that we do not visit
+ // a parent before its child.
+ for &root in self.roots.iter().rev() {
+ if Some(root) == accessed_root {
+ continue;
+ }
+ check_tree(root)?;
+ }
}
interp_ok(())
}
@@ -849,20 +926,20 @@ pub fn perform_access(
span: Span, // diagnostics
) -> InterpResult<'tcx> {
#[cfg(feature = "expensive-consistency-checks")]
- if matches!(prov, ProvenanceExtra::Wildcard) {
+ if self.roots.len() > 1 || matches!(prov, ProvenanceExtra::Wildcard) {
self.verify_wildcard_consistency(global);
}
+
let source_idx = match prov {
ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()),
ProvenanceExtra::Wildcard => None,
};
-
if let Some((access_range, access_kind, access_cause)) = access_range_and_kind {
// Default branch: this is a "normal" access through a known range.
// We iterate over affected locations and traverse the tree for each of them.
for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
loc.perform_access(
- self.root,
+ self.roots.iter().copied(),
&mut self.nodes,
source_idx,
loc_range,
@@ -898,7 +975,7 @@ pub fn perform_access(
{
let access_cause = diagnostics::AccessCause::FnExit(access_kind);
loc.perform_access(
- self.root,
+ self.roots.iter().copied(),
&mut self.nodes,
Some(source_idx),
loc_range,
@@ -920,7 +997,9 @@ pub fn perform_access(
/// Integration with the BorTag garbage collector
impl Tree {
pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) {
- self.remove_useless_children(self.root, live_tags);
+ for i in 0..(self.roots.len()) {
+ self.remove_useless_children(self.roots[i], live_tags);
+ }
// Right after the GC runs is a good moment to check if we can
// merge some adjacent ranges that were made equal by the removal of some
// tags (this does not necessarily mean that they have identical internal representations,
@@ -1073,20 +1152,20 @@ impl<'tcx> LocationTree {
/// * `visit_children`: Whether to skip updating the children of `access_source`.
fn perform_access(
&mut self,
- root: UniIndex,
+ roots: impl Iterator<Item = UniIndex>,
nodes: &mut UniValMap<Node>,
access_source: Option<UniIndex>,
- loc_range: Range<u64>,
- access_range: Option<AllocRange>,
+ loc_range: Range<u64>, // diagnostics
+ access_range: Option<AllocRange>, // diagnostics
access_kind: AccessKind,
- access_cause: diagnostics::AccessCause,
+ access_cause: diagnostics::AccessCause, // diagnostics
global: &GlobalState,
alloc_id: AllocId, // diagnostics
span: Span, // diagnostics
visit_children: ChildrenVisitMode,
) -> InterpResult<'tcx> {
- if let Some(idx) = access_source {
- self.perform_normal_access(
+ let accessed_root = if let Some(idx) = access_source {
+ Some(self.perform_normal_access(
idx,
nodes,
loc_range.clone(),
@@ -1097,13 +1176,38 @@ fn perform_access(
alloc_id,
span,
visit_children,
- )
+ )?)
} else {
- // `SkipChildrenOfAccessed` only gets set on protector release.
- // Since a wildcard reference are never protected this assert shouldn't fail.
+ // `SkipChildrenOfAccessed` only gets set on protector release, which only
+ // occurs on a known node.
assert!(matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed));
+ None
+ };
+
+ let accessed_root_tag = accessed_root.map(|idx| nodes.get(idx).unwrap().tag);
+ if matches!(visit_children, ChildrenVisitMode::SkipChildrenOfAccessed) {
+ // FIXME: approximate which roots could be children of the accessed node and only skip them instead of all other trees.
+ return interp_ok(());
+ }
+ for root in roots {
+ // We don't perform a wildcard access on the tree we already performed a
+ // normal access on.
+ if Some(root) == accessed_root {
+ continue;
+ }
+ // The choice of `max_local_tag` requires some thought.
+ // This can only be a local access for nodes that are a parent of the accessed node
+ // and are therefore smaller, so the accessed node itself is a valid choice for `max_local_tag`.
+ // However, using `accessed_root` is better since that will be smaller. It is still a valid choice
+ // because for nodes *in other trees*, if they are a parent of the accessed node then they
+ // are a parent of `accessed_root`.
+ //
+ // As a consequence of this, since the root of the main tree is the smallest tag in the entire
+ // allocation, if the access occurred in the main tree then other subtrees will only see foreign accesses.
self.perform_wildcard_access(
root,
+ access_source,
+ /*max_local_tag*/ accessed_root_tag,
nodes,
loc_range.clone(),
access_range,
@@ -1112,11 +1216,14 @@ fn perform_access(
global,
alloc_id,
span,
- )
+ )?;
}
+ interp_ok(())
}
/// Performs a normal access on the tree containing `access_source`.
+ ///
+ /// Returns the root index of this tree.
/// * `access_source`: The index of the tag being accessed.
/// * `visit_children`: Whether to skip the children of `access_source`
/// during the access. Used for protector end access.
@@ -1124,15 +1231,15 @@ fn perform_normal_access(
&mut self,
access_source: UniIndex,
nodes: &mut UniValMap<Node>,
- loc_range: Range<u64>,
- access_range: Option<AllocRange>,
+ loc_range: Range<u64>, // diagnostics
+ access_range: Option<AllocRange>, // diagnostics
access_kind: AccessKind,
- access_cause: diagnostics::AccessCause,
+ access_cause: diagnostics::AccessCause, // diagnostics
global: &GlobalState,
alloc_id: AllocId, // diagnostics
span: Span, // diagnostics
visit_children: ChildrenVisitMode,
- ) -> InterpResult<'tcx> {
+ ) -> InterpResult<'tcx, UniIndex> {
// Performs the per-node work:
// - insert the permission if it does not exist
// - perform the access
@@ -1141,7 +1248,7 @@ fn perform_normal_access(
// - skip the traversal of the children in some cases
// - do not record noop transitions
//
- // `perms_range` is only for diagnostics (it is the range of
+ // `loc_range` is only for diagnostics (it is the range of
// the `RangeMap` on which we are currently working).
let node_skipper = |args: &NodeAppArgs<'_>| -> ContinueTraversal {
let node = args.nodes.get(args.idx).unwrap();
@@ -1150,7 +1257,7 @@ fn perform_normal_access(
let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());
old_state.skip_if_known_noop(access_kind, args.rel_pos)
};
- let node_app = |args: NodeAppArgs<'_>| -> Result<(), _> {
+ let node_app = |args: NodeAppArgs<'_>| {
let node = args.nodes.get_mut(args.idx).unwrap();
let mut perm = args.loc.perms.entry(args.idx);
@@ -1164,7 +1271,7 @@ fn perform_normal_access(
&mut args.loc.wildcard_accesses,
access_kind,
access_cause,
- /* access_range */ access_range,
+ access_range,
args.rel_pos,
span,
loc_range.clone(),
@@ -1182,6 +1289,7 @@ fn perform_normal_access(
.build()
})
};
+
let visitor = TreeVisitor { nodes, loc: self };
match visit_children {
ChildrenVisitMode::VisitChildrenOfAccessed =>
@@ -1191,31 +1299,61 @@ fn perform_normal_access(
}
.into()
}
+
/// Performs a wildcard access on the tree with root `root`. Takes the `access_relatedness`
/// for each node from the `WildcardState` datastructure.
/// * `root`: Root of the tree being accessed.
+ /// * `access_source`: the index of the accessed tag, if any.
+ /// This is only used for printing the correct tag on errors.
+ /// * `max_local_tag`: The access can only be local for nodes whose tag is
+ /// at most `max_local_tag`.
fn perform_wildcard_access(
&mut self,
root: UniIndex,
+ access_source: Option<UniIndex>,
+ max_local_tag: Option<BorTag>,
nodes: &mut UniValMap<Node>,
- loc_range: Range<u64>,
- access_range: Option<AllocRange>,
+ loc_range: Range<u64>, // diagnostics
+ access_range: Option<AllocRange>, // diagnostics
access_kind: AccessKind,
- access_cause: diagnostics::AccessCause,
+ access_cause: diagnostics::AccessCause, // diagnostics
global: &GlobalState,
alloc_id: AllocId, // diagnostics
span: Span, // diagnostics
) -> InterpResult<'tcx> {
- let f_continue =
- |idx: UniIndex, nodes: &UniValMap<Node>, loc: &LocationTree| -> ContinueTraversal {
- let node = nodes.get(idx).unwrap();
- let perm = loc.perms.get(idx);
- let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default();
+ let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| {
+ let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default();
+ // If the tag is larger than `max_local_tag` then the access can only be foreign.
+ let only_foreign = max_local_tag.is_some_and(|max_local_tag| max_local_tag < node.tag);
+ wildcard_state.access_relatedness(access_kind, only_foreign)
+ };
+
+ // This does a traversal across the tree updating children before their parents. The
+ // difference to `perform_normal_access` is that we take the access relatedness from
+ // the wildcard tracking state of the node instead of from the visitor itself.
+ //
+ // Unlike for a normal access, the iteration order is important for improving the
+ // accuracy of wildcard accesses if `max_local_tag` is `Some`: processing the effects of this
+ // access further down the tree can cause exposed nodes to lose permissions, thus updating
+ // the wildcard data structure, which will be taken into account when processing the parent
+ // nodes. Also see the test `cross_tree_update_older_invalid_exposed2.rs`
+ // (Doing accesses in the opposite order cannot help with precision but the reasons are complicated;
+ // see <https://github.com/rust-lang/miri/pull/4707#discussion_r2581661123>.)
+ //
+ // Note, however, that this is an approximation: there can be situations where a node is
+ // marked as having an exposed foreign node, but actually that foreign node cannot be
+ // the source of the access due to `max_local_tag`. The wildcard tracking cannot know
+ // about `max_local_tag` so we will incorrectly assume that this might be a foreign access.
+ TreeVisitor { loc: self, nodes }.traverse_children_this(
+ root,
+ |args| -> ContinueTraversal {
+ let node = args.nodes.get(args.idx).unwrap();
+ let perm = args.loc.perms.get(args.idx);
let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());
// If we know where, relative to this node, the wildcard access occurs,
// then check if we can skip the entire subtree.
- if let Some(relatedness) = wildcard_state.access_relatedness(access_kind)
+ if let Some(relatedness) = get_relatedness(args.idx, node, args.loc)
&& let Some(relatedness) = relatedness.to_relatedness()
{
// We can use the usual SIFA machinery to skip nodes.
@@ -1223,78 +1361,64 @@ fn perform_wildcard_access(
} else {
ContinueTraversal::Recurse
}
- };
- // This does a traversal starting from the root through the tree updating
- // the permissions of each node.
- // The difference to `perform_access` is that we take the access
- // relatedness from the wildcard tracking state of the node instead of
- // from the visitor itself.
- TreeVisitor { loc: self, nodes }
- .traverse_this_parents_children_other(
- root,
- |args| f_continue(args.idx, args.nodes, args.loc),
- |args| {
- let node = args.nodes.get_mut(args.idx).unwrap();
- let mut entry = args.loc.perms.entry(args.idx);
- let perm = entry.or_insert(node.default_location_state());
+ },
+ |args| {
+ let node = args.nodes.get_mut(args.idx).unwrap();
- let protected = global.borrow().protected_tags.contains_key(&node.tag);
+ let protected = global.borrow().protected_tags.contains_key(&node.tag);
- let Some(wildcard_relatedness) = args
- .loc
- .wildcard_accesses
- .get(args.idx)
- .and_then(|s| s.access_relatedness(access_kind))
- else {
- // There doesn't exist a valid exposed reference for this access to
- // happen through.
- // If this fails for one id, then it fails for all ids so this.
- // Since we always check the root first, this means it should always
- // fail on the root.
- assert_eq!(root, args.idx);
- return Err(no_valid_exposed_references_error(
- alloc_id,
- loc_range.start,
- access_cause,
- ));
- };
-
- let Some(relatedness) = wildcard_relatedness.to_relatedness() else {
- // If the access type is Either, then we do not apply any transition
- // to this node, but we still update each of its children.
- // This is an imprecision! In the future, maybe we can still do some sort
- // of best-effort update here.
- return Ok(());
- };
- // We know the exact relatedness, so we can actually do precise checks.
- perm.perform_transition(
- args.idx,
- args.nodes,
- &mut args.loc.wildcard_accesses,
- access_kind,
+ let Some(wildcard_relatedness) = get_relatedness(args.idx, node, args.loc) else {
+ // There doesn't exist a valid exposed reference for this access to
+ // happen through.
+ // This can only happen if `root` is the main root: We set
+ // `max_foreign_access==Write` on all wildcard roots, so at least a foreign access
+ // is always possible on all nodes in a wildcard subtree.
+ return Err(no_valid_exposed_references_error(
+ alloc_id,
+ loc_range.start,
access_cause,
- access_range,
- relatedness,
- span,
- loc_range.clone(),
- protected,
- )
- .map_err(|trans| {
- let node = args.nodes.get(args.idx).unwrap();
- TbError {
- conflicting_info: &node.debug_info,
- access_cause,
- alloc_id,
- error_offset: loc_range.start,
- error_kind: trans,
- // We don't know from where the access came during a wildcard access.
- accessed_info: None,
- }
- .build()
- })
- },
- )
- .into()
+ ));
+ };
+
+ let Some(relatedness) = wildcard_relatedness.to_relatedness() else {
+ // If the access type is Either, then we do not apply any transition
+ // to this node, but we still update each of its children.
+ // This is an imprecision! In the future, maybe we can still do some sort
+ // of best-effort update here.
+ return Ok(());
+ };
+
+ let mut entry = args.loc.perms.entry(args.idx);
+ let perm = entry.or_insert(node.default_location_state());
+ // We know the exact relatedness, so we can actually do precise checks.
+ perm.perform_transition(
+ args.idx,
+ args.nodes,
+ &mut args.loc.wildcard_accesses,
+ access_kind,
+ access_cause,
+ access_range,
+ relatedness,
+ span,
+ loc_range.clone(),
+ protected,
+ )
+ .map_err(|trans| {
+ let node = args.nodes.get(args.idx).unwrap();
+ TbError {
+ conflicting_info: &node.debug_info,
+ access_cause,
+ alloc_id,
+ error_offset: loc_range.start,
+ error_kind: trans,
+ accessed_info: access_source
+ .map(|idx| &args.nodes.get(idx).unwrap().debug_info),
+ }
+ .build()
+ })
+ },
+ )?;
+ interp_ok(())
}
}
@@ -1309,10 +1433,11 @@ pub fn default_location_state(&self) -> LocationState {
impl VisitProvenance for Tree {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
- // To ensure that the root never gets removed, we visit it
- // (the `root` node of `Tree` is not an `Option<_>`)
- visit(None, Some(self.nodes.get(self.root).unwrap().tag));
-
+ // To ensure that the roots never get removed, we visit them.
+ // FIXME: it should be possible to GC wildcard tree roots.
+ for id in self.roots.iter().copied() {
+ visit(None, Some(self.nodes.get(id).unwrap().tag));
+ }
// We also need to keep around any exposed tags through which
// an access could still happen.
for (_id, node) in self.nodes.iter() {
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
index 56a85e6..3b55a9e 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
@@ -88,10 +88,26 @@ fn max_local_access(&self) -> WildcardAccessLevel {
}
/// From where relative to the node with this wildcard info a read or write access could happen.
- pub fn access_relatedness(&self, kind: AccessKind) -> Option<WildcardAccessRelatedness> {
- match kind {
+ /// If `only_foreign` is true then we treat `LocalAccess` as impossible. This means we return
+ /// `None` if only a `LocalAccess` is possible, and we treat `EitherAccess` as a
+ /// `ForeignAccess`.
+ pub fn access_relatedness(
+ &self,
+ kind: AccessKind,
+ only_foreign: bool,
+ ) -> Option<WildcardAccessRelatedness> {
+ let rel = match kind {
AccessKind::Read => self.read_access_relatedness(),
AccessKind::Write => self.write_access_relatedness(),
+ };
+ if only_foreign {
+ use WildcardAccessRelatedness as E;
+ match rel {
+ Some(E::EitherAccess | E::ForeignAccess) => Some(E::ForeignAccess),
+ Some(E::LocalAccess) | None => None,
+ }
+ } else {
+ rel
}
}
@@ -131,6 +147,15 @@ pub fn for_new_child(&self) -> Self {
..Default::default()
}
}
+ /// Crates the initial `WildcardState` for a wildcard root.
+ /// This has `max_foreign_access==Write` as it actually is the child of *some* exposed node
+ /// through which we can receive foreign accesses.
+ ///
+ /// This is different from the main root which has `max_foreign_access==None`, since there
+ /// cannot be a foreign access to the root of the allocation.
+ pub fn for_wildcard_root() -> Self {
+ Self { max_foreign_access: WildcardAccessLevel::Write, ..Default::default() }
+ }
/// Pushes the nodes of `children` onto the stack who's `max_foreign_access`
/// needs to be updated.
@@ -435,6 +460,10 @@ impl Tree {
/// Checks that the wildcard tracking data structure is internally consistent and
/// has the correct `exposed_as` values.
pub fn verify_wildcard_consistency(&self, global: &GlobalState) {
+ // We rely on the fact that `roots` is ordered according to tag from low to high.
+ assert!(self.roots.is_sorted_by_key(|idx| self.nodes.get(*idx).unwrap().tag));
+ let main_root_idx = self.roots[0];
+
let protected_tags = &global.borrow().protected_tags;
for (_, loc) in self.locations.iter_all() {
let wildcard_accesses = &loc.wildcard_accesses;
@@ -447,7 +476,8 @@ pub fn verify_wildcard_consistency(&self, global: &GlobalState) {
let state = wildcard_accesses.get(id).unwrap();
let expected_exposed_as = if node.is_exposed {
- let perm = perms.get(id).unwrap();
+ let perm =
+ perms.get(id).copied().unwrap_or_else(|| node.default_location_state());
perm.permission()
.strongest_allowed_child_access(protected_tags.contains_key(&node.tag))
@@ -477,7 +507,16 @@ pub fn verify_wildcard_consistency(&self, global: &GlobalState) {
.max(parent_state.max_foreign_access)
.max(parent_state.exposed_as)
} else {
- WildcardAccessLevel::None
+ if main_root_idx == id {
+ // There can never be a foreign access to the root of the allocation.
+ // So its foreign access level is always `None`.
+ WildcardAccessLevel::None
+ } else {
+ // For wildcard roots any access on a different subtree can be foreign
+ // to it. So a wildcard root has the maximum possible foreign access
+ // level.
+ WildcardAccessLevel::Write
+ }
};
// Count how many children can be the source of wildcard reads or writes
diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.rs
similarity index 70%
rename from src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs
rename to src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.rs
index 76516b7..8cd72b5 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.rs
@@ -1,4 +1,6 @@
+//@revisions: stack tree
//@compile-flags: -Zmiri-permissive-provenance
+//@[tree]compile-flags: -Zmiri-tree-borrows
fn main() {
unsafe {
@@ -12,6 +14,8 @@ fn main() {
// And we test that it has uniqueness by doing a conflicting write.
*exposed_ptr = 0;
// Stack: Unknown(<N)
- let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/
+ let _val = *root2;
+ //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/
+ //~[tree]| ERROR: /read access through .* is forbidden/
}
}
diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.stack.stderr
similarity index 81%
rename from src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.stderr
rename to src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.stack.stderr
index 065e739..dccbe6a 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.stack.stderr
@@ -1,5 +1,5 @@
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
- --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC
+ --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
|
LL | let _val = *root2;
| ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
@@ -7,12 +7,12 @@
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
- --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC
+ --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
|
LL | let root2 = &mut *exposed_ptr;
| ^^^^^^^^^^^^^^^^^
help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access
- --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC
+ --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
|
LL | *exposed_ptr = 0;
| ^^^^^^^^^^^^^^^^
diff --git a/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.tree.stderr
new file mode 100644
index 0000000..e6c9a14
--- /dev/null
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.tree.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
+ |
+LL | let _val = *root2;
+ | ^^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
+ |
+LL | let root2 = &mut *exposed_ptr;
+ | ^^^^^^^^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
+ |
+LL | *exposed_ptr = 0;
+ | ^^^^^^^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.rs
similarity index 65%
rename from src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs
rename to src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.rs
index 97e0bf4..9e8c940 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.rs
@@ -1,4 +1,6 @@
+//@revisions: stack tree
//@compile-flags: -Zmiri-permissive-provenance
+//@[tree]compile-flags: -Zmiri-tree-borrows
fn main() {
unsafe {
@@ -7,6 +9,8 @@ fn main() {
let exposed_ptr = addr as *mut i32;
// From the exposed ptr, we get a new unique ptr.
let root2 = &mut *exposed_ptr;
+ // Activate the reference (unnecessary on Stacked Borrows).
+ *root2 = 42;
// let _fool = root2 as *mut _; // this would fool us, since SRW(N+1) remains on the stack
// Stack: Unknown(<N), Unique(N)
// Stack if _fool existed: Unknown(<N), Unique(N), SRW(N+1)
@@ -15,6 +19,10 @@ fn main() {
// Stack: Unknown(<N), Disabled(N)
// collapsed to Unknown(<N)
// Stack if _fool existed: Unknown(<N), Disabled(N), SRW(N+1); collapsed to Unknown(<N+2) which would not cause an ERROR
- let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/
+
+ // Stack borrows would also fail if we replaced this with a read, but tree borrows would let it pass.
+ *root2 = 3;
+ //~[stack]^ ERROR: /write access .* tag does not exist in the borrow stack/
+ //~[tree]| ERROR: /write access through .* is forbidden/
}
}
diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.stack.stderr
similarity index 60%
rename from src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.stderr
rename to src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.stack.stderr
index 5643226..c598ff8 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.stack.stderr
@@ -1,18 +1,18 @@
-error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
- --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC
+error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
+ --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
|
-LL | let _val = *root2;
- | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
+LL | *root2 = 3;
+ | ^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
- --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC
+ --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
|
LL | let root2 = &mut *exposed_ptr;
| ^^^^^^^^^^^^^^^^^
help: <TAG> was later invalidated at offsets [0x0..0x4] by a read access
- --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC
+ --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
|
LL | let _val = *exposed_ptr;
| ^^^^^^^^^^^^
diff --git a/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.tree.stderr
new file mode 100644
index 0000000..17ae8bc
--- /dev/null
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.tree.stderr
@@ -0,0 +1,31 @@
+error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
+ |
+LL | *root2 = 3;
+ | ^^^^^^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Frozen which forbids this child write access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
+ |
+LL | let root2 = &mut *exposed_ptr;
+ | ^^^^^^^^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
+ --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
+ |
+LL | *root2 = 42;
+ | ^^^^^^^^^^^
+ = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
+help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4]
+ --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
+ |
+LL | let _val = *exposed_ptr;
+ | ^^^^^^^^^^^^
+ = help: this transition corresponds to a loss of write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs b/src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.rs
similarity index 69%
rename from src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs
rename to src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.rs
index 0e34c5c..a6c67f8 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.rs
@@ -1,4 +1,6 @@
+//@revisions: stack tree
//@compile-flags: -Zmiri-permissive-provenance
+//@[tree]compile-flags: -Zmiri-tree-borrows
fn main() {
unsafe {
@@ -12,6 +14,9 @@ fn main() {
// (The write is still fine, using the `root as *mut i32` provenance which got exposed.)
*exposed_ptr = 0;
// Stack: Unknown(<N)
- let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/
+
+ let _val = *root2;
+ //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/
+ //~[tree]| ERROR: /read access through .* is forbidden/
}
}
diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.stack.stderr
similarity index 81%
rename from src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.stderr
rename to src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.stack.stderr
index 5ed9ab6..7550337 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.stack.stderr
@@ -1,5 +1,5 @@
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
- --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC
+ --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
|
LL | let _val = *root2;
| ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
@@ -7,12 +7,12 @@
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4]
- --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC
+ --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
|
LL | let root2 = &*exposed_ptr;
| ^^^^^^^^^^^^^
help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access
- --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC
+ --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
|
LL | *exposed_ptr = 0;
| ^^^^^^^^^^^^^^^^
diff --git a/src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.tree.stderr
new file mode 100644
index 0000000..2f6f7eb
--- /dev/null
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.tree.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
+ |
+LL | let _val = *root2;
+ | ^^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Frozen
+ --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
+ |
+LL | let root2 = &*exposed_ptr;
+ | ^^^^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
+ |
+LL | *exposed_ptr = 0;
+ | ^^^^^^^^^^^^^^^^
+ = help: this transition corresponds to a loss of read permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs
new file mode 100644
index 0000000..4009907
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs
@@ -0,0 +1,36 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks how accesses from one subtree affect other subtrees.
+/// This test checks the case where the access is to the main tree.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ptr_base = &mut x as *mut u32;
+ let ref1 = unsafe { &mut *ptr_base };
+ let ref2 = unsafe { &mut *ptr_base };
+
+ let int1 = ref1 as *mut u32 as usize;
+ let wild = int1 as *mut u32;
+
+ let reb3 = unsafe { &mut *wild };
+
+ // ┌────────────┐
+ // │ │
+ // │ ptr_base ├───────────┐ *
+ // │ │ │ │
+ // └──────┬─────┘ │ │
+ // │ │ │
+ // │ │ │
+ // ▼ ▼ ▼
+ // ┌────────────┐ ┌───────────┐ ┌───────────┐
+ // │ │ │ │ │ │
+ // │ ref1(Res)* │ │ ref2(Res) │ │ reb3(Res) │
+ // │ │ │ │ │ │
+ // └────────────┘ └───────────┘ └───────────┘
+
+ // ref2 is part of the main tree and therefore foreign to all subtrees.
+ // Therefore, this disables reb3.
+ *ref2 = 13;
+
+ let _fail = *reb3; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.stderr
new file mode 100644
index 0000000..bdd7502
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC
+ |
+LL | let _fail = *reb3;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC
+ |
+LL | let reb3 = unsafe { &mut *wild };
+ | ^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC
+ |
+LL | *ref2 = 13;
+ | ^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs
new file mode 100644
index 0000000..a21dcfe
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs
@@ -0,0 +1,37 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks how accesses from one subtree affect other subtrees.
+/// This tests how main is effected by an access through a subtree.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ptr_base = &mut x as *mut u32;
+ let ref1 = unsafe { &mut *ptr_base };
+ let ref2 = unsafe { &mut *ptr_base };
+
+ let int1 = ref1 as *mut u32 as usize;
+ let wild = int1 as *mut u32;
+
+ let reb = unsafe { &mut *wild };
+
+ // ┌────────────┐
+ // │ │
+ // │ ptr_base ├───────────┐ *
+ // │ │ │ │
+ // └──────┬─────┘ │ │
+ // │ │ │
+ // │ │ │
+ // ▼ ▼ ▼
+ // ┌────────────┐ ┌───────────┐ ┌───────────┐
+ // │ │ │ │ │ │
+ // │ ref1(Res)* │ │ ref2(Res) │ │ reb(Res) │
+ // │ │ │ │ │ │
+ // └────────────┘ └───────────┘ └───────────┘
+
+ // Writes through the reborrowed reference causing a wildcard
+ // write on the main tree. This disables ref2 as it doesn't
+ // have any exposed children.
+ *reb = 13;
+
+ let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.stderr
new file mode 100644
index 0000000..285ffc4
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC
+ |
+LL | let _fail = *ref2;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC
+ |
+LL | let ref2 = unsafe { &mut *ptr_base };
+ | ^^^^^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC
+ |
+LL | *reb = 13;
+ | ^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.rs
new file mode 100644
index 0000000..641ffb4
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.rs
@@ -0,0 +1,44 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks how accesses from one subtree affect other subtrees.
+/// This test checks that an access from a subtree performs a
+/// wildcard access on all earlier trees, and that local
+/// accesses are treated as access errors for tags that are
+/// larger than the root of the accessed subtree.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ptr_base = &mut x as *mut u32;
+ let ref1 = unsafe { &mut *ptr_base };
+
+ // Activates ref1.
+ *ref1 = 4;
+
+ let int1 = ref1 as *mut u32 as usize;
+ let wild = int1 as *mut u32;
+
+ let ref2 = unsafe { &mut *wild };
+
+ // Freezes ref1.
+ let ref3 = unsafe { &mut *ptr_base };
+ let _int3 = ref3 as *mut u32 as usize;
+
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Act) ├───────────┐ *
+ // │ │ │ │
+ // └──────┬───────┘ │ │
+ // │ │ │
+ // │ │ │
+ // ▼ ▼ ▼
+ // ┌─────────────┐ ┌────────────┐ ┌───────────┐
+ // │ │ │ │ │ │
+ // │ ref1(Frz)* │ │ ref3(Res)* │ │ ref2(Res) │
+ // │ │ │ │ │ │
+ // └─────────────┘ └────────────┘ └───────────┘
+
+ // Performs a wildcard access on the main root. However, as there are
+ // no exposed tags with write permissions and a tag smaller than ref2
+ // this access fails.
+ *ref2 = 13; //~ ERROR: /write access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.stderr
new file mode 100644
index 0000000..3d91ec4
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.stderr
@@ -0,0 +1,14 @@
+error: Undefined Behavior: write access through <wildcard> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.rs:LL:CC
+ |
+LL | *ref2 = 13;
+ | ^^^^^^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: there are no exposed tags which may perform this access here
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs
new file mode 100644
index 0000000..a65508c
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs
@@ -0,0 +1,47 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks how accesses from one subtree affect other subtrees.
+/// This test checks that an access from an earlier created subtree
+/// is foreign to a later created one.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ref_base = &mut x;
+
+ let int0 = ref_base as *mut u32 as usize;
+ let wild = int0 as *mut u32;
+
+ let reb1 = unsafe { &mut *wild };
+
+ let reb2 = unsafe { &mut *wild };
+
+ let ref3 = &mut *reb1;
+ let _int3 = ref3 as *mut u32 as usize;
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Res)*│ * *
+ // │ │ │ │
+ // └──────────────┘ │ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ reb1(Res) ├ │ reb2(Res) ├
+ // │ │ │ │
+ // └──────┬─────┘ └────────────┘
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ ref3(Res)* │
+ // │ │
+ // └────────────┘
+
+ // This access disables reb2 because ref3 cannot be a child of it
+ // as reb2 both has a higher tag and doesn't have any exposed children.
+ *ref3 = 13;
+
+ let _fail = *reb2; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.stderr
new file mode 100644
index 0000000..c1ab12f
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC
+ |
+LL | let _fail = *reb2;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC
+ |
+LL | let reb2 = unsafe { &mut *wild };
+ | ^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC
+ |
+LL | *ref3 = 13;
+ | ^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs
new file mode 100644
index 0000000..666eac1
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs
@@ -0,0 +1,48 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks how accesses from one subtree affect other subtrees.
+/// This test checks that an access from an earlier created subtree
+/// is foreign to a later created one.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ref_base = &mut x;
+
+ let int0 = ref_base as *mut u32 as usize;
+ let wild = int0 as *mut u32;
+
+ let reb1 = unsafe { &mut *wild };
+
+ let reb2 = unsafe { &mut *wild };
+ let _int2 = reb2 as *mut u32 as usize;
+
+ let ref3 = &mut *reb1;
+ let _int3 = ref3 as *mut u32 as usize;
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Res)*│ * *
+ // │ │ │ │
+ // └──────────────┘ │ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ reb1(Res) │ │ reb2(Res)* │
+ // │ │ │ │
+ // └──────┬─────┘ └────────────┘
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ ref3(Res)* │
+ // │ │
+ // └────────────┘
+
+ // This access disables reb2 because ref3 cannot be a child of it
+ // as ref3's root has a lower tag than reb2.
+ *ref3 = 13;
+
+ let _fail = *reb2; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.stderr
new file mode 100644
index 0000000..e1232c7
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC
+ |
+LL | let _fail = *reb2;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC
+ |
+LL | let reb2 = unsafe { &mut *wild };
+ | ^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC
+ |
+LL | *ref3 = 13;
+ | ^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs
new file mode 100644
index 0000000..35fbc30
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs
@@ -0,0 +1,47 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks how accesses from one subtree affect other subtrees.
+/// This test checks that an access from a newer created subtree
+/// performs a wildcard access on all earlier trees.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ref_base = &mut x;
+
+ let int0 = ref_base as *mut u32 as usize;
+ let wild = int0 as *mut u32;
+
+ let reb1 = unsafe { &mut *wild };
+
+ let reb2 = unsafe { &mut *wild };
+
+ let ref3 = &mut *reb2;
+ let _int3 = ref3 as *mut u32 as usize;
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Res)*│ * *
+ // │ │ │ │
+ // └──────────────┘ │ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ reb1(Res) ├ │ reb2(Res) ├
+ // │ │ │ │
+ // └────────────┘ └──────┬─────┘
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ ref3(Res)* │
+ // │ │
+ // └────────────┘
+
+ // this access disables reb2 because ref3 cannot be a child of it
+ // as reb1 does not have any exposed children.
+ *ref3 = 13;
+
+ let _fail = *reb1; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.stderr
new file mode 100644
index 0000000..54f041b
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC
+ |
+LL | let _fail = *reb1;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC
+ |
+LL | let reb1 = unsafe { &mut *wild };
+ | ^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC
+ |
+LL | *ref3 = 13;
+ | ^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs
new file mode 100644
index 0000000..0f87bb4
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs
@@ -0,0 +1,53 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks how accesses from one subtree affect other subtrees.
+/// This test checks that an access from a newer created subtree
+/// performs a wildcard access on all earlier trees, and that
+/// either accesses are treated as foreign for tags that are
+/// larger than the root of the accessed subtree.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ref_base = &mut x;
+
+ let int0 = ref_base as *mut u32 as usize;
+ let wild = int0 as *mut u32;
+
+ let reb1 = unsafe { &mut *wild };
+
+ let reb2 = unsafe { &mut *wild };
+
+ let ref3 = &mut *reb1;
+ let _int3 = ref3 as *mut u32 as usize;
+
+ let ref4 = &mut *reb2;
+ let _int4 = ref4 as *mut u32 as usize;
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Res)*│ * *
+ // │ │ │ │
+ // └──────────────┘ │ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ reb1(Res) ├ │ reb2(Res) ├
+ // │ │ │ │
+ // └──────┬─────┘ └──────┬─────┘
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ ref3(Res)* │ │ ref4(Res)* │
+ // │ │ │ │
+ // └────────────┘ └────────────┘
+
+ // This access disables ref3 and reb1 because ref4 cannot be a child of it
+ // as reb2 has a smaller tag than ref3.
+ *ref4 = 13;
+
+ // Fails because ref3 is disabled.
+ let _fail = *ref3; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.stderr
new file mode 100644
index 0000000..66787ef
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC
+ |
+LL | let _fail = *ref3;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC
+ |
+LL | let ref3 = &mut *reb1;
+ | ^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC
+ |
+LL | *ref4 = 13;
+ | ^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs
new file mode 100644
index 0000000..f01ac26
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs
@@ -0,0 +1,59 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks how accesses from one subtree affect other subtrees.
+/// This test checks that an access from a newer created subtree
+/// performs a wildcard access on all earlier trees, and that
+/// either accesses are treated as foreign for tags that are
+/// larger than the root of the accessed subtree.
+/// This tests the special case where these updates get propagated
+/// up the tree.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ref_base = &mut x;
+
+ let int0 = ref_base as *mut u32 as usize;
+ let wild = int0 as *mut u32;
+
+ let reb1 = unsafe { &mut *wild };
+
+ let reb2 = unsafe { &mut *wild };
+
+ let ref3 = &mut *reb1;
+ let _int3 = ref3 as *mut u32 as usize;
+
+ let ref4 = &mut *reb2;
+ let _int4 = ref4 as *mut u32 as usize;
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Res)*│ * *
+ // │ │ │ │
+ // └──────────────┘ │ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ reb1(Res) ├ │ reb2(Res) ├
+ // │ │ │ │
+ // └──────┬─────┘ └──────┬─────┘
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ ref3(Res)* │ │ ref4(Res)* │
+ // │ │ │ │
+ // └────────────┘ └────────────┘
+
+ // This access disables ref3 and reb1 because ref4 cannot be a child of it
+ // as reb2 has a smaller tag than ref3.
+ //
+ // Because of the update order during a wildcard access (child before parent)
+ // ref3 gets disabled before we update reb1. So reb1 has no exposed children
+ // with write access at the time it gets updated so it also gets disabled.
+ *ref4 = 13;
+
+ // Fails because reb1 is disabled.
+ let _fail = *reb1; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.stderr
new file mode 100644
index 0000000..644d30c
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC
+ |
+LL | let _fail = *reb1;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC
+ |
+LL | let reb1 = unsafe { &mut *wild };
+ | ^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC
+ |
+LL | *ref4 = 13;
+ | ^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.rs
new file mode 100644
index 0000000..18fe931
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.rs
@@ -0,0 +1,39 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+/// Checks if we pass a reference derived from a wildcard pointer
+/// that it gets correctly protected.
+pub fn main() {
+ let mut x: u32 = 32;
+ let ref1 = &mut x;
+
+ let ref2 = &mut *ref1;
+ let int2 = ref2 as *mut u32 as usize;
+
+ let wild = int2 as *mut u32;
+ let wild_ref = unsafe { &mut *wild };
+
+ let mut protect = |_arg: &mut u32| {
+ // _arg is a protected pointer with wildcard parent.
+
+ // ┌────────────┐
+ // │ │
+ // │ ref1(Res) │ *
+ // │ │ │
+ // └──────┬─────┘ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ ref2(Res)* │ │ _arg(Res) │
+ // │ │ │ │
+ // └────────────┘ └────────────┘
+
+ // Writes to ref1, causing a foreign write to ref2 and _arg.
+ // Since _arg is protected this is UB.
+ *ref1 = 13; //~ ERROR: /write access through .* is forbidden/
+ };
+
+ // We pass a pointer with wildcard provenance to the function.
+ protect(wild_ref);
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.stderr
new file mode 100644
index 0000000..e257a35
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.stderr
@@ -0,0 +1,37 @@
+error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
+ |
+LL | *ref1 = 13;
+ | ^^^^^^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
+ = help: this foreign write access would cause the protected tag <TAG> (currently Reserved) to become Disabled
+ = help: protected tags must never be Disabled
+help: the accessed tag <TAG> was created here
+ --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
+ |
+LL | let mut protect = |_arg: &mut u32| {
+ | _______________________^
+... |
+LL | | *ref1 = 13;
+LL | | };
+ | |_____^
+help: the protected tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
+ |
+LL | let mut protect = |_arg: &mut u32| {
+ | ^^^^
+ = note: BACKTRACE (of the first span):
+ = note: inside closure at tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
+note: inside `main`
+ --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
+ |
+LL | protect(wild_ref);
+ | ^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.stderr
index de92cf9..6e115b2 100644
--- a/src/tools/miri/tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: deallocation through <wildcard> at ALLOC[0x0] is forbidden
+error: Undefined Behavior: deallocation through <TAG> at ALLOC[0x0] is forbidden
--> RUSTLIB/alloc/src/boxed.rs:LL:CC
|
LL | self.1.deallocate(From::from(ptr.cast()), layout);
@@ -6,8 +6,13 @@
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
- = help: the allocation of the accessed tag <wildcard> also contains the strongly protected tag <TAG>
+ = help: the allocation of the accessed tag <TAG> also contains the strongly protected tag <TAG>
= help: the strongly protected tag <TAG> disallows deallocations
+help: the accessed tag <TAG> was created here
+ --> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC
+ |
+LL | drop(unsafe { Box::from_raw(raw as *mut i32) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the strongly protected tag <TAG> was created here, in the initial state Reserved
--> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC
|
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs
new file mode 100644
index 0000000..7ce243a
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs
@@ -0,0 +1,44 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+// Checks if we correctly infer the relatedness of nodes that are
+// part of the same wildcard root.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ref_base = &mut x;
+
+ let int1 = ref_base as *mut u32 as usize;
+ let wild = int1 as *mut u32;
+
+ let reb = unsafe { &mut *wild };
+ let ptr_reb = reb as *mut u32;
+ let ref1 = unsafe { &mut *ptr_reb };
+ let ref2 = unsafe { &mut *ptr_reb };
+
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Res)*│ *
+ // │ │ │
+ // └──────────────┘ │
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ reb(Res) ├───────────┐
+ // │ │ │
+ // └──────┬─────┘ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌───────────┐
+ // │ │ │ │
+ // │ ref1(Res) │ │ ref2(Res) │
+ // │ │ │ │
+ // └────────────┘ └───────────┘
+
+ // ref1 is foreign to ref2, so this should disable ref2.
+ *ref1 = 13;
+
+ let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.stderr
new file mode 100644
index 0000000..4b4f73a
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC
+ |
+LL | let _fail = *ref2;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC
+ |
+LL | let ref2 = unsafe { &mut *ptr_reb };
+ | ^^^^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC
+ |
+LL | *ref1 = 13;
+ | ^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs
new file mode 100644
index 0000000..7b115f7
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs
@@ -0,0 +1,49 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+// Checks if we correctly infer the relatedness of nodes that are
+// part of the same wildcard root during a wildcard access.
+pub fn main() {
+ let mut x: u32 = 42;
+
+ let ref_base = &mut x;
+
+ let int = ref_base as *mut u32 as usize;
+ let wild = int as *mut u32;
+
+ let reb = unsafe { &mut *wild };
+ let ptr_reb = reb as *mut u32;
+ let ref1 = unsafe { &mut *ptr_reb };
+ let _int1 = ref1 as *mut u32 as usize;
+ let ref2 = unsafe { &mut *ptr_reb };
+
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Res)*│ *
+ // │ │ │
+ // └──────────────┘ │
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ reb(Res) ├───────────┐
+ // │ │ │
+ // └──────┬─────┘ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌───────────┐
+ // │ │ │ │
+ // │ ref1(Res)* │ │ ref2(Res) │
+ // │ │ │ │
+ // └────────────┘ └───────────┘
+
+ // Writes either through ref1 or ptr_base.
+ // This disables ref2 as the access is foreign to it in either case.
+ unsafe { *wild = 13 };
+
+ // This is fine because the earlier write could have come from ref1.
+ let _succ = *ref1;
+ // ref2 is disabled so this fails.
+ let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.stderr
new file mode 100644
index 0000000..e1a8beb
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
+ --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC
+ |
+LL | let _fail = *ref2;
+ | ^^^^^ Undefined Behavior occurred here
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+ = help: the accessed tag <TAG> has state Disabled which forbids this child read access
+help: the accessed tag <TAG> was created here, in the initial state Reserved
+ --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC
+ |
+LL | let ref2 = unsafe { &mut *ptr_reb };
+ | ^^^^^^^^^^^^^
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
+ --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC
+ |
+LL | unsafe { *wild = 13 };
+ | ^^^^^^^^^^
+ = help: this transition corresponds to a loss of read and write permissions
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/write_to_shr.stderr b/src/tools/miri/tests/fail/tree_borrows/write_to_shr.stderr
deleted file mode 100644
index f57b28c..0000000
--- a/src/tools/miri/tests/fail/tree_borrows/write_to_shr.stderr
+++ /dev/null
@@ -1,26 +0,0 @@
-error: Undefined Behavior: write access through <TAG> is forbidden
- --> $DIR/write_to_shr.rs:LL:CC
- |
-LL | *xmut = 31;
- | ^^^^^^^^^^ write access through <TAG> is forbidden
- |
- = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
- = help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
- = help: the conflicting tag <TAG> has state Frozen which forbids child write accesses
-help: the accessed tag <TAG> was created here
- --> $DIR/write_to_shr.rs:LL:CC
- |
-LL | let xmut = unsafe { &mut *(xref as *const u64 as *mut u64) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: the conflicting tag <TAG> was created here, in the initial state Frozen
- --> $DIR/write_to_shr.rs:LL:CC
- |
-LL | let xref = unsafe { &*(x as *mut u64) };
- | ^^^^^^^^^^^^^^^^^
- = note: BACKTRACE (of the first span):
- = note: inside `main` at $DIR/write_to_shr.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.rs b/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.rs
new file mode 100644
index 0000000..1f6b797
--- /dev/null
+++ b/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.rs
@@ -0,0 +1,36 @@
+// We disable the GC for this test because it would change what is printed.
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance -Zmiri-provenance-gc=0
+
+#[path = "../../../utils/mod.rs"]
+#[macro_use]
+mod utils;
+
+fn main() {
+ unsafe {
+ let x = &0u8;
+ name!(x);
+ let xa = &*x;
+ name!(xa);
+ let xb = &*x;
+ name!(xb);
+ let wild = xb as *const u8 as usize as *const u8;
+
+ let y = &*wild;
+ name!(y);
+ let ya = &*y;
+ name!(ya);
+ let yb = &*y;
+ name!(yb);
+ let _int = ya as *const u8 as usize;
+
+ let z = &*wild;
+ name!(z);
+
+ let u = &*wild;
+ name!(u);
+ let ua = &*u;
+ name!(ua);
+ let alloc_id = alloc_id!(x);
+ print_state!(alloc_id);
+ }
+}
diff --git a/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr b/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr
new file mode 100644
index 0000000..583c845
--- /dev/null
+++ b/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr
@@ -0,0 +1,17 @@
+──────────────────────────────────────────────────
+Warning: this tree is indicative only. Some tags may have been hidden.
+0.. 1
+| Act | └─┬──<TAG=root of the allocation>
+| Frz | └─┬──<TAG=x>
+| Frz | ├────<TAG=xa>
+| Frz | └────<TAG=xb> (exposed)
+| |
+| Frz | *─┬──<TAG=y>
+| Frz | ├────<TAG=ya> (exposed)
+| Frz | └────<TAG=yb>
+| |
+| Frz | *────<TAG=z>
+| |
+| Frz | *─┬──<TAG=u>
+| Frz | └────<TAG=ua>
+──────────────────────────────────────────────────
diff --git a/src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs b/src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs
new file mode 100644
index 0000000..fbb50d5
--- /dev/null
+++ b/src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs
@@ -0,0 +1,224 @@
+//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
+
+pub fn main() {
+ multiple_exposed_siblings1();
+ multiple_exposed_siblings2();
+ reborrow3();
+ returned_mut_is_usable();
+ only_foreign_is_temporary();
+}
+
+/// Checks that accessing through a reborrowed wildcard doesn't
+/// disable any exposed reference.
+fn multiple_exposed_siblings1() {
+ let mut x: u32 = 42;
+
+ let ptr_base = &mut x as *mut u32;
+
+ let ref1 = unsafe { &mut *ptr_base };
+ let int1 = ref1 as *mut u32 as usize;
+
+ let ref2 = unsafe { &mut *ptr_base };
+ let _int2 = ref2 as *mut u32 as usize;
+
+ let wild = int1 as *mut u32;
+
+ let reb = unsafe { &mut *wild };
+
+ // ┌────────────┐
+ // │ │
+ // │ ptr_base ├────────────┐ *
+ // │ │ │ │
+ // └──────┬─────┘ │ │
+ // │ │ │
+ // │ │ │
+ // ▼ ▼ ▼
+ // ┌────────────┐ ┌────────────┐ ┌────────────┐
+ // │ │ │ │ │ │
+ // │ ref1(Res)* │ │ ref2(Res)* │ │ reb(Res) │
+ // │ │ │ │ │ │
+ // └────────────┘ └────────────┘ └────────────┘
+
+ // Could either have as a parent ref1 or ref2.
+ // So we can't disable either of them.
+ *reb = 13;
+
+ // We can still access either ref1 or ref2.
+ // Although it is actually UB to access both of them.
+ assert_eq!(*ref2, 13);
+ assert_eq!(*ref1, 13);
+}
+
+/// Checks that wildcard accesses do not invalidate any exposed
+/// nodes through which the access could have happened.
+/// It checks this for the case where some reborrowed wildcard
+/// pointers are exposed as well.
+fn multiple_exposed_siblings2() {
+ let mut x: u32 = 42;
+
+ let ptr_base = &mut x as *mut u32;
+ let int = ptr_base as usize;
+
+ let wild = int as *mut u32;
+
+ let reb_ptr = unsafe { &mut *wild } as *mut u32;
+
+ let ref1 = unsafe { &mut *reb_ptr };
+ let _int1 = ref1 as *mut u32 as usize;
+
+ let ref2 = unsafe { &mut *reb_ptr };
+ let _int2 = ref2 as *mut u32 as usize;
+
+ // ┌────────────┐
+ // │ │
+ // │ ptr_base* │ *
+ // │ │ │
+ // └────────────┘ │
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ reb ├────────────┐
+ // │ │ │
+ // └──────┬─────┘ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ ref1(Res)* │ │ ref2(Res)* │
+ // │ │ │ │
+ // └────────────┘ └────────────┘
+
+ // Writes either through ref1, ref2 or ptr_base, which are all exposed.
+ // Since we don't know which we do not apply any transitions to any of
+ // the references.
+ unsafe { wild.write(13) };
+
+ // We should be able to access either ref1 or ref2.
+ // Although it is actually UB to access ref1 and ref2 together.
+ assert_eq!(*ref2, 13);
+ assert_eq!(*ref1, 13);
+}
+
+/// Checks that accessing a reborrowed wildcard reference doesn't
+/// invalidate other reborrowed wildcard references, if they
+/// are also exposed.
+fn reborrow3() {
+ let mut x: u32 = 42;
+
+ let ptr_base = &mut x as *mut u32;
+ let int = ptr_base as usize;
+
+ let wild = int as *mut u32;
+
+ let reb1 = unsafe { &mut *wild };
+ let ref2 = &mut *reb1;
+ let _int = ref2 as *mut u32 as usize;
+
+ let reb3 = unsafe { &mut *wild };
+
+ // ┌────────────┐
+ // │ │
+ // │ ptr_base* │ * *
+ // │ │ │ │
+ // └────────────┘ │ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ reb1(Res) | │ reb3(Res) |
+ // │ │ │ │
+ // └──────┬─────┘ └────────────┘
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ ref2(Res)* │
+ // │ │
+ // └────────────┘
+
+ // This is the only valid ordering these accesses can happen in.
+
+ // reb3 could be a child of ref2 so we don't disable ref2, reb1.
+ *reb3 = 1;
+ // Disables reb3 as it cannot be an ancestor of ref2.
+ *ref2 = 2;
+ // Disables ref2 (and reb3 if it wasn't already).
+ *reb1 = 3;
+}
+
+/// Analogous to same test in `../tree-borrows.rs` but with returning a
+/// reborrowed wildcard reference.
+fn returned_mut_is_usable() {
+ let mut x: u32 = 32;
+ let ref1 = &mut x;
+
+ let y = protect(ref1);
+
+ fn protect(arg: &mut u32) -> &mut u32 {
+ // Reborrow `arg` through a wildcard.
+ let int = arg as *mut u32 as usize;
+ let wild = int as *mut u32;
+ let ref2 = unsafe { &mut *wild };
+
+ // Activate the reference so that it is vulnerable to foreign reads.
+ *ref2 = 42;
+
+ ref2
+ // An implicit read through `arg` is inserted here.
+ }
+
+ *y = 4;
+}
+
+/// When accessing an allocation through a tag that was created from wildcard reference
+/// we treat nodes with a larger tag as if the access could only have been foreign to them.
+/// This change in access relatedness should not be visible in later accesses.
+fn only_foreign_is_temporary() {
+ let mut x = 0u32;
+ let wild = &mut x as *mut u32 as usize as *mut u32;
+
+ let reb1 = unsafe { &mut *wild };
+ let reb2 = unsafe { &mut *wild };
+ let ref3 = &mut *reb1;
+ let _int = ref3 as *mut u32 as usize;
+
+ let reb4 = unsafe { &mut *wild };
+
+ //
+ //
+ // * * *
+ // │ │ │
+ // │ │ │
+ // │ │ │
+ // │ │ │
+ // ▼ ▼ ▼
+ // ┌────────────┐ ┌────────────┐ ┌────────────┐
+ // │ │ │ │ │ │
+ // │ reb1(Res) │ │ reb2(Res) │ │ reb4(Res) │
+ // │ │ │ │ │ │
+ // └──────┬─────┘ └────────────┘ └────────────┘
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ ref3(Res)* │
+ // │ │
+ // └────────────┘
+
+ // Performs a foreign read on ref3 and doesn't update reb1.
+ // This temporarily treats ref3 as if only foreign accesses are possible to
+ // it. This is because the accessed tag reb2 has a larger tag than ref3.
+ let _x = *reb2;
+ // Should not update ref3, reb1 as we don't know if the access is local or foreign.
+ // This should stop treating ref3 as only foreign because the accessed tag reb4
+ // has a larger tag than ref3.
+ *reb4 = 32;
+ // The previous write could have been local to ref3, so this access should still work.
+ *ref3 = 4;
+}
diff --git a/src/tools/miri/tests/pass/tree_borrows/wildcard/undetected_ub.rs b/src/tools/miri/tests/pass/tree_borrows/wildcard/undetected_ub.rs
index c34fbcb..7339254 100644
--- a/src/tools/miri/tests/pass/tree_borrows/wildcard/undetected_ub.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/wildcard/undetected_ub.rs
@@ -4,7 +4,7 @@
pub fn main() {
uncertain_provenance();
protected_exposed();
- protected_wildcard();
+ cross_tree_update_older_invalid_exposed();
}
/// Currently, if we do not know for a tag if an access is local or foreign,
@@ -101,45 +101,61 @@ fn protect(ref3: &mut u32) {
}
protect(ref1);
- // ref2 is disabled, so this read causes UB, but we currently don't protect this.
+ // ref2 should be disabled, so this read causes UB, but we currently don't detect this.
let _fail = *ref2;
}
-/// Currently, we do not assign protectors to wildcard references.
-/// This test has UB because it does a foreign write to a protected reference.
-/// However, that reference is a wildcard, so this doesn't get detected.
-#[allow(unused_variables)]
-pub fn protected_wildcard() {
- let mut x: u32 = 32;
- let ref1 = &mut x;
- let ref2 = &mut *ref1;
+/// Checks how accesses from one subtree affect other subtrees.
+/// This test shows an example where we don't update a node whose exposed
+/// children are greater than `max_local_tag`.
+pub fn cross_tree_update_older_invalid_exposed() {
+ let mut x: [u32; 2] = [42, 43];
- let int = ref2 as *mut u32 as usize;
- let wild = int as *mut u32;
- let wild_ref = unsafe { &mut *wild };
+ let ref_base = &mut x;
- let mut protect = |arg: &mut u32| {
- // arg is a protected pointer with wildcard provenance.
+ let int0 = ref_base as *mut u32 as usize;
+ let wild = int0 as *mut u32;
- // ┌────────────┐
- // │ │
- // │ ref1(Res) │
- // │ │
- // └──────┬─────┘
- // │
- // │
- // ▼
- // ┌────────────┐
- // │ │
- // │ ref2(Res)* │
- // │ │
- // └────────────┘
+ let reb1 = unsafe { &mut *wild };
+ *reb1 = 44;
- // Writes to ref1, disabling ref2, i.e. disabling all exposed references.
- // Since a wildcard reference is protected, this is UB. But we currently don't detect this.
- *ref1 = 13;
- };
+ // We need this reference to be at a different location,
+ // so that creating it doesn't freeze reb1.
+ let reb2 = unsafe { &mut *wild.wrapping_add(1) };
+ let reb2_ptr = (reb2 as *mut u32).wrapping_sub(1);
- // We pass a pointer with wildcard provenance to the function.
- protect(wild_ref);
+ let ref3 = &mut *reb1;
+ let _int3 = ref3 as *mut u32 as usize;
+
+ // ┌──────────────┐
+ // │ │
+ // │ptr_base(Res)*│ * *
+ // │ │ │ │
+ // └──────────────┘ │ │
+ // │ │
+ // │ │
+ // ▼ ▼
+ // ┌────────────┐ ┌────────────┐
+ // │ │ │ │
+ // │ reb1(Act) │ │ reb2(Res) │
+ // │ │ │ │
+ // └──────┬─────┘ └────────────┘
+ // │
+ // │
+ // ▼
+ // ┌────────────┐
+ // │ │
+ // │ ref3(Res)* │
+ // │ │
+ // └────────────┘
+
+ // This access doesn't freeze reb1 even though no access could have come from its
+ // child ref3 (since ref3>reb2). This is because ref3 doesnt get disabled during this
+ // access.
+ //
+ // ref3 doesn't get frozen because it's still reserved.
+ let _y = unsafe { *reb2_ptr };
+
+ // reb1 should be frozen so a write should be UB. But we currently don't detect this.
+ *reb1 = 4;
}
diff --git a/src/tools/miri/tests/pass/tree_borrows/wildcard/wildcard.rs b/src/tools/miri/tests/pass/tree_borrows/wildcard/wildcard.rs
index c406bfe..0138531 100644
--- a/src/tools/miri/tests/pass/tree_borrows/wildcard/wildcard.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/wildcard/wildcard.rs
@@ -151,7 +151,6 @@ fn protector_conflicted_release() {
/// Analogous to same test in `../tree-borrows.rs` but with a protected wildcard reference.
fn returned_mut_is_usable() {
- // NOTE: Currently we ignore protectors on wildcard references.
fn reborrow(x: &mut u8) -> &mut u8 {
let y = &mut *x;
// Activate the reference so that it is vulnerable to foreign reads.
diff --git a/src/tools/miri/tests/utils/macros.rs b/src/tools/miri/tests/utils/macros.rs
index 3f5b9f7..25f40ed 100644
--- a/src/tools/miri/tests/utils/macros.rs
+++ b/src/tools/miri/tests/utils/macros.rs
@@ -9,7 +9,7 @@
/// The id obtained can be passed directly to `print_state!`.
macro_rules! alloc_id {
($ptr:expr) => {
- $crate::utils::miri_get_alloc_id($ptr as *const u8 as *const ())
+ $crate::utils::miri_get_alloc_id($ptr as *const _ as *const ())
};
}
@@ -52,6 +52,6 @@ macro_rules! name {
};
($ptr:expr => $nth_parent:expr, $name:expr) => {
let name = $name.as_bytes();
- $crate::utils::miri_pointer_name($ptr as *const u8 as *const (), $nth_parent, name);
+ $crate::utils::miri_pointer_name($ptr as *const _ as *const (), $nth_parent, name);
};
}