Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 38 additions & 16 deletions src/borrow_tracker/tree_borrows/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,32 @@ pub struct Event {
pub span: Span,
}

/// Diagnostics data about the current access and the location we are accessing.
/// Used to create history events and errors.
#[derive(Clone, Debug)]
pub struct AccessDiagnostics {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of this name -- "diagnostic" is the term used in the Rust compiler for a fully computed warning/error output, and that's not what this is.

What about "DiagnosticInfo"?

pub alloc_id: AllocId,
pub span: Span,
/// The range the diagnostic actually applies to.
/// This is always a subset of `access_range`.
pub transition_range: Range<u64>,
/// The range the access is happening to. Is `None` if this is the protector release access
pub access_range: Option<AllocRange>,
pub access_cause: AccessCause,
}
impl AccessDiagnostics {
/// Creates a history event.
pub fn create_event(&self, transition: PermTransition, is_foreign: bool) -> Event {
Event {
transition,
is_foreign,
access_cause: self.access_cause,
access_range: self.access_range,
transition_range: self.transition_range.clone(),
span: self.span,
}
}
}
/// List of all events that affected a tag.
/// NOTE: not all of these events are relevant for a particular location,
/// the events should be filtered before the generation of diagnostics.
Expand Down Expand Up @@ -280,28 +306,25 @@ impl History {
pub(super) struct TbError<'node> {
/// What failure occurred.
pub error_kind: TransitionError,
/// The allocation in which the error is happening.
pub alloc_id: AllocId,
/// The offset (into the allocation) at which the conflict occurred.
pub error_offset: u64,
/// The tag on which the error was triggered.
/// On protector violations, this is the tag that was protected.
/// On accesses rejected due to insufficient permissions, this is the
/// tag that lacked those permissions.
pub conflicting_info: &'node NodeDebugInfo,
// What kind of access caused this error (read, write, reborrow, deallocation)
pub access_cause: AccessCause,
/// Which tag, if any, the access that caused this error was made through, i.e.
/// which tag was used to read/write/deallocate.
/// Not set on wildcard accesses.
pub accessed_info: Option<&'node NodeDebugInfo>,
/// Diagnostic data about the current access.
pub access_diagnostics: &'node AccessDiagnostics,
}

