Skip to content

Commit f94bc71

Browse files
committed
Auto merge of #1080 - rust-lang:stacked_borrow_tracing, r=RalfJung
Add a scheme to find the place where an id was destroyed cc #974 I'm not too happy with it, but since stacked borrows don't have access to the current call stack, I can't just report a warning as per #797 We could add some global mutex that we can throw strings at and `step` will clear out that mutex and report warnings before moving the `statement_id` or the `block_id`, not sure how well that would work. For now I think this is sufficient
2 parents 4ebc030 + 8d409a7 commit f94bc71

File tree

8 files changed

+46
-11
lines changed

8 files changed

+46
-11
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Several `-Z` flags are relevant for Miri:
160160
the program has access to host resources such as environment variables and
161161
randomness (and, eventually, file systems and more).
162162
* `-Zmiri-ignore-leaks` disables the memory leak checker.
163-
* `-Zmiri-env-exclude=<var>` keeps the `var` environment variable isolated from
163+
* `-Zmiri-env-exclude=<var>` keeps the `var` environment variable isolated from
164164
the host. Can be used multiple times to exclude several variables. The `TERM`
165165
environment variable is excluded by default.
166166
* `-Zmir-opt-level` controls how many MIR optimizations are performed. Miri
@@ -171,6 +171,11 @@ Several `-Z` flags are relevant for Miri:
171171
sets this flag per default.
172172
* `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri
173173
enables this per default because it is needed for validation.
174+
* `-Zmiri-track-pointer-tag=<tag>` aborts interpretation with a backtrace when the
175+
given pointer tag is popped from a borrow stack (which is where the tag
176+
becomes invalid and any future use of it will error anyway). This helps you
177+
in finding out why UB is happening and where in your code would be a good
178+
place to look for it.
174179

175180
Moreover, Miri recognizes some environment variables:
176181

benches/helpers/miri_helper.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls<'_> {
3232
excluded_env_vars: vec![],
3333
args: vec![],
3434
seed: None,
35+
tracked_pointer_tag: None,
3536
};
3637
eval_main(tcx, entry_def_id, config);
3738
});

src/bin/miri-rustc-tests.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
4545
excluded_env_vars: vec![],
4646
args: vec![],
4747
seed: None,
48+
tracked_pointer_tag: None,
4849
};
4950
let did = self.0.hir().body_owner_def_id(body_id);
5051
println!("running test: {}", self.0.def_path_debug_str(did));
@@ -64,7 +65,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
6465
ignore_leaks: false,
6566
excluded_env_vars: vec![],
6667
args: vec![],
67-
seed: None
68+
seed: None,
69+
tracked_pointer_tag: None,
6870
};
6971
miri::eval_main(tcx, entry_def_id, config);
7072

src/bin/miri.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fn main() {
126126
let mut communicate = false;
127127
let mut ignore_leaks = false;
128128
let mut seed: Option<u64> = None;
129+
let mut tracked_pointer_tag: Option<miri::PtrId> = None;
129130
let mut rustc_args = vec![];
130131
let mut miri_args = vec![];
131132
let mut after_dashdash = false;
@@ -176,6 +177,17 @@ fn main() {
176177
arg if arg.starts_with("-Zmiri-env-exclude=") => {
177178
excluded_env_vars.push(arg.trim_start_matches("-Zmiri-env-exclude=").to_owned());
178179
},
180+
arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
181+
let id: u64 = match arg.trim_start_matches("-Zmiri-track-pointer-tag=").parse() {
182+
Ok(id) => id,
183+
Err(err) => panic!("-Zmiri-track-pointer-tag requires a valid `u64` as the argument: {}", err),
184+
};
185+
if let Some(id) = miri::PtrId::new(id) {
186+
tracked_pointer_tag = Some(id);
187+
} else {
188+
panic!("-Zmiri-track-pointer-tag must be a nonzero id");
189+
}
190+
},
179191
_ => {
180192
rustc_args.push(arg);
181193
}
@@ -208,6 +220,7 @@ fn main() {
208220
excluded_env_vars,
209221
seed,
210222
args: miri_args,
223+
tracked_pointer_tag,
211224
};
212225
rustc_driver::install_ice_hook();
213226
let result = rustc_driver::catch_fatal_errors(move || {

src/eval.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ pub struct MiriConfig {
2626
pub args: Vec<String>,
2727
/// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
2828
pub seed: Option<u64>,
29+
/// The stacked borrow id to report about
30+
pub tracked_pointer_tag: Option<PtrId>,
2931
}
3032

3133
/// Details of premature program termination.
3234
pub enum TerminationInfo {
3335
Exit(i64),
36+
PoppedTrackedPointerTag(Item),
3437
Abort,
3538
}
3639

@@ -47,7 +50,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
4750
tcx.at(syntax::source_map::DUMMY_SP),
4851
ty::ParamEnv::reveal_all(),
4952
Evaluator::new(config.communicate),
50-
MemoryExtra::new(StdRng::seed_from_u64(config.seed.unwrap_or(0)), config.validate),
53+
MemoryExtra::new(StdRng::seed_from_u64(config.seed.unwrap_or(0)), config.validate, config.tracked_pointer_tag),
5154
);
5255
// Complete initialization.
5356
EnvVars::init(&mut ecx, config.excluded_env_vars);
@@ -216,6 +219,8 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
216219
.expect("invalid MachineStop payload");
217220
match info {
218221
TerminationInfo::Exit(code) => return Some(*code),
222+
TerminationInfo::PoppedTrackedPointerTag(item) =>
223+
format!("popped tracked tag for item {:?}", item),
219224
TerminationInfo::Abort =>
220225
format!("the evaluated program aborted execution")
221226
}

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ pub use crate::operator::EvalContextExt as OperatorEvalContextExt;
4343
pub use crate::range_map::RangeMap;
4444
pub use crate::helpers::{EvalContextExt as HelpersEvalContextExt};
4545
pub use crate::mono_hash_map::MonoHashMap;
46-
pub use crate::stacked_borrows::{EvalContextExt as StackedBorEvalContextExt, Tag, Permission, Stack, Stacks, Item};
46+
pub use crate::stacked_borrows::{
47+
EvalContextExt as StackedBorEvalContextExt, Tag, Permission, Stack, Stacks, Item, PtrId,
48+
GlobalState,
49+
};
4750
pub use crate::machine::{
4851
PAGE_SIZE, STACK_ADDR, STACK_SIZE, NUM_CPUS,
4952
MemoryExtra, AllocExtra, FrameData, MiriMemoryKind, Evaluator, MiriEvalContext, MiriEvalContextExt,

src/machine.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ pub struct MemoryExtra {
7777
}
7878

7979
impl MemoryExtra {
80-
pub fn new(rng: StdRng, validate: bool) -> Self {
80+
pub fn new(rng: StdRng, validate: bool, tracked_pointer_tag: Option<PtrId>) -> Self {
8181
MemoryExtra {
82-
stacked_borrows: Default::default(),
82+
stacked_borrows: Rc::new(RefCell::new(GlobalState::new(tracked_pointer_tag))),
8383
intptrcast: Default::default(),
8484
rng: RefCell::new(rng),
8585
validate,

src/stacked_borrows.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc::hir::Mutability::{Mutable, Immutable};
1212
use rustc::mir::RetagKind;
1313

1414
use crate::{
15-
InterpResult, HelpersEvalContextExt,
15+
InterpResult, HelpersEvalContextExt, TerminationInfo,
1616
MemoryKind, MiriMemoryKind, RangeMap, AllocId, Pointer, Immediate, ImmTy, PlaceTy, MPlaceTy,
1717
};
1818

@@ -105,6 +105,8 @@ pub struct GlobalState {
105105
next_call_id: CallId,
106106
/// Those call IDs corresponding to functions that are still running.
107107
active_calls: HashSet<CallId>,
108+
/// The id to trace in this execution run
109+
tracked_pointer_tag: Option<PtrId>,
108110
}
109111
/// Memory extra state gives us interior mutable access to the global state.
110112
pub type MemoryExtra = Rc<RefCell<GlobalState>>;
@@ -151,18 +153,17 @@ impl fmt::Display for RefKind {
151153
}
152154

153155
/// Utilities for initialization and ID generation
154-
impl Default for GlobalState {
155-
fn default() -> Self {
156+
impl GlobalState {
157+
pub fn new(tracked_pointer_tag: Option<PtrId>) -> Self {
156158
GlobalState {
157159
next_ptr_id: NonZeroU64::new(1).unwrap(),
158160
base_ptr_ids: HashMap::default(),
159161
next_call_id: NonZeroU64::new(1).unwrap(),
160162
active_calls: HashSet::default(),
163+
tracked_pointer_tag,
161164
}
162165
}
163-
}
164166

165-
impl GlobalState {
166167
fn new_ptr(&mut self) -> PtrId {
167168
let id = self.next_ptr_id;
168169
self.next_ptr_id = NonZeroU64::new(id.get() + 1).unwrap();
@@ -270,6 +271,11 @@ impl<'tcx> Stack {
270271

271272
/// Check if the given item is protected.
272273
fn check_protector(item: &Item, tag: Option<Tag>, global: &GlobalState) -> InterpResult<'tcx> {
274+
if let Tag::Tagged(id) = item.tag {
275+
if Some(id) == global.tracked_pointer_tag {
276+
throw_machine_stop!(TerminationInfo::PoppedTrackedPointerTag(item.clone()));
277+
}
278+
}
273279
if let Some(call) = item.protector {
274280
if global.is_active(call) {
275281
if let Some(tag) = tag {

0 commit comments

Comments
 (0)