Skip to content

Commit

Permalink
Added support for multiple tracked pointers, allocs and calls
Browse files Browse the repository at this point in the history
- Changed arg parsing to handle comma seperated list of `u64`'s.
- Changed type and field names of config, executor and global state
  to hold a set of tracked ids.
- Adjusted Readme:
    - explained list format
    - arguments do not overwrite, instead append
    - no effect on duplication
- Created a parsing function for comma separated lists
- Added error printing to alloc_id parsing
  • Loading branch information
y86-dev committed Apr 21, 2022
1 parent c9039de commit bf17dbe
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 63 deletions.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,16 +314,20 @@ environment variable:
ensure alignment. (The standard library `align_to` method works fine in both
modes; under symbolic alignment it only fills the middle slice when the
allocation guarantees sufficient alignment.)
* `-Zmiri-track-alloc-id=<id>` shows a backtrace when the given allocation is
* `-Zmiri-track-alloc-id=<id1>,<id2>,...` shows a backtrace when the given allocations are
being allocated or freed. This helps in debugging memory leaks and
use after free bugs.
* `-Zmiri-track-call-id=<id>` shows a backtrace when the given call id is
use after free bugs. Specifying this argument multiple times does not overwrite the previous
values, instead it appends its values to the list. Listing an id multiple times has no effect.
* `-Zmiri-track-call-id=<id1>,<id2>,...` shows a backtrace when the given call ids are
assigned to a stack frame. This helps in debugging UB related to Stacked
Borrows "protectors".
* `-Zmiri-track-pointer-tag=<tag>` shows a backtrace when the given pointer tag
Borrows "protectors". Specifying this argument multiple times does not overwrite the previous
values, instead it appends its values to the list. Listing an id multiple times has no effect.
* `-Zmiri-track-pointer-tag=<tag1>,<tag2>,...` shows a backtrace when a given pointer tag
is popped from a borrow stack (which is where the tag becomes invalid and any
future use of it will error). This helps you in finding out why UB is
happening and where in your code would be a good place to look for it.
Specifying this argument multiple times does not overwrite the previous
values, instead it appends its values to the list. Listing a tag multiple times has no effect.
* `-Zmiri-tag-raw-pointers` makes Stacked Borrows assign proper tags even for raw pointers. This can
make valid code using int-to-ptr casts fail to pass the checks, but also can help identify latent
aliasing issues in code that Miri accepts by default. You can recognize false positives by
Expand Down
78 changes: 47 additions & 31 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,13 @@ fn run_compiler(
std::process::exit(exit_code)
}

/// Parses a comma separated list of `T` from the given string:
///
/// `<value1>,<value2>,<value3>,...`
fn parse_comma_list<T: FromStr>(input: &str) -> Result<Vec<T>, T::Err> {
input.split(',').map(str::parse::<T>).collect()
}

