Skip to content

Commit 98c8c8f

Browse files
committed
Auto merge of #2030 - saethlin:track-alloc-history, r=oli-obk
Print spans where tags are created and invalidated 5225225 called this "automatic tag tracking" and I think that may be a reasonable description, but I would like to kill tag tracking as a primary use of Miri if possible. Tag tracking isn't always possible; for example if the UB is only detected with isolation off and the failing tag is made unstable by removing isolation. (also it's bad UX to run the tool twice) This is just one of the things we can do with #2024 The memory usage of this is _shockingly_ low, I think because the memory usage of Miri is driven by allocations where each byte ends up with its own very large stack. The memory usage in this change is linear with the number of tags, not tags * bytes. If memory usage gets out of control we can cap the number of events we save per allocation, from experience we tend to only use the most recent few in diagnostics but of course there's no guarantee of that so if we can manage to keep everything that would be best. In many cases now I can tell exactly what these codebases are doing wrong just from the new outputs here, which I think is extremely cool. New helps generated with plain old `cargo miri test` on `rust-argon2` v1.0.0: ``` test argon2::tests::single_thread_verification_multi_lane_hash ... error: Undefined Behavior: trying to reborrow <1485898> for Unique permission at alloc110523[0x0], but that tag does not exist in the borrow stack for this location --> /home/ben/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/mem/manually_drop.rs:89:9 | 89 | slot.value | ^^^^^^^^^^ | | | trying to reborrow <1485898> for Unique permission at alloc110523[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at alloc110523[0x0..0x20] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the 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: <1485898> was created by a retag at offsets [0x0..0x20] --> src/memory.rs:42:13 | 42 | vec.push(unsafe { &mut (*ptr) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: <1485898> was later invalidated at offsets [0x0..0x20] --> src/memory.rs:42:31 | 42 | vec.push(unsafe { &mut (*ptr) }); | ^^^^^^^^^^^ ``` And with `-Zmiri-tag-raw-pointers` on `slab` v0.4.5 ``` error: Undefined Behavior: trying to reborrow <2915> for Unique permission at alloc1418[0x0], but that tag does not exist in the borrow stack for this location --> /tmp/slab-0.4.5/src/lib.rs:835:16 | 835 | match (&mut *ptr1, &mut *ptr2) { | ^^^^^^^^^^ | | | trying to reborrow <2915> for Unique permission at alloc1418[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at alloc1418[0x0..0x10] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the 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: <2915> was created by a retag at offsets [0x0..0x10] --> /tmp/slab-0.4.5/src/lib.rs:833:20 | 833 | let ptr1 = self.entries.get_unchecked_mut(key1) as *mut Entry<T>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: <2915> was later invalidated at offsets [0x0..0x20] --> /tmp/slab-0.4.5/src/lib.rs:834:20 | 834 | let ptr2 = self.entries.get_unchecked_mut(key2) as *mut Entry<T>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` And without raw pointer tagging, `cargo miri test` on `half` v1.8.2 ``` error: Undefined Behavior: trying to reborrow <untagged> for Unique permission at alloc1340[0x0], but that tag only grants SharedReadOnly permission for this location --> /home/ben/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/slice/raw.rs:141:9 | 141 | &mut *ptr::slice_from_raw_parts_mut(data, len) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | trying to reborrow <untagged> for Unique permission at alloc1340[0x0], but that tag only grants SharedReadOnly permission for this location | this error occurs as part of a reborrow at alloc1340[0x0..0x6] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the 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 most recently created at offsets [0x0..0x6] --> /tmp/half-1.8.2/src/slice.rs:309:22 | 309 | let length = self.len(); | ^^^^^^^^^^ help: this tag was also created here at offsets [0x0..0x6] --> /tmp/half-1.8.2/src/slice.rs:308:23 | 308 | let pointer = self.as_ptr() as *mut u16; | ^^^^^^^^^^^^^ ``` The second suggestion is close to guesswork, but from experience it tends to be correct (as in, it tends to locate the pointer the user wanted) more often that it doesn't.
2 parents d76c2c5 + 8ff0aac commit 98c8c8f

File tree

6 files changed

+503
-124
lines changed

6 files changed

+503
-124
lines changed

src/diagnostics.rs

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use log::trace;
77
use rustc_middle::ty;
88
use rustc_span::{source_map::DUMMY_SP, Span, SpanData, Symbol};
99

10-
use crate::stacked_borrows::{AccessKind, SbTag};
10+
use crate::helpers::HexRange;
11+
use crate::stacked_borrows::{diagnostics::TagHistory, AccessKind, SbTag};
1112
use crate::*;
1213