impl TbError<'_> {
/// Produce a UB error.
pub fn build<'tcx>(self) -> InterpErrorKind<'tcx> {
use TransitionError::*;
let cause = self.access_cause;
let cause = self.access_diagnostics.access_cause;
let error_offset = self.access_diagnostics.transition_range.start;
let accessed = self.accessed_info;
let accessed_str =
self.accessed_info.map(|v| format!("{v}")).unwrap_or_else(|| "<wildcard>".into());
Expand All @@ -316,9 +339,8 @@ impl TbError<'_> {
// all tags through which an access would cause UB.
let accessed_is_conflicting = accessed.map(|a| a.tag) == Some(conflicting.tag);
let title = format!(
"{cause} through {accessed_str} at {alloc_id:?}[{offset:#x}] is forbidden",
alloc_id = self.alloc_id,
offset = self.error_offset
"{cause} through {accessed_str} at {alloc_id:?}[{error_offset:#x}] is forbidden",
alloc_id = self.access_diagnostics.alloc_id
);
let (title, details, conflicting_tag_name) = match self.error_kind {
ChildAccessForbidden(perm) => {
Expand Down Expand Up @@ -368,7 +390,7 @@ impl TbError<'_> {
history.extend(accessed_info.history.forget(), "accessed", false);
}
history.extend(
self.conflicting_info.history.extract_relevant(self.error_offset, self.error_kind),
self.conflicting_info.history.extract_relevant(error_offset, self.error_kind),
conflicting_tag_name,
true,
);
Expand All @@ -379,12 +401,12 @@ impl TbError<'_> {
/// Cannot access this allocation with wildcard provenance, as there are no
/// valid exposed references for this access kind.
pub fn no_valid_exposed_references_error<'tcx>(
alloc_id: AllocId,
offset: u64,
access_cause: AccessCause,
AccessDiagnostics { alloc_id, transition_range, access_cause, .. }: &AccessDiagnostics,
) -> InterpErrorKind<'tcx> {
let title =
format!("{access_cause} through <wildcard> at {alloc_id:?}[{offset:#x}] is forbidden");
let title = format!(
"{access_cause} through <wildcard> at {alloc_id:?}[{offset:#x}] is forbidden",
offset = transition_range.start
);
let details = vec![format!("there are no exposed tags which may perform this access here")];
let history = HistoryData::default();
err_machine_stop!(TerminationInfo::TreeBorrowsUb { title, details, history })
Expand Down
116 changes: 42 additions & 74 deletions src/borrow_tracker/tree_borrows/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_span::Span;
use smallvec::SmallVec;

use super::diagnostics::AccessCause;
use super::diagnostics::{AccessCause, AccessDiagnostics};
use super::wildcard::WildcardState;
use crate::borrow_tracker::tree_borrows::Permission;
use crate::borrow_tracker::tree_borrows::diagnostics::{
Expand Down Expand Up @@ -91,12 +91,9 @@ impl LocationState {
nodes: &mut UniValMap<Node>,
wildcard_accesses: &mut UniValMap<WildcardState>,
access_kind: AccessKind,
access_cause: AccessCause, //diagnostics
access_range: Option<AllocRange>, //diagnostics
relatedness: AccessRelatedness,
span: Span, //diagnostics
location_range: Range<u64>, //diagnostics
protected: bool,
diagnostics: &AccessDiagnostics,
) -> Result<(), TransitionError> {
// Call this function now (i.e. only if we know `relatedness`), which
// ensures it is only called when `skip_if_known_noop` returns
Expand All @@ -107,14 +104,9 @@ impl LocationState {
if !transition.is_noop() {
let node = nodes.get_mut(idx).unwrap();
// Record the event as part of the history.
node.debug_info.history.push(diagnostics::Event {
transition,
is_foreign: relatedness.is_foreign(),
access_cause,
access_range,
transition_range: location_range,
span,
});
node.debug_info
.history
.push(diagnostics.create_event(transition, relatedness.is_foreign()));

// We need to update the wildcard state, if the permission
// of an exposed pointer changes.
Expand Down Expand Up @@ -842,6 +834,13 @@ impl<'tcx> Tree {
// 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) {
let diagnostics = AccessDiagnostics {
alloc_id,
span,
transition_range: loc_range,
access_range: Some(access_range),
access_cause: AccessCause::Dealloc,
};
// Checks the tree containing `idx` for strong protector violations.
// It does this in traversal order.
let mut check_tree = |idx| {
Expand All @@ -867,11 +866,9 @@ impl<'tcx> Tree {
&& 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,
access_diagnostics: &diagnostics,
conflicting_info: &node.debug_info,
accessed_info: start_idx
.map(|idx| &args.nodes.get(idx).unwrap().debug_info),
}
Expand Down Expand Up @@ -938,18 +935,21 @@ impl<'tcx> Tree {
// 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) {
let diagnostics = AccessDiagnostics {
alloc_id,
span,
access_cause,
access_range: Some(access_range),
transition_range: loc_range,
};
loc.perform_access(
self.roots.iter().copied(),
&mut self.nodes,
source_idx,
loc_range,
Some(access_range),
access_kind,
access_cause,
global,
alloc_id,
span,
ChildrenVisitMode::VisitChildrenOfAccessed,
&diagnostics,
)?;
}
} else {
Expand All @@ -973,19 +973,21 @@ impl<'tcx> Tree {
&& let Some(access_kind) = p.permission.protector_end_access()
&& p.accessed
{
let access_cause = diagnostics::AccessCause::FnExit(access_kind);
let diagnostics = AccessDiagnostics {
alloc_id,
span,
access_cause: AccessCause::FnExit(access_kind),
access_range: None,
transition_range: loc_range,
};
loc.perform_access(
self.roots.iter().copied(),
&mut self.nodes,
Some(source_idx),
loc_range,
None,
access_kind,
access_cause,
global,
alloc_id,
span,
ChildrenVisitMode::SkipChildrenOfAccessed,
&diagnostics,
)?;
}
}
Expand Down Expand Up @@ -1155,27 +1157,19 @@ impl<'tcx> LocationTree {
roots: impl Iterator<Item = UniIndex>,
nodes: &mut UniValMap<Node>,
access_source: Option<UniIndex>,
loc_range: Range<u64>, // diagnostics
access_range: Option<AllocRange>, // diagnostics
access_kind: AccessKind,
access_cause: diagnostics::AccessCause, // diagnostics
global: &GlobalState,
alloc_id: AllocId, // diagnostics
span: Span, // diagnostics
visit_children: ChildrenVisitMode,
diagnostics: &AccessDiagnostics,
) -> InterpResult<'tcx> {
let accessed_root = if let Some(idx) = access_source {
Some(self.perform_normal_access(
idx,
nodes,
loc_range.clone(),
access_range,
access_kind,
access_cause,
global,
alloc_id,
span,
visit_children,
diagnostics,
)?)
} else {
// `SkipChildrenOfAccessed` only gets set on protector release, which only
Expand Down Expand Up @@ -1209,13 +1203,9 @@ impl<'tcx> LocationTree {
access_source,
/*max_local_tag*/ accessed_root_tag,
nodes,
loc_range.clone(),
access_range,
access_kind,
access_cause,
global,
alloc_id,
span,
diagnostics,
)?;
}
interp_ok(())
Expand All @@ -1231,14 +1221,10 @@ impl<'tcx> LocationTree {
&mut self,
access_source: UniIndex,
nodes: &mut UniValMap<Node>,
loc_range: Range<u64>, // diagnostics
access_range: Option<AllocRange>, // diagnostics
access_kind: AccessKind,
access_cause: diagnostics::AccessCause, // diagnostics
global: &GlobalState,
alloc_id: AllocId, // diagnostics
span: Span, // diagnostics
visit_children: ChildrenVisitMode,
diagnostics: &AccessDiagnostics,
) -> InterpResult<'tcx, UniIndex> {
// Performs the per-node work:
// - insert the permission if it does not exist
Expand Down Expand Up @@ -1270,20 +1256,15 @@ impl<'tcx> LocationTree {
args.nodes,
&mut args.loc.wildcard_accesses,
access_kind,
access_cause,
access_range,
args.rel_pos,
span,
loc_range.clone(),
protected,
diagnostics,
)
.map_err(|error_kind| {
TbError {
conflicting_info: &args.nodes.get(args.idx).unwrap().debug_info,
access_cause,
alloc_id,
error_offset: loc_range.start,
error_kind,
access_diagnostics: diagnostics,
conflicting_info: &args.nodes.get(args.idx).unwrap().debug_info,
accessed_info: Some(&args.nodes.get(access_source).unwrap().debug_info),
}
.build()
Expand Down Expand Up @@ -1313,13 +1294,9 @@ impl<'tcx> LocationTree {
access_source: Option<UniIndex>,
max_local_tag: Option<BorTag>,
nodes: &mut UniValMap<Node>,
loc_range: Range<u64>, // diagnostics
access_range: Option<AllocRange>, // diagnostics
access_kind: AccessKind,
access_cause: diagnostics::AccessCause, // diagnostics
global: &GlobalState,
alloc_id: AllocId, // diagnostics
span: Span, // diagnostics
diagnostics: &AccessDiagnostics,
) -> InterpResult<'tcx> {
let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| {
let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default();
Expand Down Expand Up @@ -1373,11 +1350,7 @@ impl<'tcx> LocationTree {
// 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,
));
return Err(no_valid_exposed_references_error(diagnostics));
};

let Some(relatedness) = wildcard_relatedness.to_relatedness() else {
Expand All @@ -1396,21 +1369,16 @@ impl<'tcx> LocationTree {
args.nodes,
&mut args.loc.wildcard_accesses,
access_kind,
access_cause,
access_range,
relatedness,
span,
loc_range.clone(),
protected,
diagnostics,
)
.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,
access_diagnostics: diagnostics,
conflicting_info: &node.debug_info,
accessed_info: access_source
.map(|idx| &args.nodes.get(idx).unwrap().debug_info),
}
Expand Down