fn main() {
rustc_driver::install_ice_hook();

Expand Down Expand Up @@ -397,46 +404,55 @@ fn main() {
.push(arg.strip_prefix("-Zmiri-env-forward=").unwrap().to_owned());
}
arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
let id: u64 =
match arg.strip_prefix("-Zmiri-track-pointer-tag=").unwrap().parse() {
Ok(id) => id,
Err(err) =>
panic!(
"-Zmiri-track-pointer-tag requires a valid `u64` argument: {}",
err
),
};
if let Some(id) = miri::PtrId::new(id) {
miri_config.tracked_pointer_tag = Some(id);
} else {
panic!("-Zmiri-track-pointer-tag requires a nonzero argument");
let ids: Vec<u64> = match parse_comma_list(
arg.strip_prefix("-Zmiri-track-pointer-tag=").unwrap(),
) {
Ok(ids) => ids,
Err(err) =>
panic!(
"-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {}",
err
),
};
for id in ids.into_iter().map(miri::PtrId::new) {
if let Some(id) = id {
miri_config.tracked_pointer_tags.insert(id);
} else {
panic!("-Zmiri-track-pointer-tag requires nonzero arguments");
}
}
}
arg if arg.starts_with("-Zmiri-track-call-id=") => {
let id: u64 = match arg.strip_prefix("-Zmiri-track-call-id=").unwrap().parse() {
Ok(id) => id,
let ids: Vec<u64> = match parse_comma_list(
arg.strip_prefix("-Zmiri-track-call-id=").unwrap(),
) {
Ok(ids) => ids,
Err(err) =>
panic!("-Zmiri-track-call-id requires a valid `u64` argument: {}", err),
panic!(
"-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {}",
err
),
};
if let Some(id) = miri::CallId::new(id) {
miri_config.tracked_call_id = Some(id);
} else {
panic!("-Zmiri-track-call-id requires a nonzero argument");
for id in ids.into_iter().map(miri::CallId::new) {
if let Some(id) = id {
miri_config.tracked_call_ids.insert(id);
} else {
panic!("-Zmiri-track-call-id requires a nonzero argument");
}
}
}
arg if arg.starts_with("-Zmiri-track-alloc-id=") => {
let id = match arg
.strip_prefix("-Zmiri-track-alloc-id=")
.unwrap()
.parse()
.ok()
.and_then(NonZeroU64::new)
{
Some(id) => id,
None =>
panic!("-Zmiri-track-alloc-id requires a valid non-zero `u64` argument"),
let ids: Vec<miri::AllocId> = match parse_comma_list::<NonZeroU64>(
arg.strip_prefix("-Zmiri-track-alloc-id=").unwrap(),
) {
Ok(ids) => ids.into_iter().map(miri::AllocId).collect(),
Err(err) =>
panic!(
"-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {}",
err
),
};
miri_config.tracked_alloc_id = Some(miri::AllocId(id));
miri_config.tracked_alloc_ids.extend(ids);
}
arg if arg.starts_with("-Zmiri-compare-exchange-weak-failure-rate=") => {
let rate = match arg
Expand Down
20 changes: 11 additions & 9 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use rustc_target::spec::abi::Abi;

use rustc_session::config::EntryFnType;

use std::collections::HashSet;

use crate::*;

#[derive(Copy, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -91,12 +93,12 @@ pub struct MiriConfig {
pub args: Vec<String>,
/// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
pub seed: Option<u64>,
/// The stacked borrows pointer id to report about
pub tracked_pointer_tag: Option<PtrId>,
/// The stacked borrows call ID to report about
pub tracked_call_id: Option<CallId>,
/// The allocation id to report about.
pub tracked_alloc_id: Option<AllocId>,
/// The stacked borrows pointer ids to report about
pub tracked_pointer_tags: HashSet<PtrId>,
/// The stacked borrows call IDs to report about
pub tracked_call_ids: HashSet<CallId>,
/// The allocation ids to report about.
pub tracked_alloc_ids: HashSet<AllocId>,
/// Whether to track raw pointers in stacked borrows.
pub tag_raw: bool,
/// Determine if data race detection should be enabled
Expand Down Expand Up @@ -130,9 +132,9 @@ impl Default for MiriConfig {
forwarded_env_vars: vec![],
args: vec![],
seed: None,
tracked_pointer_tag: None,
tracked_call_id: None,
tracked_alloc_id: None,
tracked_pointer_tags: Default::default(),
tracked_call_ids: Default::default(),
tracked_alloc_ids: Default::default(),
tag_raw: false,
data_race_detector: true,
cmpxchg_weak_failure_rate: 0.8,
Expand Down
15 changes: 8 additions & 7 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashSet;
use std::fmt;
use std::num::NonZeroU64;
use std::time::Instant;
Expand Down Expand Up @@ -281,9 +282,9 @@ pub struct Evaluator<'mir, 'tcx> {
/// Needs to be queried by ptr_to_int, hence needs interior mutability.
pub(crate) rng: RefCell<StdRng>,

/// An allocation ID to report when it is being allocated
/// The allocation IDs to report when they are being allocated
/// (helps for debugging memory leaks and use after free bugs).
tracked_alloc_id: Option<AllocId>,
tracked_alloc_ids: HashSet<AllocId>,

/// Controls whether alignment of memory accesses is being checked.
pub(crate) check_alignment: AlignmentCheck,
Expand All @@ -303,8 +304,8 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
let stacked_borrows = if config.stacked_borrows {
Some(RefCell::new(stacked_borrows::GlobalStateInner::new(
config.tracked_pointer_tag,
config.tracked_call_id,
config.tracked_pointer_tags.clone(),
config.tracked_call_ids.clone(),
config.tag_raw,
)))
} else {
Expand Down Expand Up @@ -340,7 +341,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
local_crates,
extern_statics: FxHashMap::default(),
rng: RefCell::new(rng),
tracked_alloc_id: config.tracked_alloc_id,
tracked_alloc_ids: config.tracked_alloc_ids.clone(),
check_alignment: config.check_alignment,
cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
}
Expand Down Expand Up @@ -560,7 +561,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
alloc: Cow<'b, Allocation>,
kind: Option<MemoryKind<Self::MemoryKind>>,
) -> Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>> {
if Some(id) == ecx.machine.tracked_alloc_id {
if ecx.machine.tracked_alloc_ids.contains(&id) {
register_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id));
}

Expand Down Expand Up @@ -669,7 +670,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
(alloc_id, tag): (AllocId, Self::TagExtra),
range: AllocRange,
) -> InterpResult<'tcx> {
if Some(alloc_id) == machine.tracked_alloc_id {
if machine.tracked_alloc_ids.contains(&alloc_id) {
register_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
}
if let Some(data_race) = &mut alloc_extra.data_race {
Expand Down
23 changes: 12 additions & 11 deletions src/stacked_borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rustc_middle::ty::{
};
use rustc_span::DUMMY_SP;
use rustc_target::abi::Size;
use std::collections::HashSet;

use crate::*;

Expand Down Expand Up @@ -104,10 +105,10 @@ pub struct GlobalStateInner {
next_call_id: CallId,
/// Those call IDs corresponding to functions that are still running.
active_calls: FxHashSet<CallId>,
/// The pointer id to trace
tracked_pointer_tag: Option<PtrId>,
/// The call id to trace
tracked_call_id: Option<CallId>,
/// The pointer ids to trace
tracked_pointer_tags: HashSet<PtrId>,
/// The call ids to trace
tracked_call_ids: HashSet<CallId>,
/// Whether to track raw pointers.
tag_raw: bool,
}
Expand Down Expand Up @@ -158,24 +159,24 @@ impl fmt::Display for RefKind {
/// Utilities for initialization and ID generation
impl GlobalStateInner {
pub fn new(
tracked_pointer_tag: Option<PtrId>,
tracked_call_id: Option<CallId>,
tracked_pointer_tags: HashSet<PtrId>,
tracked_call_ids: HashSet<CallId>,
tag_raw: bool,
) -> Self {
GlobalStateInner {
next_ptr_id: NonZeroU64::new(1).unwrap(),
base_ptr_ids: FxHashMap::default(),
next_call_id: NonZeroU64::new(1).unwrap(),
active_calls: FxHashSet::default(),
tracked_pointer_tag,
tracked_call_id,
tracked_pointer_tags,
tracked_call_ids,
tag_raw,
}
}

fn new_ptr(&mut self) -> PtrId {
let id = self.next_ptr_id;
if Some(id) == self.tracked_pointer_tag {
if self.tracked_pointer_tags.contains(&id) {
register_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(id));
}
self.next_ptr_id = NonZeroU64::new(id.get() + 1).unwrap();
Expand All @@ -185,7 +186,7 @@ impl GlobalStateInner {
pub fn new_call(&mut self) -> CallId {
let id = self.next_call_id;
trace!("new_call: Assigning ID {}", id);
if Some(id) == self.tracked_call_id {
if self.tracked_call_ids.contains(&id) {
register_diagnostic(NonHaltingDiagnostic::CreatedCallId(id));
}
assert!(self.active_calls.insert(id));
Expand Down Expand Up @@ -311,7 +312,7 @@ impl<'tcx> Stack {
global: &GlobalStateInner,
) -> InterpResult<'tcx> {
if let SbTag::Tagged(id) = item.tag {
if Some(id) == global.tracked_pointer_tag {
if global.tracked_pointer_tags.contains(&id) {
register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(
item.clone(),
provoking_access,
Expand Down

0 comments on commit bf17dbe

Please sign in to comment.