1314
/// Details of premature program termination.
@@ -19,6 +20,7 @@ pub enum TerminationInfo {
1920
msg: String,
2021
help: Option<String>,
2122
url: String,
23+
history: Option<TagHistory>,
2224
},
2325
Deadlock,
2426
MultipleSymbolDefinitions {
@@ -155,12 +157,46 @@ pub fn report_error<'tcx, 'mir>(
155157
(None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation;")),
156158
(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")),
157159
],
158-
ExperimentalUb { url, help, .. } => {
160+
ExperimentalUb { url, help, history, .. } => {
159161
msg.extend(help.clone());
160-
vec![
162+
let mut helps = vec![
161163
(None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental")),
162-
(None, format!("see {} for further information", url))
163-
]
164+
(None, format!("see {} for further information", url)),
165+
];
166+
match history {
167+
Some(TagHistory::Tagged {tag, created: (created_range, created_span), invalidated, protected }) => {
168+
let msg = format!("{:?} was created by a retag at offsets {}", tag, HexRange(*created_range));
169+
helps.push((Some(created_span.clone()), msg));
170+
if let Some((invalidated_range, invalidated_span)) = invalidated {
171+
let msg = format!("{:?} was later invalidated at offsets {}", tag, HexRange(*invalidated_range));
172+
helps.push((Some(invalidated_span.clone()), msg));
173+
}
174+
if let Some((protecting_tag, protecting_tag_span, protection_span)) = protected {
175+
helps.push((Some(protecting_tag_span.clone()), format!("{:?} was protected due to {:?} which was created here", tag, protecting_tag)));
176+
helps.push((Some(protection_span.clone()), "this protector is live for this call".to_string()));
177+
}
178+
}
179+
Some(TagHistory::Untagged{ recently_created, recently_invalidated, matching_created, protected }) => {
180+
if let Some((range, span)) = recently_created {
181+
let msg = format!("tag was most recently created at offsets {}", HexRange(*range));
182+
helps.push((Some(span.clone()), msg));
183+
}
184+
if let Some((range, span)) = recently_invalidated {
185+
let msg = format!("tag was later invalidated at offsets {}", HexRange(*range));
186+
helps.push((Some(span.clone()), msg));
187+
}
188+
if let Some((range, span)) = matching_created {
189+
let msg = format!("this tag was also created here at offsets {}", HexRange(*range));
190+
helps.push((Some(span.clone()), msg));
191+
}
192+
if let Some((protecting_tag, protecting_tag_span, protection_span)) = protected {
193+
helps.push((Some(protecting_tag_span.clone()), format!("{:?} was protected due to a tag which was created here", protecting_tag)));
194+
helps.push((Some(protection_span.clone()), "this protector is live for this call".to_string()));
195+
}
196+
}
197+
None => {}
198+
}
199+
helps
164200
}
165201
MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
166202
vec![

src/helpers.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod convert;
22

33
use std::mem;
44
use std::num::NonZeroUsize;
5+
use std::rc::Rc;
56
use std::time::Duration;
67

78
use log::trace;
@@ -816,7 +817,7 @@ pub fn isolation_abort_error(name: &str) -> InterpResult<'static> {
816817

817818
/// Retrieve the list of local crates that should have been passed by cargo-miri in
818819
/// MIRI_LOCAL_CRATES and turn them into `CrateNum`s.
819-
pub fn get_local_crates(tcx: &TyCtxt<'_>) -> Vec<CrateNum> {
820+
pub fn get_local_crates(tcx: &TyCtxt<'_>) -> Rc<[CrateNum]> {
820821
// Convert the local crate names from the passed-in config into CrateNums so that they can
821822
// be looked up quickly during execution
822823
let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
@@ -830,5 +831,14 @@ pub fn get_local_crates(tcx: &TyCtxt<'_>) -> Vec<CrateNum> {
830831
local_crates.push(crate_num);
831832
}
832833
}
833-
local_crates
834+
Rc::from(local_crates.as_slice())
835+
}
836+
837+
/// Formats an AllocRange like [0x1..0x3], for use in diagnostics.
838+
pub struct HexRange(pub AllocRange);
839+
840+
impl std::fmt::Display for HexRange {
841+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
842+
write!(f, "[{:#x}..{:#x}]", self.0.start.bytes(), self.0.end().bytes())
843+
}
834844
}

src/machine.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::cell::RefCell;
66
use std::collections::HashSet;
77
use std::fmt;
88
use std::num::NonZeroU64;
9+
use std::rc::Rc;
910
use std::time::Instant;
1011

1112
use rand::rngs::StdRng;
@@ -273,7 +274,7 @@ pub struct Evaluator<'mir, 'tcx> {
273274
pub(crate) backtrace_style: BacktraceStyle,
274275

275276
/// Crates which are considered local for the purposes of error reporting.
276-
pub(crate) local_crates: Vec<CrateNum>,
277+
pub(crate) local_crates: Rc<[CrateNum]>,
277278

278279
/// Mapping extern static names to their base pointer.
279280
extern_statics: FxHashMap<Symbol, Pointer<Tag>>,
@@ -569,7 +570,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
569570
let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
570571
let alloc = alloc.into_owned();
571572
let stacks = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
572-
Some(Stacks::new_allocation(id, alloc.size(), stacked_borrows, kind))
573+
Some(Stacks::new_allocation(
574+
id,
575+
alloc.size(),
576+
stacked_borrows,
577+
kind,
578+
&ecx.machine.threads,
579+
ecx.machine.local_crates.clone(),
580+
))
573581
} else {
574582
None
575583
};
@@ -634,6 +642,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
634642
tag,
635643
range,
636644
machine.stacked_borrows.as_ref().unwrap(),
645+
&machine.threads,
637646
)
638647
} else {
639648
Ok(())
@@ -656,7 +665,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
656665
alloc_id,
657666
tag,
658667
range,
659-
machine.stacked_borrows.as_mut().unwrap(),
668+
machine.stacked_borrows.as_ref().unwrap(),
669+
&machine.threads,
660670
)
661671
} else {
662672
Ok(())
@@ -682,7 +692,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
682692
alloc_id,
683693
tag,
684694
range,
685-
machine.stacked_borrows.as_mut().unwrap(),
695+
machine.stacked_borrows.as_ref().unwrap(),
686696
)
687697
} else {
688698
Ok(())

0 commit comments

Comments
 (0)