Auto merge of #2275 - RalfJung:permissive-provenance-for-all, r=RalfJung

Enable permissive provenance by default

This completes the plan laid out in https://github.com/rust-lang/miri/issues/2133:
- We use permissive provenance with wildcard pointers by default.
- We print a warning on int2ptr casts. `-Zmiri-permissive-provenance` suppresses the warning; `-Zmiri-strict-provenance` turns it into a hard error.
- Raw pointer tagging is now always enabled, so we remove the `-Zmiri-tag-raw-pointers` flag and the code for untagged pointers. (Passing the flag still works, for compatibility -- but we just ignore it, with a warning.)

We also fix an intptrcast issue:
- Only live allocations are considered when computing the AllocId from an address.

So, finally, Miri has a good story for ptr2int2ptr roundtrips *and* no weird false negatives when doing raw pointer stuff with Stacked Borrows. :-) 🎉   Thanks a lot to everyone who helped with this, in particular `@carbotaniuman` who convinced me this is even possible.

Fixes https://github.com/rust-lang/miri/issues/2133
Fixes https://github.com/rust-lang/miri/issues/1866
Fixes https://github.com/rust-lang/miri/issues/1993
This commit is contained in:
bors 2022-06-28 01:20:40 +00:00
commit 7fafbde038
78 changed files with 440 additions and 544 deletions

View File

@ -289,6 +289,11 @@ environment variable. We first document the most relevant and most commonly used
`-Zmiri-disable-isolation` is set.
* `-Zmiri-ignore-leaks` disables the memory leak checker, and also allows some
remaining threads to exist when the main thread exits.
* `-Zmiri-permissive-provenance` disables the warning for integer-to-pointer casts and
[`ptr::from_exposed_addr`](https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html).
This will necessarily miss some bugs as those operations are not efficiently and accurately
implementable in a sanitizer, but it will only miss bugs that concern memory/pointers which is
subject to these operations.
* `-Zmiri-preemption-rate` configures the probability that at the end of a basic block, the active
thread will be preempted. The default is `0.01` (i.e., 1%). Setting this to `0` disables
preemption.
@ -306,7 +311,17 @@ environment variable. We first document the most relevant and most commonly used
* `-Zmiri-strict-provenance` enables [strict
provenance](https://github.com/rust-lang/rust/issues/95228) checking in Miri. This means that
casting an integer to a pointer yields a result with 'invalid' provenance, i.e., with provenance
that cannot be used for any memory access. Also implies `-Zmiri-tag-raw-pointers`.
that cannot be used for any memory access.
* `-Zmiri-symbolic-alignment-check` makes the alignment check more strict. By default, alignment is
checked by casting the pointer to an integer, and making sure that is a multiple of the alignment.
This can lead to cases where a program passes the alignment check by pure chance, because things
"happened to be" sufficiently aligned -- there is no UB in this execution but there would be UB in
others. To avoid such cases, the symbolic alignment check only takes into account the requested
alignment of the relevant allocation, and the offset into that allocation. This avoids missing
such bugs, but it also incurs some false positives when the code does manual integer arithmetic to
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.)
The remaining flags are for advanced use only, and more likely to change or be removed.
Some of these are **unsound**, which means they can lead
@ -321,7 +336,7 @@ to Miri failing to detect cases of undefined behavior in a program.
integers via `mem::transmute` or union/pointer type punning. This has two effects: it disables the
check against integers storing a pointer (i.e., data with provenance), thus allowing
pointer-to-integer transmutation, and it treats integer-to-pointer transmutation as equivalent to
a cast. Using this flag is **unsound** and
a cast. Implies `-Zmiri-permissive-provenance`. Using this flag is **unsound** and
[deprecated](https://github.com/rust-lang/miri/issues/2188).
* `-Zmiri-disable-abi-check` disables checking [function ABI]. Using this flag
is **unsound**.
@ -354,27 +369,6 @@ to Miri failing to detect cases of undefined behavior in a program.
application instead of raising an error within the context of Miri (and halting
execution). Note that code might not expect these operations to ever panic, so
this flag can lead to strange (mis)behavior.
* `-Zmiri-permissive-provenance` is **experimental**. This will make Miri do a
best-effort attempt to implement the semantics of
[`expose_addr`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.expose_addr)
and
[`ptr::from_exposed_addr`](https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html)
for pointer-to-int and int-to-pointer casts, respectively. This will
necessarily miss some bugs as those semantics are not efficiently
implementable in a sanitizer, but it will only miss bugs that concerns
memory/pointers which is subject to these operations.
* `-Zmiri-symbolic-alignment-check` makes the alignment check more strict. By
default, alignment is checked by casting the pointer to an integer, and making
sure that is a multiple of the alignment. This can lead to cases where a
program passes the alignment check by pure chance, because things "happened to
be" sufficiently aligned -- there is no UB in this execution but there would
be UB in others. To avoid such cases, the symbolic alignment check only takes
into account the requested alignment of the relevant allocation, and the
offset into that allocation. This avoids missing such bugs, but it also
incurs some false positives when the code does manual integer arithmetic to
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=<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. Specifying this argument multiple times does not overwrite the previous
@ -389,13 +383,6 @@ to Miri failing to detect cases of undefined behavior in a program.
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
`<untagged>` occurring in the message -- this indicates a pointer that was cast from an integer,
so Miri was unable to track this pointer. Note that it is not currently guaranteed that code that
works with `-Zmiri-tag-raw-pointers` also works without `-Zmiri-tag-raw-pointers`, but for the
vast majority of code, this will be the case.
[function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier

View File

@ -340,6 +340,7 @@ fn main() {
Please let us know at <https://github.com/rust-lang/miri/issues/2188> if you rely on this flag."
);
miri_config.allow_ptr_int_transmute = true;
miri_config.provenance_mode = ProvenanceMode::Permissive;
} else if arg == "-Zmiri-disable-abi-check" {
miri_config.check_abi = false;
} else if arg == "-Zmiri-disable-isolation" {
@ -374,20 +375,18 @@ fn main() {
} else if arg == "-Zmiri-panic-on-unsupported" {
miri_config.panic_on_unsupported = true;
} else if arg == "-Zmiri-tag-raw-pointers" {
miri_config.tag_raw = true;
eprintln!("WARNING: `-Zmiri-tag-raw-pointers` has no effect; it is enabled by default");
} else if arg == "-Zmiri-strict-provenance" {
miri_config.provenance_mode = ProvenanceMode::Strict;
miri_config.tag_raw = true;
miri_config.allow_ptr_int_transmute = false;
} else if arg == "-Zmiri-permissive-provenance" {
miri_config.provenance_mode = ProvenanceMode::Permissive;
miri_config.tag_raw = true;
} else if arg == "-Zmiri-mute-stdout-stderr" {
miri_config.mute_stdout_stderr = true;
} else if arg == "-Zmiri-track-raw-pointers" {
eprintln!(
"WARNING: -Zmiri-track-raw-pointers has been renamed to -Zmiri-tag-raw-pointers, the old name is deprecated."
"WARNING: `-Zmiri-track-raw-pointers` has no effect; it is enabled by default"
);
miri_config.tag_raw = true;
} else if let Some(param) = arg.strip_prefix("-Zmiri-seed=") {
if miri_config.seed.is_some() {
panic!("Cannot specify -Zmiri-seed multiple times!");
@ -410,7 +409,7 @@ fn main() {
err
),
};
for id in ids.into_iter().map(miri::PtrId::new) {
for id in ids.into_iter().map(miri::SbTag::new) {
if let Some(id) = id {
miri_config.tracked_pointer_tags.insert(id);
} else {

View File

@ -69,6 +69,9 @@ pub enum NonHaltingDiagnostic {
FreedAlloc(AllocId),
RejectedIsolatedOp(String),
ProgressReport,
Int2Ptr {
details: bool,
},
}
/// Level of Miri specific diagnostics
@ -177,24 +180,6 @@ pub fn report_error<'tcx, 'mir>(
helps.push((Some(*protection_span), "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), msg));
}
if let Some((range, span)) = recently_invalidated {
let msg = format!("tag was later invalidated at offsets {}", HexRange(*range));
helps.push((Some(*span), 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), msg));
}
if let Some((protecting_tag, protecting_tag_span, protection_span)) = protected {
helps.push((Some(*protecting_tag_span), format!("{:?} was protected due to a tag which was created here", protecting_tag)));
helps.push((Some(*protection_span), "this protector is live for this call".to_string()));
}
}
None => {}
}
helps
@ -468,15 +453,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
format!("{op} was made to return an error due to isolation"),
ProgressReport =>
format!("progress report: current operation being executed is here"),
Int2Ptr { .. } => format!("integer-to-pointer cast"),
};
let (title, diag_level) = match e {
RejectedIsolatedOp(_) =>
("operation rejected by isolation", DiagLevel::Warning),
_ => ("tracking was triggered", DiagLevel::Note),
Int2Ptr { .. } => ("integer-to-pointer cast", DiagLevel::Warning),
CreatedPointerTag(..)
| PoppedPointerTag(..)
| CreatedCallId(..)
| CreatedAlloc(..)
| FreedAlloc(..)
| ProgressReport => ("tracking was triggered", DiagLevel::Note),
};
report_msg(this, diag_level, title, vec![msg], vec![], &stacktrace);
let helps = match e {
Int2Ptr { details: true } =>
vec![
(None, format!("this program is using integer-to-pointer casts or (equivalently) `from_exposed_addr`,")),
(None, format!("which means that Miri might miss pointer bugs in this program")),
(None, format!("see https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation")),
(None, format!("to ensure that Miri does not miss bugs in your program, use `with_addr` (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance) instead")),
(None, format!("you can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics")),
(None, format!("alternatively, the `-Zmiri-permissive-provenance` flag disables this warning")),
],
_ => vec![],
};
report_msg(this, diag_level, title, vec![msg], helps, &stacktrace);
}
});
}

