Print spans where tags are created and invalidated
This commit is contained in:
parent
cf5e75389d
commit
f3f7e083dc
@ -7,7 +7,8 @@ use log::trace;
|
|||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_span::{source_map::DUMMY_SP, Span, SpanData, Symbol};
|
use rustc_span::{source_map::DUMMY_SP, Span, SpanData, Symbol};
|
||||||
|
|
||||||
use crate::stacked_borrows::{AccessKind, SbTag};
|
use crate::helpers::HexRange;
|
||||||
|
use crate::stacked_borrows::{AccessKind, SbTag, TagHistory};
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Details of premature program termination.
|
/// Details of premature program termination.
|
||||||
@ -19,6 +20,7 @@ pub enum TerminationInfo {
|
|||||||
msg: String,
|
msg: String,
|
||||||
help: Option<String>,
|
help: Option<String>,
|
||||||
url: String,
|
url: String,
|
||||||
|
history: Option<TagHistory>,
|
||||||
},
|
},
|
||||||
Deadlock,
|
Deadlock,
|
||||||
MultipleSymbolDefinitions {
|
MultipleSymbolDefinitions {
|
||||||
@ -155,12 +157,46 @@ pub fn report_error<'tcx, 'mir>(
|
|||||||
(None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation;")),
|
(None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation;")),
|
||||||
(None, format!("or pass `-Zmiri-isolation-error=warn` to configure Miri to return an error code from isolated operations (if supported for that operation) and continue with a warning")),
|
(None, format!("or pass `-Zmiri-isolation-error=warn` to configure Miri to return an error code from isolated operations (if supported for that operation) and continue with a warning")),
|
||||||
],
|
],
|
||||||
ExperimentalUb { url, help, .. } => {
|
ExperimentalUb { url, help, history, .. } => {
|
||||||
msg.extend(help.clone());
|
msg.extend(help.clone());
|
||||||
vec![
|
let mut helps = vec![
|
||||||
(None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental")),
|
(None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental")),
|
||||||
(None, format!("see {} for further information", url))
|
(None, format!("see {} for further information", url)),
|
||||||
]
|
];
|
||||||
|
match history {
|
||||||
|
Some(TagHistory::Tagged {tag, created: (created_range, created_span), invalidated, protected }) => {
|
||||||
|
let msg = format!("{:?} was created due to a retag at offsets {}", tag, HexRange(*created_range));
|
||||||
|
helps.push((Some(created_span.clone()), msg));
|
||||||
|
if let Some((invalidated_range, invalidated_span)) = invalidated {
|
||||||
|
let msg = format!("{:?} was later invalidated due to a retag at offsets {}", tag, HexRange(*invalidated_range));
|
||||||
|
helps.push((Some(invalidated_span.clone()), msg));
|
||||||
|
}
|
||||||
|
if let Some((protecting_tag, protecting_tag_span, protection_span)) = protected {
|
||||||
|
helps.push((Some(protecting_tag_span.clone()), format!("{:?} was protected due to {:?} which was created here", tag, protecting_tag)));
|
||||||
|
helps.push((Some(protection_span.clone()), "this protector is live for this call".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(TagHistory::Untagged{ recently_created, recently_invalidated, matching_created, protected }) => {
|
||||||
|
if let Some((range, span)) = recently_created {
|
||||||
|
let msg = format!("tag was most recently created at offsets {}", HexRange(*range));
|
||||||
|
helps.push((Some(span.clone()), msg));
|
||||||
|
}
|
||||||
|
if let Some((range, span)) = recently_invalidated {
|
||||||
|
let msg = format!("tag was later invalidated at offsets {}", HexRange(*range));
|
||||||
|
helps.push((Some(span.clone()), msg));
|
||||||
|
}
|
||||||
|
if let Some((range, span)) = matching_created {
|
||||||
|
let msg = format!("this tag was also created here at offsets {}", HexRange(*range));
|
||||||
|
helps.push((Some(span.clone()), msg));
|
||||||
|
}
|
||||||
|
if let Some((protecting_tag, protecting_tag_span, protection_span)) = protected {
|
||||||
|
helps.push((Some(protecting_tag_span.clone()), format!("{:?} was protected due to a tag which was created here", protecting_tag)));
|
||||||
|
helps.push((Some(protection_span.clone()), "this protector is live for this call".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
helps
|
||||||
}
|
}
|
||||||
MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
|
MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
|
||||||
vec![
|
vec![
|
||||||
|
21
src/eval.rs
21
src/eval.rs
@ -283,6 +283,24 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
|||||||
Ok((ecx, ret_place))
|
Ok((ecx, ret_place))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is potentially a performance hazard.
|
||||||
|
// Factoring it into its own function lets us keep an eye on how much it shows up in a profile.
|
||||||
|
fn set_current_span<'mir, 'tcx: 'mir>(ecx: &mut MiriEvalContext<'mir, 'tcx>) {
|
||||||
|
let current_span = Machine::stack(&ecx)
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.find(|frame| {
|
||||||
|
let info =
|
||||||
|
FrameInfo { instance: frame.instance, span: frame.current_span(), lint_root: None };
|
||||||
|
ecx.machine.is_local(&info)
|
||||||
|
})
|
||||||
|
.map(|frame| frame.current_span())
|
||||||
|
.unwrap_or(rustc_span::DUMMY_SP);
|
||||||
|
if let Some(sb) = ecx.machine.stacked_borrows.as_mut() {
|
||||||
|
sb.get_mut().current_span = current_span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Evaluates the entry function specified by `entry_id`.
|
/// Evaluates the entry function specified by `entry_id`.
|
||||||
/// Returns `Some(return_code)` if program executed completed.
|
/// Returns `Some(return_code)` if program executed completed.
|
||||||
/// Returns `None` if an evaluation error occured.
|
/// Returns `None` if an evaluation error occured.
|
||||||
@ -310,6 +328,9 @@ pub fn eval_entry<'tcx>(
|
|||||||
let info = ecx.preprocess_diagnostics();
|
let info = ecx.preprocess_diagnostics();
|
||||||
match ecx.schedule()? {
|
match ecx.schedule()? {
|
||||||
SchedulingAction::ExecuteStep => {
|
SchedulingAction::ExecuteStep => {
|
||||||
|
if ecx.machine.stacked_borrows.is_some() {
|
||||||
|
set_current_span(&mut ecx);
|
||||||
|
}
|
||||||
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
|
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
|
||||||
}
|
}
|
||||||
SchedulingAction::ExecuteTimeoutCallback => {
|
SchedulingAction::ExecuteTimeoutCallback => {
|
||||||
|
@ -813,3 +813,12 @@ pub fn get_local_crates(tcx: &TyCtxt<'_>) -> Vec<CrateNum> {
|
|||||||
}
|
}
|
||||||
local_crates
|
local_crates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Formats an AllocRange like [0x1..0x3], for use in diagnostics.
|
||||||
|
pub struct HexRange(pub AllocRange);
|
||||||
|
|
||||||
|
impl std::fmt::Display for HexRange {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "[{:#x}..{:#x}]", self.0.start.bytes(), self.0.end().bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -632,7 +632,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
|
|||||||
alloc_id,
|
alloc_id,
|
||||||
tag,
|
tag,
|
||||||
range,
|
range,
|
||||||
machine.stacked_borrows.as_ref().unwrap(),
|
machine.stacked_borrows.as_ref().unwrap(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -655,7 +655,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
|
|||||||
alloc_id,
|
alloc_id,
|
||||||
tag,
|
tag,
|
||||||
range,
|
range,
|
||||||
machine.stacked_borrows.as_mut().unwrap(),
|
machine.stacked_borrows.as_ref().unwrap(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -681,7 +681,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
|
|||||||
alloc_id,
|
alloc_id,
|
||||||
tag,
|
tag,
|
||||||
range,
|
range,
|
||||||
machine.stacked_borrows.as_mut().unwrap(),
|
machine.stacked_borrows.as_ref().unwrap(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
@ -14,9 +15,11 @@ use rustc_middle::ty::{
|
|||||||
layout::{HasParamEnv, LayoutOf},
|
layout::{HasParamEnv, LayoutOf},
|
||||||
};
|
};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
use rustc_span::{Span, SpanData};
|
||||||
use rustc_target::abi::Size;
|
use rustc_target::abi::Size;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use crate::helpers::HexRange;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub type PtrId = NonZeroU64;
|
pub type PtrId = NonZeroU64;
|
||||||
@ -111,7 +114,53 @@ pub struct GlobalStateInner {
|
|||||||
tracked_call_ids: HashSet<CallId>,
|
tracked_call_ids: HashSet<CallId>,
|
||||||
/// Whether to track raw pointers.
|
/// Whether to track raw pointers.
|
||||||
tag_raw: bool,
|
tag_raw: bool,
|
||||||
|
/// Extra per-allocation information
|
||||||
|
extras: HashMap<AllocId, AllocHistory>,
|
||||||
|
/// Current span
|
||||||
|
pub(crate) current_span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct AllocHistory {
|
||||||
|
// The time tags can be compressed down to one bit per event, by just storing a Vec<u8>
|
||||||
|
// where each bit is set to indicate if the event was a creation or a retag
|
||||||
|
current_time: usize,
|
||||||
|
creations: Vec<Event>,
|
||||||
|
invalidations: Vec<Event>,
|
||||||
|
protectors: Vec<Protection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Protection {
|
||||||
|
orig_tag: SbTag,
|
||||||
|
tag: SbTag,
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Event {
|
||||||
|
time: usize,
|
||||||
|
parent: Option<SbTag>,
|
||||||
|
tag: SbTag,
|
||||||
|
range: AllocRange,
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TagHistory {
|
||||||
|
Tagged {
|
||||||
|
tag: SbTag,
|
||||||
|
created: (AllocRange, SpanData),
|
||||||
|
invalidated: Option<(AllocRange, SpanData)>,
|
||||||
|
protected: Option<(SbTag, SpanData, SpanData)>,
|
||||||
|
},
|
||||||
|
Untagged {
|
||||||
|
recently_created: Option<(AllocRange, SpanData)>,
|
||||||
|
recently_invalidated: Option<(AllocRange, SpanData)>,
|
||||||
|
matching_created: Option<(AllocRange, SpanData)>,
|
||||||
|
protected: Option<(SbTag, SpanData, SpanData)>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// We need interior mutable access to the global state.
|
/// We need interior mutable access to the global state.
|
||||||
pub type GlobalState = RefCell<GlobalStateInner>;
|
pub type GlobalState = RefCell<GlobalStateInner>;
|
||||||
|
|
||||||
@ -171,6 +220,8 @@ impl GlobalStateInner {
|
|||||||
tracked_pointer_tags,
|
tracked_pointer_tags,
|
||||||
tracked_call_ids,
|
tracked_call_ids,
|
||||||
tag_raw,
|
tag_raw,
|
||||||
|
extras: HashMap::new(),
|
||||||
|
current_span: DUMMY_SP,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,16 +269,155 @@ impl GlobalStateInner {
|
|||||||
self.base_ptr_ids.try_insert(id, tag).unwrap();
|
self.base_ptr_ids.try_insert(id, tag).unwrap();
|
||||||
tag
|
tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_creation(
|
||||||
|
&mut self,
|
||||||
|
parent: Option<SbTag>,
|
||||||
|
tag: SbTag,
|
||||||
|
alloc: AllocId,
|
||||||
|
range: AllocRange,
|
||||||
|
) {
|
||||||
|
let extras = self.extras.entry(alloc).or_default();
|
||||||
|
extras.creations.push(Event {
|
||||||
|
parent,
|
||||||
|
tag,
|
||||||
|
range,
|
||||||
|
span: self.current_span,
|
||||||
|
time: extras.current_time,
|
||||||
|
});
|
||||||
|
extras.current_time += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_invalidation(&mut self, tag: SbTag, alloc: AllocId, range: AllocRange) {
|
||||||
|
let extras = self.extras.entry(alloc).or_default();
|
||||||
|
extras.invalidations.push(Event {
|
||||||
|
parent: None,
|
||||||
|
tag,
|
||||||
|
range,
|
||||||
|
span: self.current_span,
|
||||||
|
time: extras.current_time,
|
||||||
|
});
|
||||||
|
extras.current_time += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_protector(&mut self, orig_tag: SbTag, tag: SbTag, alloc: AllocId) {
|
||||||
|
let extras = self.extras.entry(alloc).or_default();
|
||||||
|
extras.protectors.push(Protection { orig_tag, tag, span: self.current_span });
|
||||||
|
extras.current_time += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_stack_history(
|
||||||
|
&self,
|
||||||
|
tag: SbTag,
|
||||||
|
alloc: AllocId,
|
||||||
|
alloc_range: AllocRange,
|
||||||
|
offset: Size,
|
||||||
|
protector_tag: Option<SbTag>,
|
||||||
|
) -> Option<TagHistory> {
|
||||||
|
let extras = self.extras.get(&alloc)?;
|
||||||
|
let protected = protector_tag
|
||||||
|
.and_then(|protector| {
|
||||||
|
extras.protectors.iter().find_map(|protection| {
|
||||||
|
if protection.tag == protector {
|
||||||
|
Some((protection.orig_tag, protection.span.data()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.and_then(|(tag, call_span)| {
|
||||||
|
extras.creations.iter().rev().find_map(|event| {
|
||||||
|
if event.tag == tag {
|
||||||
|
Some((event.parent?, event.span.data(), call_span))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if let SbTag::Tagged(_) = tag {
|
||||||
|
let get_matching = |events: &[Event]| {
|
||||||
|
events.iter().rev().find_map(|event| {
|
||||||
|
if event.tag == tag { Some((event.range, event.span.data())) } else { None }
|
||||||
|
})
|
||||||
|
};
|
||||||
|
Some(TagHistory::Tagged {
|
||||||
|
tag,
|
||||||
|
created: get_matching(&extras.creations)?,
|
||||||
|
invalidated: get_matching(&extras.invalidations),
|
||||||
|
protected,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let mut created_time = 0;
|
||||||
|
// Find the most recently created tag that satsfies this offset
|
||||||
|
let recently_created = extras.creations.iter().rev().find_map(|event| {
|
||||||
|
if event.tag == tag && offset >= event.range.start && offset < event.range.end() {
|
||||||
|
created_time = event.time;
|
||||||
|
Some((event.range, event.span.data()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find a different recently created tag that satisfies this whole operation, predates
|
||||||
|
// the recently created tag, and has a different span.
|
||||||
|
// We're trying to make a guess at which span the user wanted to provide the tag that
|
||||||
|
// they're using.
|
||||||
|
let matching_created = if let Some((_created_range, created_span)) = recently_created {
|
||||||
|
extras.creations.iter().rev().find_map(|event| {
|
||||||
|
if event.tag == tag
|
||||||
|
&& alloc_range.start >= event.range.start
|
||||||
|
&& alloc_range.end() <= event.range.end()
|
||||||
|
&& event.span.data() != created_span
|
||||||
|
&& event.time != created_time
|
||||||
|
{
|
||||||
|
Some((event.range, event.span.data()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let recently_invalidated = if recently_created.is_some() {
|
||||||
|
// Find the most recent invalidation of this tag which post-dates the creation
|
||||||
|
let mut found = None;
|
||||||
|
for event in extras.invalidations.iter().rev() {
|
||||||
|
if event.time < created_time {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if event.tag == tag && offset >= event.range.start && offset < event.range.end()
|
||||||
|
{
|
||||||
|
found = Some((event.range, event.span.data()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Some(TagHistory::Untagged {
|
||||||
|
recently_created,
|
||||||
|
matching_created,
|
||||||
|
recently_invalidated,
|
||||||
|
protected,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error reporting
|
/// Error reporting
|
||||||
fn err_sb_ub(msg: String, help: Option<String>) -> InterpError<'static> {
|
fn err_sb_ub(
|
||||||
|
msg: String,
|
||||||
|
help: Option<String>,
|
||||||
|
history: Option<TagHistory>,
|
||||||
|
) -> InterpError<'static> {
|
||||||
err_machine_stop!(TerminationInfo::ExperimentalUb {
|
err_machine_stop!(TerminationInfo::ExperimentalUb {
|
||||||
msg,
|
msg,
|
||||||
help,
|
help,
|
||||||
url: format!(
|
url: format!(
|
||||||
"https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md"
|
"https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md"
|
||||||
),
|
),
|
||||||
|
history
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,31 +498,39 @@ impl<'tcx> Stack {
|
|||||||
/// `None` during a deallocation.
|
/// `None` during a deallocation.
|
||||||
fn check_protector(
|
fn check_protector(
|
||||||
item: &Item,
|
item: &Item,
|
||||||
provoking_access: Option<(SbTag, AccessKind)>,
|
provoking_access: Option<(SbTag, AllocId, AllocRange, Size)>, // just for debug printing amd error messages
|
||||||
global: &GlobalStateInner,
|
global: &GlobalStateInner,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
if let SbTag::Tagged(id) = item.tag {
|
if let SbTag::Tagged(id) = item.tag {
|
||||||
if global.tracked_pointer_tags.contains(&id) {
|
if global.tracked_pointer_tags.contains(&id) {
|
||||||
register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(
|
register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(
|
||||||
*item,
|
*item,
|
||||||
provoking_access,
|
None,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(call) = item.protector {
|
if let Some(call) = item.protector {
|
||||||
if global.is_active(call) {
|
if global.is_active(call) {
|
||||||
if let Some((tag, _)) = provoking_access {
|
if let Some((tag, alloc_id, alloc_range, offset)) = provoking_access {
|
||||||
Err(err_sb_ub(
|
Err(err_sb_ub(
|
||||||
format!(
|
format!(
|
||||||
"not granting access to tag {:?} because incompatible item is protected: {:?}",
|
"not granting access to tag {:?} because incompatible item is protected: {:?}",
|
||||||
tag, item
|
tag, item
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
|
global.get_stack_history(
|
||||||
|
tag,
|
||||||
|
alloc_id,
|
||||||
|
alloc_range,
|
||||||
|
offset,
|
||||||
|
Some(item.tag),
|
||||||
|
),
|
||||||
))?
|
))?
|
||||||
} else {
|
} else {
|
||||||
Err(err_sb_ub(
|
Err(err_sb_ub(
|
||||||
format!("deallocating while item is protected: {:?}", item),
|
format!("deallocating while item is protected: {:?}", item),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
))?
|
))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -348,15 +546,15 @@ impl<'tcx> Stack {
|
|||||||
&mut self,
|
&mut self,
|
||||||
access: AccessKind,
|
access: AccessKind,
|
||||||
tag: SbTag,
|
tag: SbTag,
|
||||||
(alloc_id, range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages
|
(alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages
|
||||||
global: &GlobalStateInner,
|
global: &mut GlobalStateInner,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
// Two main steps: Find granting item, remove incompatible items above.
|
// Two main steps: Find granting item, remove incompatible items above.
|
||||||
|
|
||||||
// Step 1: Find granting item.
|
// Step 1: Find granting item.
|
||||||
let granting_idx = self
|
let granting_idx = self.find_granting(access, tag).ok_or_else(|| {
|
||||||
.find_granting(access, tag)
|
self.access_error(access, tag, alloc_id, alloc_range, offset, global)
|
||||||
.ok_or_else(|| self.access_error(access, tag, alloc_id, range, offset))?;
|
})?;
|
||||||
|
|
||||||
// Step 2: Remove incompatible items above them. Make sure we do not remove protected
|
// Step 2: Remove incompatible items above them. Make sure we do not remove protected
|
||||||
// items. Behavior differs for reads and writes.
|
// items. Behavior differs for reads and writes.
|
||||||
@ -366,7 +564,8 @@ impl<'tcx> Stack {
|
|||||||
let first_incompatible_idx = self.find_first_write_incompatible(granting_idx);
|
let first_incompatible_idx = self.find_first_write_incompatible(granting_idx);
|
||||||
for item in self.borrows.drain(first_incompatible_idx..).rev() {
|
for item in self.borrows.drain(first_incompatible_idx..).rev() {
|
||||||
trace!("access: popping item {:?}", item);
|
trace!("access: popping item {:?}", item);
|
||||||
Stack::check_protector(&item, Some((tag, access)), global)?;
|
Stack::check_protector(&item, Some((tag, alloc_id, alloc_range, offset)), global)?;
|
||||||
|
global.add_invalidation(item.tag, alloc_id, alloc_range);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses.
|
// On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses.
|
||||||
@ -381,8 +580,13 @@ impl<'tcx> Stack {
|
|||||||
let item = &mut self.borrows[idx];
|
let item = &mut self.borrows[idx];
|
||||||
if item.perm == Permission::Unique {
|
if item.perm == Permission::Unique {
|
||||||
trace!("access: disabling item {:?}", item);
|
trace!("access: disabling item {:?}", item);
|
||||||
Stack::check_protector(item, Some((tag, access)), global)?;
|
Stack::check_protector(
|
||||||
|
item,
|
||||||
|
Some((tag, alloc_id, alloc_range, offset)),
|
||||||
|
global,
|
||||||
|
)?;
|
||||||
item.perm = Permission::Disabled;
|
item.perm = Permission::Disabled;
|
||||||
|
global.add_invalidation(item.tag, alloc_id, alloc_range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,15 +600,18 @@ impl<'tcx> Stack {
|
|||||||
fn dealloc(
|
fn dealloc(
|
||||||
&mut self,
|
&mut self,
|
||||||
tag: SbTag,
|
tag: SbTag,
|
||||||
dbg_ptr: Pointer<AllocId>, // just for debug printing and error messages
|
(alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing amd error messages
|
||||||
global: &GlobalStateInner,
|
global: &GlobalStateInner,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
// Step 1: Find granting item.
|
// Step 1: Find granting item.
|
||||||
self.find_granting(AccessKind::Write, tag).ok_or_else(|| {
|
self.find_granting(AccessKind::Write, tag).ok_or_else(|| {
|
||||||
err_sb_ub(format!(
|
err_sb_ub(format!(
|
||||||
"no item granting write access for deallocation to tag {:?} at {:?} found in borrow stack",
|
"no item granting write access for deallocation to tag {:?} at {:?} found in borrow stack",
|
||||||
tag, dbg_ptr,
|
tag, alloc_id,
|
||||||
), None)
|
),
|
||||||
|
None,
|
||||||
|
global.get_stack_history(tag, alloc_id, alloc_range, offset, None),
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Step 2: Remove all items. Also checks for protectors.
|
// Step 2: Remove all items. Also checks for protectors.
|
||||||
@ -426,16 +633,16 @@ impl<'tcx> Stack {
|
|||||||
derived_from: SbTag,
|
derived_from: SbTag,
|
||||||
new: Item,
|
new: Item,
|
||||||
(alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages
|
(alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages
|
||||||
global: &GlobalStateInner,
|
global: &mut GlobalStateInner,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
// Figure out which access `perm` corresponds to.
|
// Figure out which access `perm` corresponds to.
|
||||||
let access =
|
let access =
|
||||||
if new.perm.grants(AccessKind::Write) { AccessKind::Write } else { AccessKind::Read };
|
if new.perm.grants(AccessKind::Write) { AccessKind::Write } else { AccessKind::Read };
|
||||||
// Now we figure out which item grants our parent (`derived_from`) this kind of access.
|
// Now we figure out which item grants our parent (`derived_from`) this kind of access.
|
||||||
// We use that to determine where to put the new item.
|
// We use that to determine where to put the new item.
|
||||||
let granting_idx = self
|
let granting_idx = self.find_granting(access, derived_from).ok_or_else(|| {
|
||||||
.find_granting(access, derived_from)
|
self.grant_error(derived_from, new, alloc_id, alloc_range, offset, global)
|
||||||
.ok_or_else(|| self.grant_error(derived_from, new, alloc_id, alloc_range, offset))?;
|
})?;
|
||||||
|
|
||||||
// Compute where to put the new item.
|
// Compute where to put the new item.
|
||||||
// Either way, we ensure that we insert the new item in a way such that between
|
// Either way, we ensure that we insert the new item in a way such that between
|
||||||
@ -483,6 +690,7 @@ impl<'tcx> Stack {
|
|||||||
alloc_id: AllocId,
|
alloc_id: AllocId,
|
||||||
alloc_range: AllocRange,
|
alloc_range: AllocRange,
|
||||||
error_offset: Size,
|
error_offset: Size,
|
||||||
|
global: &GlobalStateInner,
|
||||||
) -> InterpError<'static> {
|
) -> InterpError<'static> {
|
||||||
let action = format!(
|
let action = format!(
|
||||||
"trying to reborrow {:?} for {:?} permission at {}[{:#x}]",
|
"trying to reborrow {:?} for {:?} permission at {}[{:#x}]",
|
||||||
@ -494,6 +702,7 @@ impl<'tcx> Stack {
|
|||||||
err_sb_ub(
|
err_sb_ub(
|
||||||
format!("{}{}", action, self.error_cause(derived_from)),
|
format!("{}{}", action, self.error_cause(derived_from)),
|
||||||
Some(Self::operation_summary("a reborrow", alloc_id, alloc_range)),
|
Some(Self::operation_summary("a reborrow", alloc_id, alloc_range)),
|
||||||
|
global.get_stack_history(derived_from, alloc_id, alloc_range, error_offset, None),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,6 +714,7 @@ impl<'tcx> Stack {
|
|||||||
alloc_id: AllocId,
|
alloc_id: AllocId,
|
||||||
alloc_range: AllocRange,
|
alloc_range: AllocRange,
|
||||||
error_offset: Size,
|
error_offset: Size,
|
||||||
|
global: &GlobalStateInner,
|
||||||
) -> InterpError<'static> {
|
) -> InterpError<'static> {
|
||||||
let action = format!(
|
let action = format!(
|
||||||
"attempting a {} using {:?} at {}[{:#x}]",
|
"attempting a {} using {:?} at {}[{:#x}]",
|
||||||
@ -516,6 +726,7 @@ impl<'tcx> Stack {
|
|||||||
err_sb_ub(
|
err_sb_ub(
|
||||||
format!("{}{}", action, self.error_cause(tag)),
|
format!("{}{}", action, self.error_cause(tag)),
|
||||||
Some(Self::operation_summary("an access", alloc_id, alloc_range)),
|
Some(Self::operation_summary("an access", alloc_id, alloc_range)),
|
||||||
|
global.get_stack_history(tag, alloc_id, alloc_range, error_offset, None),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,11 +736,10 @@ impl<'tcx> Stack {
|
|||||||
alloc_range: AllocRange,
|
alloc_range: AllocRange,
|
||||||
) -> String {
|
) -> String {
|
||||||
format!(
|
format!(
|
||||||
"this error occurs as part of {} at {:?}[{:#x}..{:#x}]",
|
"this error occurs as part of {} at {:?}{}",
|
||||||
operation,
|
operation,
|
||||||
alloc_id,
|
alloc_id,
|
||||||
alloc_range.start.bytes(),
|
HexRange(alloc_range)
|
||||||
alloc_range.end().bytes()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -620,6 +830,7 @@ impl Stacks {
|
|||||||
(tag, Permission::SharedReadWrite)
|
(tag, Permission::SharedReadWrite)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
extra.add_creation(None, base_tag, id, alloc_range(Size::ZERO, size));
|
||||||
Stacks::new(size, perm, base_tag)
|
Stacks::new(size, perm, base_tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,11 +848,11 @@ impl Stacks {
|
|||||||
Pointer::new(alloc_id, range.start),
|
Pointer::new(alloc_id, range.start),
|
||||||
range.size.bytes()
|
range.size.bytes()
|
||||||
);
|
);
|
||||||
let global = &*state.borrow();
|
|
||||||
self.for_each(range, move |offset, stack| {
|
self.for_each(range, move |offset, stack| {
|
||||||
stack.access(AccessKind::Read, tag, (alloc_id, range, offset), global)
|
let mut state = state.borrow_mut();
|
||||||
|
stack.access(AccessKind::Read, tag, (alloc_id, range, offset), &mut state)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn memory_written<'tcx>(
|
pub fn memory_written<'tcx>(
|
||||||
@ -649,7 +860,7 @@ impl Stacks {
|
|||||||
alloc_id: AllocId,
|
alloc_id: AllocId,
|
||||||
tag: SbTag,
|
tag: SbTag,
|
||||||
range: AllocRange,
|
range: AllocRange,
|
||||||
state: &mut GlobalState,
|
state: &GlobalState,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
trace!(
|
trace!(
|
||||||
"write access with tag {:?}: {:?}, size {}",
|
"write access with tag {:?}: {:?}, size {}",
|
||||||
@ -657,9 +868,9 @@ impl Stacks {
|
|||||||
Pointer::new(alloc_id, range.start),
|
Pointer::new(alloc_id, range.start),
|
||||||
range.size.bytes()
|
range.size.bytes()
|
||||||
);
|
);
|
||||||
let global = state.get_mut();
|
|
||||||
self.for_each_mut(range, move |offset, stack| {
|
self.for_each_mut(range, move |offset, stack| {
|
||||||
stack.access(AccessKind::Write, tag, (alloc_id, range, offset), global)
|
let mut state = state.borrow_mut();
|
||||||
|
stack.access(AccessKind::Write, tag, (alloc_id, range, offset), &mut state)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -669,13 +880,15 @@ impl Stacks {
|
|||||||
alloc_id: AllocId,
|
alloc_id: AllocId,
|
||||||
tag: SbTag,
|
tag: SbTag,
|
||||||
range: AllocRange,
|
range: AllocRange,
|
||||||
state: &mut GlobalState,
|
state: &GlobalState,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes());
|
trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes());
|
||||||
let global = state.get_mut();
|
|
||||||
self.for_each_mut(range, move |offset, stack| {
|
self.for_each_mut(range, move |offset, stack| {
|
||||||
stack.dealloc(tag, Pointer::new(alloc_id, offset), global)
|
let mut state = state.borrow_mut();
|
||||||
})
|
stack.dealloc(tag, (alloc_id, range, offset), &mut state)
|
||||||
|
})?;
|
||||||
|
state.borrow_mut().extras.remove(&alloc_id);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -705,6 +918,17 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
}
|
}
|
||||||
let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr)?;
|
let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr)?;
|
||||||
|
|
||||||
|
let mem_extra = this.machine.stacked_borrows.as_mut().unwrap().get_mut();
|
||||||
|
mem_extra.add_creation(
|
||||||
|
Some(orig_tag),
|
||||||
|
new_tag,
|
||||||
|
alloc_id,
|
||||||
|
alloc_range(base_offset, base_offset + size),
|
||||||
|
);
|
||||||
|
if protect {
|
||||||
|
mem_extra.add_protector(orig_tag, new_tag, alloc_id);
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
|
// Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
|
||||||
let (alloc_size, _) =
|
let (alloc_size, _) =
|
||||||
this.get_alloc_size_and_align(alloc_id, AllocCheck::Dereferenceable)?;
|
this.get_alloc_size_and_align(alloc_id, AllocCheck::Dereferenceable)?;
|
||||||
@ -753,7 +977,6 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
let extra = this.get_alloc_extra(alloc_id)?;
|
let extra = this.get_alloc_extra(alloc_id)?;
|
||||||
let stacked_borrows =
|
let stacked_borrows =
|
||||||
extra.stacked_borrows.as_ref().expect("we should have Stacked Borrows data");
|
extra.stacked_borrows.as_ref().expect("we should have Stacked Borrows data");
|
||||||
let global = this.machine.stacked_borrows.as_ref().unwrap().borrow();
|
|
||||||
this.visit_freeze_sensitive(place, size, |mut range, frozen| {
|
this.visit_freeze_sensitive(place, size, |mut range, frozen| {
|
||||||
// Adjust range.
|
// Adjust range.
|
||||||
range.start += base_offset;
|
range.start += base_offset;
|
||||||
@ -765,7 +988,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
};
|
};
|
||||||
let item = Item { perm, tag: new_tag, protector };
|
let item = Item { perm, tag: new_tag, protector };
|
||||||
stacked_borrows.for_each(range, |offset, stack| {
|
stacked_borrows.for_each(range, |offset, stack| {
|
||||||
stack.grant(orig_tag, item, (alloc_id, range, offset), &*global)
|
let mut global =
|
||||||
|
this.machine.stacked_borrows.as_ref().unwrap().borrow_mut();
|
||||||
|
stack.grant(orig_tag, item, (alloc_id, range, offset), &mut *global)
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -777,11 +1002,11 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
let (alloc_extra, memory_extra) = this.get_alloc_extra_mut(alloc_id)?;
|
let (alloc_extra, memory_extra) = this.get_alloc_extra_mut(alloc_id)?;
|
||||||
let stacked_borrows =
|
let stacked_borrows =
|
||||||
alloc_extra.stacked_borrows.as_mut().expect("we should have Stacked Borrows data");
|
alloc_extra.stacked_borrows.as_mut().expect("we should have Stacked Borrows data");
|
||||||
let global = memory_extra.stacked_borrows.as_mut().unwrap().get_mut();
|
|
||||||
let item = Item { perm, tag: new_tag, protector };
|
let item = Item { perm, tag: new_tag, protector };
|
||||||
let range = alloc_range(base_offset, size);
|
let range = alloc_range(base_offset, size);
|
||||||
stacked_borrows.for_each_mut(alloc_range(base_offset, size), |offset, stack| {
|
stacked_borrows.for_each_mut(range, |offset, stack| {
|
||||||
stack.grant(orig_tag, item, (alloc_id, range, offset), global)
|
let mut global = memory_extra.stacked_borrows.as_ref().unwrap().borrow_mut();
|
||||||
|
stack.grant(orig_tag, item, (alloc_id, range, offset), &mut *global)
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -807,14 +1032,12 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Compute new borrow.
|
// Compute new borrow.
|
||||||
let new_tag = {
|
let mem_extra = this.machine.stacked_borrows.as_mut().unwrap().get_mut();
|
||||||
let mem_extra = this.machine.stacked_borrows.as_mut().unwrap().get_mut();
|
let new_tag = match kind {
|
||||||
match kind {
|
// Give up tracking for raw pointers.
|
||||||
// Give up tracking for raw pointers.
|
RefKind::Raw { .. } if !mem_extra.tag_raw => SbTag::Untagged,
|
||||||
RefKind::Raw { .. } if !mem_extra.tag_raw => SbTag::Untagged,
|
// All other pointers are properly tracked.
|
||||||
// All other pointers are properly tracked.
|
_ => SbTag::Tagged(mem_extra.new_ptr()),
|
||||||
_ => SbTag::Tagged(mem_extra.new_ptr()),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reborrow.
|
// Reborrow.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user