View File

@ -96,13 +96,11 @@ pub struct MiriConfig {
/// 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 ids to report about
pub tracked_pointer_tags: HashSet<PtrId>,
pub tracked_pointer_tags: HashSet<SbTag>,
/// 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
pub data_race_detector: bool,
/// Determine if weak memory emulation should be enabled. Requires data race detection to be enabled
@ -146,14 +144,13 @@ impl Default for MiriConfig {
tracked_pointer_tags: HashSet::default(),
tracked_call_ids: HashSet::default(),
tracked_alloc_ids: HashSet::default(),
tag_raw: false,
data_race_detector: true,
weak_memory_emulation: true,
cmpxchg_weak_failure_rate: 0.8, // 80%
measureme_out: None,
panic_on_unsupported: false,
backtrace_style: BacktraceStyle::Short,
provenance_mode: ProvenanceMode::Legacy,
provenance_mode: ProvenanceMode::Default,
mute_stdout_stderr: false,
preemption_rate: 0.01, // 1%
report_progress: None,

View File

@ -5,22 +5,20 @@ use log::trace;
use rand::Rng;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_span::Span;
use rustc_target::abi::{HasDataLayout, Size};
use crate::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ProvenanceMode {
/// Int2ptr casts return pointers with "wildcard" provenance
/// that basically matches that of all exposed pointers
/// (and SB tags, if enabled).
/// We support `expose_addr`/`from_exposed_addr` via "wildcard" provenance.
/// However, we want on `from_exposed_addr` to alert the user of the precision loss.
Default,
/// Like `Default`, but without the warning.
Permissive,
/// Int2ptr casts return pointers with an invalid provenance,
/// i.e., not valid for any memory access.
/// We error on `from_exposed_addr`, ensuring no precision loss.
Strict,
/// Int2ptr casts determine the allocation they point to at cast time.
/// All allocations are considered exposed.
Legacy,
}
pub type GlobalState = RefCell<GlobalStateInner>;
@ -66,6 +64,8 @@ impl<'mir, 'tcx> GlobalStateInner {
let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr);
// Determine the in-bounds provenance for this pointer.
// (This is only called on an actual access, so in-bounds is the only possible kind of provenance.)
let alloc_id = match pos {
Ok(pos) => Some(global_state.int_to_ptr_map[pos].1),
Err(0) => None,
@ -91,21 +91,22 @@ impl<'mir, 'tcx> GlobalStateInner {
}
}?;
// In legacy mode, we consider all allocations exposed.
if global_state.provenance_mode == ProvenanceMode::Legacy
|| global_state.exposed.contains(&alloc_id)
{
Some(alloc_id)
} else {
None
// We only use this provenance if it has been exposed, *and* is still live.
if global_state.exposed.contains(&alloc_id) {
// FIXME: this catches `InterpError`, which we should not usually do.
// We might need a proper fallible API from `memory.rs` to avoid this though.
if ecx.get_alloc_size_and_align(alloc_id, AllocCheck::Live).is_ok() {
return Some(alloc_id);
}
}
None
}
pub fn expose_ptr(ecx: &mut MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId, sb: SbTag) {
let global_state = ecx.machine.intptrcast.get_mut();
// In legacy and strict mode, we don't need this, so we can save some cycles
// by not tracking it.
if global_state.provenance_mode == ProvenanceMode::Permissive {
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
if global_state.provenance_mode != ProvenanceMode::Strict {
trace!("Exposing allocation id {alloc_id:?}");
global_state.exposed.insert(alloc_id);
if ecx.machine.stacked_borrows.is_some() {
@ -120,42 +121,49 @@ impl<'mir, 'tcx> GlobalStateInner {
) -> Pointer<Option<Tag>> {
trace!("Transmuting 0x{:x} to a pointer", addr);
if ecx.machine.allow_ptr_int_transmute {
// When we allow transmutes, treat them like casts.
Self::ptr_from_addr_cast(ecx, addr)
let provenance = if ecx.machine.allow_ptr_int_transmute {
// When we allow transmutes, treat them like casts: generating a wildcard pointer.
Some(Tag::Wildcard)
} else {
// We consider transmuted pointers to be "invalid" (`None` provenance).
Pointer::new(None, Size::from_bytes(addr))
}
// Usually, we consider transmuted pointers to be "invalid" (`None` provenance).
None
};
Pointer::new(provenance, Size::from_bytes(addr))
}
pub fn ptr_from_addr_cast(
ecx: &MiriEvalContext<'mir, 'tcx>,
addr: u64,
) -> Pointer<Option<Tag>> {
) -> InterpResult<'tcx, Pointer<Option<Tag>>> {
trace!("Casting 0x{:x} to a pointer", addr);
let global_state = ecx.machine.intptrcast.borrow();
match global_state.provenance_mode {
ProvenanceMode::Legacy => {
// Determine the allocation this points to at cast time.
let alloc_id = Self::alloc_id_from_addr(ecx, addr);
Pointer::new(
alloc_id.map(|alloc_id| Tag::Concrete { alloc_id, sb: SbTag::Untagged }),
Size::from_bytes(addr),
)
ProvenanceMode::Default => {
// The first time this happens at a particular location, print a warning.
thread_local! {
// `Span` is non-`Send`, so we use a thread-local instead.
static PAST_WARNINGS: RefCell<FxHashSet<Span>> = RefCell::default();
}
PAST_WARNINGS.with_borrow_mut(|past_warnings| {
let first = past_warnings.is_empty();
if past_warnings.insert(ecx.cur_span()) {
// Newly inserted, so first time we see this span.
register_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first });
}
});
}
ProvenanceMode::Strict => {
// We don't support int2ptr casts in this mode (i.e., we treat them like
// transmutes).
Pointer::new(None, Size::from_bytes(addr))
}
ProvenanceMode::Permissive => {
// This is how wildcard pointers are born.
Pointer::new(Some(Tag::Wildcard), Size::from_bytes(addr))
throw_unsup_format!(
"integer-to-pointer casts and `from_exposed_addr` are not supported with `-Zmiri-strict-provenance`; use `with_addr` instead"
)
}
ProvenanceMode::Permissive => {}
}
// This is how wildcard pointers are born.
Ok(Pointer::new(Some(Tag::Wildcard), Size::from_bytes(addr)))
}
fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 {
@ -214,6 +222,8 @@ impl<'mir, 'tcx> GlobalStateInner {
dl.overflowing_offset(base_addr, offset.bytes()).0
}
/// When a pointer is used for a memory access, this computes where in which allocation the
/// access is going.
pub fn abs_ptr_to_rel(
ecx: &MiriEvalContext<'mir, 'tcx>,
ptr: Pointer<Tag>,
@ -224,7 +234,6 @@ impl<'mir, 'tcx> GlobalStateInner {
alloc_id
} else {
// A wildcard pointer.
assert_eq!(ecx.machine.intptrcast.borrow().provenance_mode, ProvenanceMode::Permissive);
GlobalStateInner::alloc_id_from_addr(ecx, addr.bytes())?
};

View File

@ -8,6 +8,7 @@
#![feature(yeet_expr)]
#![feature(is_some_with)]
#![feature(nonzero_ops)]
#![feature(local_key_cell_methods)]
#![warn(rust_2018_idioms)]
#![allow(
clippy::collapsible_else_if,
@ -89,8 +90,8 @@ pub use crate::mono_hash_map::MonoHashMap;
pub use crate::operator::EvalContextExt as OperatorEvalContextExt;
pub use crate::range_map::RangeMap;
pub use crate::stacked_borrows::{
CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, PtrId, SbTag, SbTagExtra,
Stack, Stacks,
CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, SbTag, SbTagExtra, Stack,
Stacks,
};
pub use crate::sync::{CondvarId, EvalContextExt as SyncEvalContextExt, MutexId, RwLockId};
pub use crate::thread::{

View File

@ -140,8 +140,9 @@ pub enum Tag {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(Pointer<Tag>, 24);
// FIXME: this would with in 24bytes but layout optimizations are not smart enough
// #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
// static_assert_size!(Pointer<Option<Tag>>, 24);
//static_assert_size!(Pointer<Option<Tag>>, 24);
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(ScalarMaybeUninit<Tag>, 32);
@ -353,7 +354,6 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
Some(RefCell::new(stacked_borrows::GlobalStateInner::new(
config.tracked_pointer_tags.clone(),
config.tracked_call_ids.clone(),
config.tag_raw,
)))
} else {
None
@ -681,9 +681,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
}
let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr);
let sb_tag = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
stacked_borrows.borrow_mut().base_tag(ptr.provenance)
stacked_borrows.borrow_mut().base_ptr_tag(ptr.provenance)
} else {
SbTag::Untagged
// Value does not matter, SB is disabled
SbTag::default()
};
Pointer::new(
Tag::Concrete { alloc_id: ptr.provenance, sb: sb_tag },
@ -696,7 +697,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
ecx: &MiriEvalContext<'mir, 'tcx>,
addr: u64,
) -> InterpResult<'tcx, Pointer<Option<Self::PointerTag>>> {
Ok(intptrcast::GlobalStateInner::ptr_from_addr_cast(ecx, addr))
intptrcast::GlobalStateInner::ptr_from_addr_cast(ecx, addr)
}
#[inline(always)]

View File

@ -23,42 +23,29 @@ use crate::*;
pub mod diagnostics;
use diagnostics::{AllocHistory, TagHistory};
pub type PtrId = NonZeroU64;
pub type CallId = NonZeroU64;
// Even reading memory can have effects on the stack, so we need a `RefCell` here.
pub type AllocExtra = RefCell<Stacks>;
/// Tracking pointer provenance
#[derive(Copy, Clone, Hash, Eq)]
pub enum SbTag {
Tagged(PtrId),
Untagged,
}
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct SbTag(NonZeroU64);
impl SbTag {
fn as_u64(self) -> u64 {
match self {
SbTag::Tagged(id) => id.get(),
SbTag::Untagged => 0,
}
pub fn new(i: u64) -> Option<Self> {
NonZeroU64::new(i).map(SbTag)
}
}
impl PartialEq for SbTag {
fn eq(&self, other: &Self) -> bool {
// The codegen for the derived Partialeq is bad here and includes a branch.
// Since this code is extremely hot, this is optimized here.
// https://github.com/rust-lang/rust/issues/49892
self.as_u64() == other.as_u64()
// The default to be used when SB is disabled
pub fn default() -> Self {
Self::new(1).unwrap()
}
}
impl fmt::Debug for SbTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SbTag::Tagged(id) => write!(f, "<{}>", id),
SbTag::Untagged => write!(f, "<untagged>"),
}
write!(f, "<{}>", self.0)
}
}
@ -73,7 +60,7 @@ pub enum SbTagExtra {
impl fmt::Debug for SbTagExtra {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SbTagExtra::Concrete(tag) => write!(f, "{tag:?}"),
SbTagExtra::Concrete(pid) => write!(f, "{pid:?}"),
SbTagExtra::Wildcard => write!(f, "<wildcard>"),
}
}
@ -82,7 +69,7 @@ impl fmt::Debug for SbTagExtra {
impl SbTagExtra {
fn and_then<T>(self, f: impl FnOnce(SbTag) -> Option<T>) -> Option<T> {
match self {
SbTagExtra::Concrete(tag) => f(tag),
SbTagExtra::Concrete(pid) => f(pid),
SbTagExtra::Wildcard => None,
}
}
@ -130,15 +117,15 @@ pub struct Stack {
/// Used *mostly* as a stack; never empty.
/// Invariants:
/// * Above a `SharedReadOnly` there can only be more `SharedReadOnly`.
/// * Except for `Untagged`, no tag occurs in the stack more than once.
/// * No tag occurs in the stack more than once.
borrows: Vec<Item>,
/// If this is `Some(id)`, then the actual current stack is unknown. This can happen when
/// wildcard pointers are used to access this location. What we do know is that `borrows` are at
/// the top of the stack, and below it are arbitrarily many items whose `tag` is either
/// `Untagged` or strictly less than `id`.
/// the top of the stack, and below it are arbitrarily many items whose `tag` is strictly less
/// than `id`.
/// When the bottom is unknown, `borrows` always has a `SharedReadOnly` or `Unique` at the bottom;
/// we never have the unknown-to-known boundary in an SRW group.
unknown_bottom: Option<PtrId>,
unknown_bottom: Option<SbTag>,
}
/// Extra per-allocation state.
@ -156,21 +143,19 @@ pub struct Stacks {
#[derive(Debug)]
pub struct GlobalStateInner {
/// Next unused pointer ID (tag).
next_ptr_id: PtrId,
next_ptr_tag: SbTag,
/// Table storing the "base" tag for each allocation.
/// The base tag is the one used for the initial pointer.
/// We need this in a separate table to handle cyclic statics.
base_ptr_ids: FxHashMap<AllocId, SbTag>,
base_ptr_tags: FxHashMap<AllocId, SbTag>,
/// Next unused call ID (for protectors).
next_call_id: CallId,
/// Those call IDs corresponding to functions that are still running.
active_calls: FxHashSet<CallId>,
/// The pointer ids to trace
tracked_pointer_tags: HashSet<PtrId>,
tracked_pointer_tags: HashSet<SbTag>,
/// The call ids to trace
tracked_call_ids: HashSet<CallId>,
/// Whether to track raw pointers.
tag_raw: bool,
}
/// We need interior mutable access to the global state.
@ -219,28 +204,23 @@ impl fmt::Display for RefKind {
/// Utilities for initialization and ID generation
impl GlobalStateInner {
pub fn new(
tracked_pointer_tags: HashSet<PtrId>,
tracked_call_ids: HashSet<CallId>,
tag_raw: bool,
) -> Self {
pub fn new(tracked_pointer_tags: HashSet<SbTag>, tracked_call_ids: HashSet<CallId>) -> Self {
GlobalStateInner {
next_ptr_id: NonZeroU64::new(1).unwrap(),
base_ptr_ids: FxHashMap::default(),
next_ptr_tag: SbTag(NonZeroU64::new(1).unwrap()),
base_ptr_tags: FxHashMap::default(),
next_call_id: NonZeroU64::new(1).unwrap(),
active_calls: FxHashSet::default(),
tracked_pointer_tags,
tracked_call_ids,
tag_raw,
}
}
fn new_ptr(&mut self) -> PtrId {
let id = self.next_ptr_id;
fn new_ptr(&mut self) -> SbTag {
let id = self.next_ptr_tag;
if self.tracked_pointer_tags.contains(&id) {
register_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(id));
register_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(id.0));
}
self.next_ptr_id = NonZeroU64::new(id.get() + 1).unwrap();
self.next_ptr_tag = SbTag(NonZeroU64::new(id.0.get() + 1).unwrap());
id
}
@ -263,22 +243,14 @@ impl GlobalStateInner {
self.active_calls.contains(&id)
}
pub fn base_tag(&mut self, id: AllocId) -> SbTag {
self.base_ptr_ids.get(&id).copied().unwrap_or_else(|| {
let tag = SbTag::Tagged(self.new_ptr());
pub fn base_ptr_tag(&mut self, id: AllocId) -> SbTag {
self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| {
let tag = self.new_ptr();
trace!("New allocation {:?} has base tag {:?}", id, tag);
self.base_ptr_ids.try_insert(id, tag).unwrap();
self.base_ptr_tags.try_insert(id, tag).unwrap();
tag
})
}
pub fn base_tag_untagged(&mut self, id: AllocId) -> SbTag {
trace!("New allocation {:?} has no base tag (untagged)", id);
let tag = SbTag::Untagged;
// This must only be done on new allocations.
self.base_ptr_ids.try_insert(id, tag).unwrap();
tag
}
}
/// Error reporting
@ -368,10 +340,7 @@ impl<'tcx> Stack {
// Couldn't find it in the stack; but if there is an unknown bottom it might be there.
let found = self.unknown_bottom.is_some_and(|&unknown_limit| {
match tag {
SbTag::Tagged(tag_id) => tag_id < unknown_limit, // unknown_limit is an upper bound for what can be in the unknown bottom.
SbTag::Untagged => true, // yeah whatever
}
tag.0 < unknown_limit.0 // unknown_limit is an upper bound for what can be in the unknown bottom.
});
if found { Ok(None) } else { Err(()) }
}
@ -412,37 +381,29 @@ impl<'tcx> Stack {
/// Within `provoking_access, the `AllocRange` refers the entire operation, and
/// the `Size` refers to the specific location in the `AllocRange` that we are
/// currently checking.
fn check_protector(
fn item_popped(
item: &Item,
provoking_access: Option<(SbTagExtra, AllocRange, Size, AccessKind)>, // just for debug printing and error messages
global: &GlobalStateInner,
alloc_history: &mut AllocHistory,
) -> InterpResult<'tcx> {
if let SbTag::Tagged(id) = item.tag {
if global.tracked_pointer_tags.contains(&id) {
register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(
*item,
provoking_access.map(|(tag, _alloc_range, _size, access)| (tag, access)),
));
}
if global.tracked_pointer_tags.contains(&item.tag) {
register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(
*item,
provoking_access.map(|(tag, _alloc_range, _size, access)| (tag, access)),
));
}
if let Some(call) = item.protector {
if global.is_active(call) {
if let Some((tag, alloc_range, offset, _access)) = provoking_access {
if let Some((tag, _alloc_range, _offset, _access)) = provoking_access {
Err(err_sb_ub(
format!(
"not granting access to tag {:?} because incompatible item is protected: {:?}",
tag, item
),
None,
tag.and_then(|tag| {
alloc_history.get_logs_relevant_to(
tag,
alloc_range,
offset,
Some(item.tag),
)
}),
tag.and_then(|tag| alloc_history.get_logs_relevant_to(tag, Some(item.tag))),
))?
} else {
Err(err_sb_ub(
@ -495,7 +456,7 @@ impl<'tcx> Stack {
};
for item in self.borrows.drain(first_incompatible_idx..).rev() {
trace!("access: popping item {:?}", item);
Stack::check_protector(
Stack::item_popped(
&item,
Some((tag, alloc_range, offset, access)),
global,
@ -524,7 +485,7 @@ impl<'tcx> Stack {
if item.perm == Permission::Unique {
trace!("access: disabling item {:?}", item);
Stack::check_protector(
Stack::item_popped(
item,
Some((tag, alloc_range, offset, access)),
global,
@ -544,21 +505,19 @@ impl<'tcx> Stack {
for item in &self.borrows {
// Skip disabled items, they cannot be matched anyway.
if !matches!(item.perm, Permission::Disabled) {
if let SbTag::Tagged(tag) = item.tag {
// We are looking for a strict upper bound, so add 1 to this tag.
max = cmp::max(tag.checked_add(1).unwrap(), max);
}
// We are looking for a strict upper bound, so add 1 to this tag.
max = cmp::max(item.tag.0.checked_add(1).unwrap(), max);
}
}
if let Some(unk) = self.unknown_bottom {
max = cmp::max(unk, max);
max = cmp::max(unk.0, max);
}
// Use `max` as new strict upper bound for everything.
trace!(
"access: forgetting stack to upper bound {max} due to wildcard or unknown access"
);
self.borrows.clear();
self.unknown_bottom = Some(max);
self.unknown_bottom = Some(SbTag(max));
}
// Done.
@ -570,7 +529,7 @@ impl<'tcx> Stack {
fn dealloc(
&mut self,
tag: SbTagExtra,
(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,
alloc_history: &mut AllocHistory,
exposed_tags: &FxHashSet<SbTag>,
@ -582,13 +541,13 @@ impl<'tcx> Stack {
tag, alloc_id,
),
None,
tag.and_then(|tag| alloc_history.get_logs_relevant_to(tag, alloc_range, offset, None)),
tag.and_then(|tag| alloc_history.get_logs_relevant_to(tag, None)),
)
})?;
// Step 2: Remove all items. Also checks for protectors.
for item in self.borrows.drain(..).rev() {
Stack::check_protector(&item, None, global, alloc_history)?;
Stack::item_popped(&item, None, global, alloc_history)?;
}
Ok(())
}
@ -636,7 +595,7 @@ impl<'tcx> Stack {
// (for all we know, it might join an SRW group inside the unknown).
trace!("reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown");
self.borrows.clear();
self.unknown_bottom = Some(global.next_ptr_id);
self.unknown_bottom = Some(global.next_ptr_tag);
return Ok(());
};
@ -730,30 +689,9 @@ impl Stacks {
// not through a pointer). That is, whenever we directly write to a local, this will pop
// everything else off the stack, invalidating all previous pointers,
// and in particular, *all* raw pointers.
MemoryKind::Stack => (extra.base_tag(id), Permission::Unique),
// `Global` memory can be referenced by global pointers from `tcx`.
// Thus we call `global_base_ptr` such that the global pointers get the same tag
// as what we use here.
// `ExternStatic` is used for extern statics, so the same reasoning applies.
// The others are various forms of machine-managed special global memory, and we can get
// away with precise tracking there.
// The base pointer is not unique, so the base permission is `SharedReadWrite`.
MemoryKind::CallerLocation
| MemoryKind::Machine(
MiriMemoryKind::Global
| MiriMemoryKind::ExternStatic
| MiriMemoryKind::Tls
| MiriMemoryKind::Runtime
| MiriMemoryKind::Machine,
) => (extra.base_tag(id), Permission::SharedReadWrite),
// Heap allocations we only track precisely when raw pointers are tagged, for now.
MemoryKind::Machine(
MiriMemoryKind::Rust | MiriMemoryKind::C | MiriMemoryKind::WinHeap,
) => {
let tag =
if extra.tag_raw { extra.base_tag(id) } else { extra.base_tag_untagged(id) };
(tag, Permission::SharedReadWrite)
}
MemoryKind::Stack => (extra.base_ptr_tag(id), Permission::Unique),
// Everything else is shared by default.
_ => (extra.base_ptr_tag(id), Permission::SharedReadWrite),
};
let mut stacks = Stacks::new(size, perm, base_tag);
stacks.history.log_creation(
@ -1043,15 +981,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
};
// Compute new borrow.
let new_tag = {
let mem_extra = this.machine.stacked_borrows.as_mut().unwrap().get_mut();
match kind {
// Give up tracking for raw pointers.
RefKind::Raw { .. } if !mem_extra.tag_raw => SbTag::Untagged,
// All other pointers are properly tracked.
_ => SbTag::Tagged(mem_extra.new_ptr()),
}
};
let new_tag = this.machine.stacked_borrows.as_mut().unwrap().get_mut().new_ptr();
// Reborrow.
let alloc_id = this.reborrow(&place, size, kind, new_tag, protect)?;

View File

@ -32,7 +32,6 @@ struct Protection {
#[derive(Clone, Debug)]
struct Event {
time: usize,
parent: Option<SbTag>,
tag: SbTag,
range: AllocRange,
@ -46,12 +45,6 @@ pub enum TagHistory {
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)>,
},
}
impl AllocHistory {
@ -72,7 +65,7 @@ impl AllocHistory {
current_span: &mut CurrentSpan<'_, '_, '_>,
) {
let span = current_span.get();
self.creations.push(Event { parent, tag, range, span, time: self.current_time });
self.creations.push(Event { parent, tag, range, span });
self.current_time += 1;
}
@ -83,7 +76,7 @@ impl AllocHistory {
current_span: &mut CurrentSpan<'_, '_, '_>,
) {
let span = current_span.get();
self.invalidations.push(Event { parent: None, tag, range, span, time: self.current_time });
self.invalidations.push(Event { parent: None, tag, range, span });
self.current_time += 1;
}
@ -101,8 +94,6 @@ impl AllocHistory {
pub fn get_logs_relevant_to(
&self,
tag: SbTag,
alloc_range: AllocRange,
offset: Size,
protector_tag: Option<SbTag>,
) -> Option<TagHistory> {
let protected = protector_tag
@ -125,74 +116,17 @@ impl AllocHistory {
})
});
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(&self.creations)?,
invalidated: get_matching(&self.invalidations),
protected,
let get_matching = |events: &[Event]| {
events.iter().rev().find_map(|event| {
if event.tag == tag { Some((event.range, event.span.data())) } else { None }
})
} else {
let mut created_time = 0;
// Find the most recently created tag that satsfies this offset
let recently_created = self.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 = recently_created.and_then(|(_created_range, created_span)| {
self.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
}
})
});
// Find the most recent invalidation of this tag which post-dates the creation
let recently_invalidated = recently_created.and_then(|_| {
self.invalidations
.iter()
.rev()
.take_while(|event| event.time > created_time)
.find_map(|event| {
if event.tag == tag
&& offset >= event.range.start
&& offset < event.range.end()
{
Some((event.range, event.span.data()))
} else {
None
}
})
});
Some(TagHistory::Untagged {
recently_created,
matching_created,
recently_invalidated,
protected,
})
}
};
Some(TagHistory::Tagged {
tag,
created: get_matching(&self.creations)?,
invalidated: get_matching(&self.invalidations),
protected,
})
}
/// Report a descriptive error when `new` could not be granted from `derived_from`.
@ -214,9 +148,7 @@ impl AllocHistory {
err_sb_ub(
format!("{}{}", action, error_cause(stack, derived_from)),
Some(operation_summary("a reborrow", alloc_id, alloc_range)),
derived_from.and_then(|derived_from| {
self.get_logs_relevant_to(derived_from, alloc_range, error_offset, None)
}),
derived_from.and_then(|derived_from| self.get_logs_relevant_to(derived_from, None)),
)
}
@ -238,7 +170,7 @@ impl AllocHistory {
err_sb_ub(
format!("{}{}", action, error_cause(stack, tag)),
Some(operation_summary("an access", alloc_id, alloc_range)),
tag.and_then(|tag| self.get_logs_relevant_to(tag, alloc_range, error_offset, None)),
tag.and_then(|tag| self.get_logs_relevant_to(tag, None)),
)
}
}

View File

@ -114,20 +114,16 @@ def test_cargo_miri_test():
default_ref = "test.cross-target.stdout.ref" if is_foreign else "test.default.stdout.ref"
filter_ref = "test.filter.cross-target.stdout.ref" if is_foreign else "test.filter.stdout.ref"
# macOS needs permissive provenance inside getrandom.
test("`cargo miri test`",
cargo_miri("test"),
default_ref, "test.stderr-empty.ref",
env={'MIRIFLAGS': "-Zmiri-seed=feed"},
env={'MIRIFLAGS': "-Zmiri-permissive-provenance -Zmiri-seed=feed"},
)
test("`cargo miri test` (no isolation, no doctests)",
cargo_miri("test") + ["--bins", "--tests"], # no `--lib`, we disabled that in `Cargo.toml`
"test.cross-target.stdout.ref", "test.stderr-empty.ref",
env={'MIRIFLAGS': "-Zmiri-disable-isolation"},
)
test("`cargo miri test` (raw-ptr tracking)",
cargo_miri("test"),
default_ref, "test.stderr-empty.ref",
env={'MIRIFLAGS': "-Zmiri-tag-raw-pointers"},
env={'MIRIFLAGS': "-Zmiri-permissive-provenance -Zmiri-disable-isolation"},
)
test("`cargo miri test` (with filter)",
cargo_miri("test") + ["--", "--format=pretty", "le1"],
@ -136,6 +132,7 @@ def test_cargo_miri_test():
test("`cargo miri test` (test target)",
cargo_miri("test") + ["--test", "test", "--", "--format=pretty"],
"test.test-target.stdout.ref", "test.stderr-empty.ref",
env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
)
test("`cargo miri test` (bin target)",
cargo_miri("test") + ["--bin", "cargo-miri-test", "--", "--format=pretty"],
@ -153,11 +150,13 @@ def test_cargo_miri_test():
test("`cargo miri test` (custom target dir)",
cargo_miri("test") + ["--target-dir=custom-test"],
default_ref, "test.stderr-empty.ref",
env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
)
del os.environ["CARGO_TARGET_DIR"] # this overrides `build.target-dir` passed by `--config`, so unset it
test("`cargo miri test` (config-cli)",
cargo_miri("test") + ["--config=build.target-dir=\"config-cli\"", "-Zunstable-options"],
default_ref, "test.stderr-empty.ref",
env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
)
os.chdir(os.path.dirname(os.path.realpath(__file__)))

View File

@ -4,6 +4,6 @@ extern "Rust" {
fn main() {
unsafe {
miri_get_backtrace(2, 0 as *mut _); //~ ERROR unsupported operation: unknown `miri_get_backtrace` flags 2
miri_get_backtrace(2, std::ptr::null_mut()); //~ ERROR unsupported operation: unknown `miri_get_backtrace` flags 2
}
}

View File

@ -1,8 +1,8 @@
error: unsupported operation: unknown `miri_get_backtrace` flags 2
--> $DIR/bad-backtrace-flags.rs:LL:CC
|
LL | miri_get_backtrace(2, 0 as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_get_backtrace` flags 2
LL | miri_get_backtrace(2, std::ptr::null_mut());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_get_backtrace` flags 2
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support

View File

@ -4,6 +4,6 @@ extern "Rust" {
fn main() {
unsafe {
miri_resolve_frame(0 as *mut _, 0); //~ ERROR null pointer is not a valid pointer for this operation
miri_resolve_frame(std::ptr::null_mut(), 0); //~ ERROR null pointer is not a valid pointer for this operation
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: null pointer is not a valid pointer for this operation
--> $DIR/bad-backtrace-ptr.rs:LL:CC
|
LL | miri_resolve_frame(0 as *mut _, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ null pointer is not a valid pointer for this operation
LL | miri_resolve_frame(std::ptr::null_mut(), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ null pointer is not a valid pointer for this operation
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View File

@ -15,7 +15,7 @@ extern "Rust" {
fn main() {
unsafe {
let mut buf = vec![0 as *mut _; miri_backtrace_size(0)];
let mut buf = vec![std::ptr::null_mut(); miri_backtrace_size(0)];
miri_get_backtrace(1, buf.as_mut_ptr());

View File

@ -6,11 +6,11 @@ extern "Rust" {
fn main() {
unsafe {
let mut buf = vec![0 as *mut _; miri_backtrace_size(0)];
let mut buf = vec![std::ptr::null_mut(); miri_backtrace_size(0)];
miri_get_backtrace(1, buf.as_mut_ptr());
// miri_resolve_frame_names will error from an invalid backtrace before it will from invalid flags
miri_resolve_frame_names(buf[0], 2, 0 as *mut _, 0 as *mut _); //~ ERROR unsupported operation: unknown `miri_resolve_frame_names` flags 2
miri_resolve_frame_names(buf[0], 2, std::ptr::null_mut(), std::ptr::null_mut()); //~ ERROR unsupported operation: unknown `miri_resolve_frame_names` flags 2
}
}

View File

@ -1,8 +1,8 @@
error: unsupported operation: unknown `miri_resolve_frame_names` flags 2
--> $DIR/bad-backtrace-resolve-names-flags.rs:LL:CC
|
LL | ... miri_resolve_frame_names(buf[0], 2, 0 as *mut _, 0 as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_resolve_frame_names` flags 2
LL | ... miri_resolve_frame_names(buf[0], 2, std::ptr::null_mut(), std::ptr::null_mut());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_resolve_frame_names` flags 2
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support

View File

@ -7,9 +7,12 @@
#[thread_local]
static mut TLS: u8 = 0;
struct SendRaw(*const u8);
unsafe impl Send for SendRaw {}
fn main() {
unsafe {
let dangling_ptr = std::thread::spawn(|| &TLS as *const u8 as usize).join().unwrap();
let _val = *(dangling_ptr as *const u8); //~ ERROR dereferenced after this allocation got freed
let dangling_ptr = std::thread::spawn(|| SendRaw(&TLS as *const u8)).join().unwrap();
let _val = *dangling_ptr.0; //~ ERROR dereferenced after this allocation got freed
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: pointer to ALLOC was dereferenced after this allocation got freed
--> $DIR/thread_local_static_dealloc.rs:LL:CC
|
LL | let _val = *(dangling_ptr as *const u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer to ALLOC was dereferenced after this allocation got freed
LL | let _val = *dangling_ptr.0;
| ^^^^^^^^^^^^^^^ pointer to ALLOC was dereferenced after this allocation got freed
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View File

@ -1,5 +1,5 @@
// This should fail even without validation.
// compile-flags: -Zmiri-disable-validation
// compile-flags: -Zmiri-disable-validation -Zmiri-permissive-provenance
fn main() {
let x = 16usize as *const u32;

View File

@ -1,5 +1,5 @@
// This should fail even without validation, but some MIR opts mask the error
// compile-flags: -Zmiri-disable-validation -Zmir-opt-level=0
// compile-flags: -Zmiri-disable-validation -Zmir-opt-level=0 -Zmiri-permissive-provenance
static mut LEAK: usize = 0;
@ -10,7 +10,7 @@ fn fill(v: &mut i32) {
}
fn evil() {
unsafe { &mut *(LEAK as *mut i32) }; //~ ERROR dereferenced after this allocation got freed
unsafe { &mut *(LEAK as *mut i32) }; //~ ERROR is not a valid pointer
}
fn main() {
@ -21,6 +21,6 @@ fn main() {
_y = x;
}
// Now we use a pointer to `x` which is no longer in scope, and thus dead (even though the
// `main` stack frame still exists).
// `main` stack frame still exists). We even try going through a `usize` for extra sneakiness!
evil();
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: pointer to ALLOC was dereferenced after this allocation got freed
error: Undefined Behavior: dereferencing pointer failed: $HEX is not a valid pointer
--> $DIR/storage_dead_dangling.rs:LL:CC
|
LL | unsafe { &mut *(LEAK as *mut i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^ pointer to ALLOC was dereferenced after this allocation got freed
| ^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: $HEX is not a valid pointer
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View File

@ -1,3 +1,5 @@
// compile-flags: -Zmiri-permissive-provenance
fn main() {
let p = 44 as *const i32;
let x = unsafe { *p }; //~ ERROR is not a valid pointer

View File

@ -1,4 +1,5 @@
// error-pattern: pointer arithmetic failed: null pointer is not a valid pointer
// compile-flags: -Zmiri-permissive-provenance
fn main() {
let x = 0 as *mut i32;

View File

@ -1,4 +1,5 @@
// error-pattern: is not a valid pointer
// compile-flags: -Zmiri-permissive-provenance
fn main() {
// Can't offset an integer pointer by non-zero offset.

View File

@ -1,4 +1,5 @@
// error-pattern: is not a valid pointer
// compile-flags: -Zmiri-permissive-provenance
fn main() {
let ptr = Box::into_raw(Box::new(0u32));

View File

@ -1,20 +0,0 @@
error: Undefined Behavior: dereferencing pointer failed: $HEX is not a valid pointer
--> $DIR/permissive_provenance_transmute.rs:LL:CC
|
LL | let _val = *left_ptr;
| ^^^^^^^^^ dereferencing pointer failed: $HEX is not a valid pointer
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: inside `deref` at $DIR/permissive_provenance_transmute.rs:LL:CC
note: inside `main` at $DIR/permissive_provenance_transmute.rs:LL:CC
--> $DIR/permissive_provenance_transmute.rs:LL:CC
|
LL | deref(ptr1, ptr2.with_addr(ptr1.addr()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: dereferencing pointer failed: $HEX is not a valid pointer
--> $DIR/strict_provenance_transmute.rs:LL:CC
--> $DIR/provenance_transmute.rs:LL:CC
|
LL | let _val = *left_ptr;
| ^^^^^^^^^ dereferencing pointer failed: $HEX is not a valid pointer
@ -7,9 +7,9 @@ LL | let _val = *left_ptr;
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: inside `deref` at $DIR/strict_provenance_transmute.rs:LL:CC
note: inside `main` at $DIR/strict_provenance_transmute.rs:LL:CC
--> $DIR/strict_provenance_transmute.rs:LL:CC
= note: inside `deref` at $DIR/provenance_transmute.rs:LL:CC
note: inside `main` at $DIR/provenance_transmute.rs:LL:CC
--> $DIR/provenance_transmute.rs:LL:CC
|
LL | deref(ptr1, ptr2.with_addr(ptr1.addr()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1,22 +0,0 @@
// compile-flags: -Zmiri-disable-stacked-borrows
// normalize-stderr-test: "offset -[0-9]+" -> "offset -XX"
#![feature(strict_provenance)]
use std::ptr;
// Make sure that with legacy provenance, the allocation id of
// a casted pointer is determined at cast-time
fn main() {
let x: i32 = 0;
let y: i32 = 1;
let x_ptr = &x as *const i32;
let y_ptr = &y as *const i32;
let x_usize = x_ptr.expose_addr();
let y_usize = y_ptr.expose_addr();
let ptr = ptr::from_exposed_addr::<i32>(y_usize);
let ptr = ptr.with_addr(x_usize);
assert_eq!(unsafe { *ptr }, 0); //~ ERROR is out-of-bounds
}

View File

@ -1,15 +0,0 @@
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 4, so pointer to 4 bytes starting at offset -XX is out-of-bounds
--> $DIR/ptr_legacy_provenance.rs:LL:CC
|
LL | assert_eq!(unsafe { *ptr }, 0);
| ^^^^ dereferencing pointer failed: ALLOC has size 4, so pointer to 4 bytes starting at offset -XX is out-of-bounds
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: inside `main` at $DIR/ptr_legacy_provenance.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,6 @@
// compile-flags: -Zmiri-strict-provenance
fn main() {
let addr = &0 as *const i32 as usize;
let _ptr = addr as *const i32; //~ ERROR integer-to-pointer casts and `from_exposed_addr` are not supported
}

View File

@ -0,0 +1,14 @@
error: unsupported operation: integer-to-pointer casts and `from_exposed_addr` are not supported with `-Zmiri-strict-provenance`; use `with_addr` instead
--> $DIR/strict_provenance_cast.rs:LL:CC
|
LL | let _ptr = addr as *const i32;
| ^^^^^^^^^^^^^^^^^^ integer-to-pointer casts and `from_exposed_addr` are not supported with `-Zmiri-strict-provenance`; use `with_addr` instead
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
= note: inside `main` at $DIR/strict_provenance_cast.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -1,27 +0,0 @@
// compile-flags: -Zmiri-strict-provenance
#![feature(strict_provenance)]
use std::mem;
// This is the example from
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/286#issuecomment-1085144431>.
unsafe fn deref(left: *const u8, right: *const u8) {
let left_int: usize = mem::transmute(left);
let right_int: usize = mem::transmute(right);
if left_int == right_int {
// The compiler is allowed to replace `left_int` by `right_int` here...
let left_ptr: *const u8 = mem::transmute(left_int);
// ...which however means here it could be dereferencing the wrong pointer.
let _val = *left_ptr; //~ERROR dereferencing pointer failed
}
}
fn main() {
let ptr1 = &0u8 as *const u8;
let ptr2 = &1u8 as *const u8;
unsafe {
// Two pointers with the same address but different provenance.
deref(ptr1, ptr2.with_addr(ptr1.addr()));
}
}

View File

@ -1,20 +1,20 @@
error: Undefined Behavior: trying to reborrow <untagged> for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: trying to reborrow <TAG> for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> $DIR/aliasing_mut3.rs:LL:CC
|
LL | pub fn safe(_x: &mut i32, _y: &i32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| trying to reborrow <untagged> for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| trying to reborrow <TAG> for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of a reborrow at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/aliasing_mut3.rs:LL:CC
|
LL | safe_raw(xraw, xshr);
| ^^^^
help: tag was later invalidated at offsets [0x0..0x4]
help: <TAG> was later invalidated at offsets [0x0..0x4]
--> $DIR/aliasing_mut3.rs:LL:CC
|
LL | pub fn safe(_x: &mut i32, _y: &i32) {}

View File

@ -8,7 +8,7 @@ fn demo_mut_advanced_unique(mut our: Box<i32>) -> i32 {
unknown_code_2();
// We know this will return 5
*our //~ ERROR borrow stack
*our
}
// Now comes the evil context
@ -24,7 +24,7 @@ fn unknown_code_1(x: &i32) {
fn unknown_code_2() {
unsafe {
*LEAK = 7;
*LEAK = 7; //~ ERROR borrow stack
}
}

View File

@ -1,31 +1,30 @@
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | *our
| ^^^^
| |
| attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
LL | *LEAK = 7;
| ^^^^^^^^^
| |
| attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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 created by a retag at offsets [0x0..0x4]
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | / fn demo_mut_advanced_unique(mut our: Box<i32>) -> i32 {
LL | | unknown_code_1(&*our);
LL | |
LL | | // This "re-asserts" uniqueness of the reference: After writing, we know
... |
LL | | *our
LL | | }
| |_^
LL | LEAK = x as *const _ as *mut _;
| ^
help: <TAG> was later invalidated at offsets [0x0..0x4]
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | *LEAK = 7;
| ^^^^^^^^^
= note: inside `demo_mut_advanced_unique` at $DIR/box_exclusive_violation1.rs:LL:CC
LL | *our = 5;
| ^^^^^^^^
= note: inside `unknown_code_2` at $DIR/box_exclusive_violation1.rs:LL:CC
note: inside `demo_mut_advanced_unique` at $DIR/box_exclusive_violation1.rs:LL:CC
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | unknown_code_2();
| ^^^^^^^^^^^^^^^^
note: inside `main` at $DIR/box_exclusive_violation1.rs:LL:CC
--> $DIR/box_exclusive_violation1.rs:LL:CC
|

View File

@ -5,7 +5,7 @@
use std::mem;
union HiddenRef {
// We avoid retagging at this type, so shared vs mutable does not matter.
// We avoid retagging at this type, and we only read, so shared vs mutable does not matter.
r: &'static i32,
}

View File

@ -1,20 +1,20 @@
error: Undefined Behavior: attempting a read access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> $DIR/illegal_read6.rs:LL:CC
|
LL | let _val = *raw;
| ^^^^
| |
| attempting a read access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/illegal_read6.rs:LL:CC
|
LL | let raw = x as *mut _;
| ^
help: tag was later invalidated at offsets [0x0..0x4]
help: <TAG> was later invalidated at offsets [0x0..0x4]
--> $DIR/illegal_read6.rs:LL:CC
|
LL | let x = &mut *x; // kill `raw`

View File

@ -3,7 +3,7 @@ fn main() {
let xref = &*target;
{
let x: *mut u32 = xref as *const _ as *mut _;
unsafe { *x = 42 }; // invalidates shared ref, activates raw
unsafe { *x = 42 }; //~ ERROR only grants SharedReadOnly permission
}
let _x = *xref; //~ ERROR borrow stack
let _x = *xref;
}

View File

@ -1,24 +1,19 @@
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
--> $DIR/illegal_write1.rs:LL:CC
|
LL | let _x = *xref;
| ^^^^^
| |
| attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
LL | unsafe { *x = 42 };
| ^^^^^^^
| |
| attempting a write access using <TAG> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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 created by a retag at offsets [0x0..0x4]
--> $DIR/illegal_write1.rs:LL:CC
|
LL | let xref = &*target;
| ^^^^^^^^
help: <TAG> was later invalidated at offsets [0x0..0x4]
--> $DIR/illegal_write1.rs:LL:CC
|
LL | unsafe { *x = 42 }; // invalidates shared ref, activates raw
| ^^^^^^^
LL | let x: *mut u32 = xref as *const _ as *mut _;
| ^^^^
= note: inside `main` at $DIR/illegal_write1.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,20 +1,20 @@
error: Undefined Behavior: attempting a write access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> $DIR/illegal_write2.rs:LL:CC
|
LL | unsafe { *target2 = 13 };
| ^^^^^^^^^^^^^
| |
| attempting a write access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/illegal_write2.rs:LL:CC
|
LL | let target2 = target as *mut _;
| ^^^^^^
help: tag was later invalidated at offsets [0x0..0x4]
help: <TAG> was later invalidated at offsets [0x0..0x4]
--> $DIR/illegal_write2.rs:LL:CC
|
LL | drop(&mut *target); // reborrow

View File

@ -1,15 +1,15 @@
error: Undefined Behavior: attempting a write access using <untagged> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
--> $DIR/illegal_write3.rs:LL:CC
|
LL | unsafe { *ptr = 42 };
| ^^^^^^^^^
| |
| attempting a write access using <untagged> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
| attempting a write access using <TAG> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/illegal_write3.rs:LL:CC
|
LL | let ptr = r#ref as *const _ as *mut _; // raw ptr, with raw tag

View File

@ -6,8 +6,8 @@ fn main() {
// Even just creating it unfreezes.
let raw = &mut target as *mut _; // let this leak to raw
let reference = unsafe { &*raw }; // freeze
let ptr = reference as *const _ as *mut i32; // raw ptr, with raw tag
let _mut_ref: &mut i32 = unsafe { mem::transmute(ptr) }; // &mut, with raw tag
let _ptr = reference as *const _ as *mut i32; // raw ptr, with raw tag
let _mut_ref: &mut i32 = unsafe { mem::transmute(raw) }; // &mut, with raw tag
// Now we retag, making our ref top-of-stack -- and, in particular, unfreezing.
let _val = *reference; //~ ERROR borrow stack
}

View File

@ -17,7 +17,7 @@ LL | let reference = unsafe { &*raw }; // freeze
help: <TAG> was later invalidated at offsets [0x0..0x4]
--> $DIR/illegal_write4.rs:LL:CC
|
LL | let _mut_ref: &mut i32 = unsafe { mem::transmute(ptr) }; // &mut, with raw tag
LL | let _mut_ref: &mut i32 = unsafe { mem::transmute(raw) }; // &mut, with raw tag
| ^^^^^^^^^^^^^^^^^^^
= note: inside `main` at $DIR/illegal_write4.rs:LL:CC

View File

@ -1,17 +1,17 @@
error: Undefined Behavior: not granting access to tag <untagged> because incompatible item is protected: [Unique for <TAG> (call ID)]
error: Undefined Behavior: not granting access to tag <TAG> because incompatible item is protected: [Unique for <TAG> (call ID)]
--> $DIR/illegal_write6.rs:LL:CC
|
LL | unsafe { *y = 2 };
| ^^^^^^ not granting access to tag <untagged> because incompatible item is protected: [Unique for <TAG> (call ID)]
| ^^^^^^ not granting access to tag <TAG> because incompatible item is protected: [Unique for <TAG> (call ID)]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/illegal_write6.rs:LL:CC
|
LL | let p = x as *mut u32;
| ^
help: <TAG> was protected due to a tag which was created here
help: <TAG> was protected due to <TAG> which was created here
--> $DIR/illegal_write6.rs:LL:CC
|
LL | foo(x, p);

View File

@ -1,17 +1,17 @@
error: Undefined Behavior: not granting access to tag <untagged> because incompatible item is protected: [Unique for <TAG> (call ID)]
error: Undefined Behavior: not granting access to tag <TAG> because incompatible item is protected: [Unique for <TAG> (call ID)]
--> $DIR/invalidate_against_barrier1.rs:LL:CC
|
LL | let _val = unsafe { *x };
| ^^ not granting access to tag <untagged> because incompatible item is protected: [Unique for <TAG> (call ID)]
| ^^ not granting access to tag <TAG> because incompatible item is protected: [Unique for <TAG> (call ID)]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/invalidate_against_barrier1.rs:LL:CC
|
LL | let xraw = &mut x as *mut _;
| ^^^^^^
help: <TAG> was protected due to a tag which was created here
help: <TAG> was protected due to <TAG> which was created here
--> $DIR/invalidate_against_barrier1.rs:LL:CC
|
LL | inner(xraw, xref);

View File

@ -1,17 +1,17 @@
error: Undefined Behavior: not granting access to tag <untagged> because incompatible item is protected: [SharedReadOnly for <TAG> (call ID)]
error: Undefined Behavior: not granting access to tag <TAG> because incompatible item is protected: [SharedReadOnly for <TAG> (call ID)]
--> $DIR/invalidate_against_barrier2.rs:LL:CC
|
LL | unsafe { *x = 0 };
| ^^^^^^ not granting access to tag <untagged> because incompatible item is protected: [SharedReadOnly for <TAG> (call ID)]
| ^^^^^^ not granting access to tag <TAG> because incompatible item is protected: [SharedReadOnly for <TAG> (call ID)]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/invalidate_against_barrier2.rs:LL:CC
|
LL | let xraw = &mut x as *mut _;
| ^^^^^^
help: <TAG> was protected due to a tag which was created here
help: <TAG> was protected due to <TAG> which was created here
--> $DIR/invalidate_against_barrier2.rs:LL:CC
|
LL | inner(xraw, xref);

View File

@ -1,20 +1,20 @@
error: Undefined Behavior: attempting a write access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> $DIR/mut_exclusive_violation1.rs:LL:CC
|
LL | *LEAK = 7;
| ^^^^^^^^^
| |
| attempting a write access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/mut_exclusive_violation1.rs:LL:CC
|
LL | LEAK = x as *const _ as *mut _;
| ^
help: tag was later invalidated at offsets [0x0..0x4]
help: <TAG> was later invalidated at offsets [0x0..0x4]
--> $DIR/mut_exclusive_violation1.rs:LL:CC
|
LL | *our = 5;

View File

@ -1,20 +1,20 @@
error: Undefined Behavior: attempting a read access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> $DIR/outdated_local.rs:LL:CC
|
LL | assert_eq!(unsafe { *y }, 1);
| ^^
| |
| attempting a read access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/outdated_local.rs:LL:CC
|
LL | let y: *const i32 = &x;
| ^^
help: tag was later invalidated at offsets [0x0..0x4]
help: <TAG> was later invalidated at offsets [0x0..0x4]
--> $DIR/outdated_local.rs:LL:CC
|
LL | x = 1; // this invalidates y by reactivating the lowermost uniq borrow for this local

View File

@ -1,20 +1,20 @@
error: Undefined Behavior: attempting a read access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> $DIR/pointer_smuggling.rs:LL:CC
|
LL | let _x = unsafe { *PTR };
| ^^^^
| |
| attempting a read access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x1]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x1]
help: <TAG> was created by a retag at offsets [0x0..0x1]
--> $DIR/pointer_smuggling.rs:LL:CC
|
LL | PTR = x;
| ^
help: tag was later invalidated at offsets [0x0..0x1]
help: <TAG> was later invalidated at offsets [0x0..0x1]
--> $DIR/pointer_smuggling.rs:LL:CC
|
LL | *val = 2; // this invalidates any raw ptrs `fun1` might have created.

View File

@ -1,4 +1,3 @@
// compile-flags: -Zmiri-tag-raw-pointers
//! This demonstrates a provenance problem that requires tracking of raw pointers to be detected.
fn main() {

View File

@ -1,15 +1,15 @@
error: Undefined Behavior: attempting a write access using <untagged> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
--> $DIR/shr_frozen_violation1.rs:LL:CC
|
LL | *(x as *const i32 as *mut i32) = 7;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| attempting a write access using <untagged> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
| attempting a write access using <TAG> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
help: <TAG> was created by a retag at offsets [0x0..0x4]
--> $DIR/shr_frozen_violation1.rs:LL:CC
|
LL | *(x as *const i32 as *mut i32) = 7;

View File

@ -1,15 +1,19 @@
error: Undefined Behavior: attempting a write access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> $DIR/transmute-is-no-escape.rs:LL:CC
|
LL | unsafe { *raw = 13 };
| ^^^^^^^^^
| |
| attempting a write access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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 created by a retag at offsets [0x4..0x8]
--> $DIR/transmute-is-no-escape.rs:LL:CC
|
LL | let raw = (&mut x[1] as *mut i32).wrapping_offset(-1);
| ^^^^^^^^^
= note: inside `main` at $DIR/transmute-is-no-escape.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,3 +1,5 @@
// compile-flags: -Zmiri-permissive-provenance
// Make sure we cannot use raw ptrs to access a local that
// we took the direct address of.
fn main() {

View File

@ -1,24 +1,15 @@
error: Undefined Behavior: attempting a write access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a write access using <wildcard> at ALLOC[0x0], but no exposed tags have suitable permission in the borrow stack for this location
--> $DIR/unescaped_local.rs:LL:CC
|
LL | *raw = 13;
| ^^^^^^^^^
| |
| attempting a write access using <untagged> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
| attempting a write access using <wildcard> at ALLOC[0x0], but no exposed tags have suitable permission in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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..0x4]
--> $DIR/unescaped_local.rs:LL:CC
|
LL | let raw = &mut x as *mut i32 as usize as *mut i32;
| ^^^^^^
help: tag was later invalidated at offsets [0x0..0x4]
--> $DIR/unescaped_local.rs:LL:CC
|
LL | let _ptr = &mut x;
| ^^^^^^
= note: inside `main` at $DIR/unescaped_local.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,15 +1,19 @@
error: Undefined Behavior: attempting a read access using <untagged> at ALLOC[0x1], but that tag does not exist in the borrow stack for this location
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x1], but that tag does not exist in the borrow stack for this location
--> $DIR/unescaped_static.rs:LL:CC
|
LL | let _val = unsafe { *ptr_to_first.add(1) };
| ^^^^^^^^^^^^^^^^^^^^
| |
| attempting a read access using <untagged> at ALLOC[0x1], but that tag does not exist in the borrow stack for this location
| attempting a read access using <TAG> at ALLOC[0x1], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[0x1..0x2]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows 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 created by a retag at offsets [0x0..0x1]
--> $DIR/unescaped_static.rs:LL:CC
|
LL | let ptr_to_first = &ARRAY[0] as *const u8;
| ^^^^^^^^^
= note: inside `main` at $DIR/unescaped_static.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
// compile-flags: -Zmiri-symbolic-alignment-check
// compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance
// With the symbolic alignment check, even with intptrcast and without
// validation, we want to be *sure* to catch bugs that arise from pointers being
// insufficiently aligned. The only way to achieve that is not not let programs

View File

@ -1,3 +1,4 @@
// compile-flags: -Zmiri-disable-stacked-borrows
fn main() {
let v: Vec<u8> = Vec::with_capacity(10);
let undef = unsafe { *v.get_unchecked(5) }; //~ ERROR uninitialized

View File

@ -1,3 +1,5 @@
// compile-flags: -Zmiri-permissive-provenance
fn main() {
// Cast a function pointer such that on a call, the argument gets transmuted
// from raw ptr to reference. This is ABI-compatible, so it's not the call that

View File

@ -1,3 +1,5 @@
// compile-flags: -Zmiri-permissive-provenance
fn main() {
// Cast a function pointer such that when returning, the return value gets transmuted
// from raw ptr to reference. This is ABI-compatible, so it's not the call that

View File

@ -1,7 +1,9 @@
fn main() {
// compile-flags: -Zmiri-permissive-provenance
fn test1() {
// The slack between allocations is random.
// Loop a few times to hit the zero-slack case.
for _ in 0..1024 {
for _ in 0..512 {
let n = 0u64;
let ptr: *const u64 = &n;
@ -20,3 +22,26 @@ fn main() {
unsafe { *zst }
}
}
fn test2() {
fn foo() -> u64 {
0
}
for _ in 0..512 {
let n = 0u64;
let ptr: *const u64 = &n;
foo();
let iptr = ptr as usize;
unsafe {
let start = &*std::ptr::slice_from_raw_parts(iptr as *const (), 1);
let end = &*std::ptr::slice_from_raw_parts((iptr + 8) as *const (), 1);
assert_eq!(start.len(), end.len());
}
}
}
fn main() {
test1();
test2();
}

View File

@ -1,3 +1,5 @@
// compile-flags: -Zmiri-permissive-provenance
/// This manually makes sure that we have a pointer with the proper alignment.
fn manual_alignment() {
let x = &mut [0u8; 3];

View File

@ -47,7 +47,7 @@ fn boxed_pair_to_vec() {
struct Foo(u64);
fn reinterstruct(box_pair: Box<PairFoo>) -> Vec<Foo> {
let ref_pair = Box::leak(box_pair) as *mut PairFoo;
let ptr_foo = unsafe { &mut (*ref_pair).fst as *mut Foo };
let ptr_foo = unsafe { std::ptr::addr_of_mut!((*ref_pair).fst) };
unsafe { Vec::from_raw_parts(ptr_foo, 2, 2) }
}

33
tests/pass/box.stderr Normal file
View File

@ -0,0 +1,33 @@
warning: integer-to-pointer cast
--> $DIR/box.rs:LL:CC
|
LL | let r2 = ((r as usize) + 0) as *mut i32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
|
= help: this program is using integer-to-pointer casts or (equivalently) `from_exposed_addr`,
= help: which means that Miri might miss pointer bugs in this program
= help: see https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation
= help: to ensure that Miri does not miss bugs in your program, use `with_addr` (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance) instead
= help: you can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics
= help: alternatively, the `-Zmiri-permissive-provenance` flag disables this warning
= note: inside `into_raw` at $DIR/box.rs:LL:CC
note: inside `main` at $DIR/box.rs:LL:CC
--> $DIR/box.rs:LL:CC
|
LL | into_raw();
| ^^^^^^^^^^
warning: integer-to-pointer cast
--> $DIR/box.rs:LL:CC
|
LL | let r = ((u.as_ptr() as usize) + 0) as *mut i32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
|
= note: inside `into_unique` at $DIR/box.rs:LL:CC
note: inside `main` at $DIR/box.rs:LL:CC
--> $DIR/box.rs:LL:CC
|
LL | into_unique();
| ^^^^^^^^^^^^^

View File

@ -4,6 +4,7 @@
extern crate libc;
use std::mem;
use std::ptr;
pub type Key = libc::pthread_key_t;
@ -11,7 +12,7 @@ static mut RECORD: usize = 0;
static mut KEYS: [Key; 2] = [0; 2];
static mut GLOBALS: [u64; 2] = [1, 0];
static mut CANNARY: *mut u64 = 0 as *mut _; // this serves as a cannary: if TLS dtors are not run properly, this will not get deallocated, making the test fail.
static mut CANNARY: *mut u64 = ptr::null_mut(); // this serves as a cannary: if TLS dtors are not run properly, this will not get deallocated, making the test fail.
pub unsafe fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
let mut key = 0;
@ -30,7 +31,7 @@ pub fn record(r: usize) {
}
unsafe extern "C" fn dtor(ptr: *mut u64) {
assert!(CANNARY != 0 as *mut _); // make sure we do not get run too often
assert!(CANNARY != ptr::null_mut()); // make sure we do not get run too often
let val = *ptr;
let which_key =
@ -48,7 +49,7 @@ unsafe extern "C" fn dtor(ptr: *mut u64) {
// The correct sequence is: First key 0, then key 1, then key 0.
if RECORD == 0_1_0 {
drop(Box::from_raw(CANNARY));
CANNARY = 0 as *mut _;
CANNARY = ptr::null_mut();
}
}

View File

@ -0,0 +1,15 @@
warning: integer-to-pointer cast
--> $DIR/extern_types.rs:LL:CC
|
LL | let x: &Foo = unsafe { &*(16 as *const Foo) };
| ^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
|
= help: this program is using integer-to-pointer casts or (equivalently) `from_exposed_addr`,
= help: which means that Miri might miss pointer bugs in this program
= help: see https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation
= help: to ensure that Miri does not miss bugs in your program, use `with_addr` (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance) instead
= help: you can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics
= help: alternatively, the `-Zmiri-permissive-provenance` flag disables this warning
= note: inside `main` at $DIR/extern_types.rs:LL:CC

View File

@ -1,3 +1,5 @@
// compile-flags: -Zmiri-permissive-provenance
// This strips provenance
fn transmute_ptr_to_int<T>(x: *const T) -> usize {
unsafe { std::mem::transmute(x) }
@ -88,6 +90,16 @@ fn ptr_eq_integer() {
assert!(x != 64 as *const i32);
}
fn zst_deref_of_dangling() {
let b = Box::new(0);
let addr = &*b as *const _ as usize;
drop(b);
// Now if we cast `addr` to a ptr it might pick up the dangling provenance.
// But if we only do a ZST deref there is no UB here!
let zst = addr as *const ();
let _val = unsafe { *zst };
}
fn main() {
cast();
cast_dangling();
@ -99,4 +111,5 @@ fn main() {
ptr_eq_out_of_bounds();
ptr_eq_out_of_bounds_null();
ptr_eq_integer();
zst_deref_of_dangling();
}

View File

@ -1,3 +1,4 @@
// compile-flags: -Zmiri-permissive-provenance
#![feature(core_intrinsics, const_raw_ptr_comparison)]
#![feature(layout_for_ptr)]

View File

@ -3,13 +3,15 @@
#![feature(rustc_private)]
extern crate libc;
use std::ptr;
fn main() {
let mut buf = [0u8; 5];
unsafe {
assert_eq!(
libc::syscall(
libc::SYS_getrandom,
0 as *mut libc::c_void,
ptr::null_mut::<libc::c_void>(),
0 as libc::size_t,
0 as libc::c_uint,
),
@ -26,7 +28,7 @@ fn main() {
);
assert_eq!(
libc::getrandom(0 as *mut libc::c_void, 0 as libc::size_t, 0 as libc::c_uint),
libc::getrandom(ptr::null_mut::<libc::c_void>(), 0 as libc::size_t, 0 as libc::c_uint),
0,
);
assert_eq!(

View File

@ -2,13 +2,15 @@
#![feature(rustc_private)]
extern crate libc;
use std::ptr;
fn main() {
let mut buf = [0u8; 5];
unsafe {
assert_eq!(
libc::syscall(
libc::SYS_getrandom,
0 as *mut libc::c_void,
ptr::null_mut::<libc::c_void>(),
0 as libc::size_t,
0 as libc::c_uint,
),
@ -25,7 +27,7 @@ fn main() {
);
assert_eq!(
libc::getrandom(0 as *mut libc::c_void, 0 as libc::size_t, 0 as libc::c_uint),
libc::getrandom(ptr::null_mut::<libc::c_void>(), 0 as libc::size_t, 0 as libc::c_uint),
0,
);
assert_eq!(

View File

@ -1,5 +1,5 @@
// We test the `align_offset` panic below, make sure we test the interpreter impl and not the "real" one.
// compile-flags: -Zmiri-symbolic-alignment-check
// compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance
#![feature(never_type)]
#![allow(unconditional_panic, non_fmt_panics)]

View File

@ -1,3 +1,4 @@
// compile-flags: -Zmiri-permissive-provenance
use std::mem;
use std::ptr;

View File

@ -1,3 +1,4 @@
// compile-flags: -Zmiri-permissive-provenance
use std::{mem, ptr};
fn main() {

View File

@ -1,5 +1,3 @@
// compile-flags: -Zmiri-tag-raw-pointers
trait S: Sized {
fn tpb(&mut self, _s: Self) {}
}

View File

@ -1,4 +1,3 @@
// compile-flags: -Zmiri-tag-raw-pointers
use std::cell::{Cell, RefCell, UnsafeCell};
use std::mem::{self, MaybeUninit};

View File

@ -1,4 +1,3 @@
// compile-flags: -Zmiri-tag-raw-pointers
use std::ptr;
// Test various stacked-borrows-related things.

View File

@ -1,3 +1,4 @@
// compile-flags: -Zmiri-permissive-provenance
#[derive(PartialEq, Debug)]
struct A;