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:
commit
7fafbde038
47
README.md
47
README.md
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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())?
|
||||
};
|
||||
|
||||
|
@ -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::{
|
||||
|
@ -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)]
|
||||
|
@ -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)?;
|
||||
|
@ -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)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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__)))
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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()));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
@ -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
|
||||
}
|
@ -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
|
||||
|
6
tests/fail/provenance/strict_provenance_cast.rs
Normal file
6
tests/fail/provenance/strict_provenance_cast.rs
Normal 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
|
||||
}
|
14
tests/fail/provenance/strict_provenance_cast.stderr
Normal file
14
tests/fail/provenance/strict_provenance_cast.stderr
Normal 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
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
@ -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) {}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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`
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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
33
tests/pass/box.stderr
Normal 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();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
15
tests/pass/extern_types.stderr
Normal file
15
tests/pass/extern_types.stderr
Normal 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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// compile-flags: -Zmiri-permissive-provenance
|
||||
#![feature(core_intrinsics, const_raw_ptr_comparison)]
|
||||
#![feature(layout_for_ptr)]
|
||||
|
||||
|
@ -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!(
|
||||
|
@ -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!(
|
||||
|
@ -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)]
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// compile-flags: -Zmiri-permissive-provenance
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// compile-flags: -Zmiri-permissive-provenance
|
||||
use std::{mem, ptr};
|
||||
|
||||
fn main() {
|
||||
|
@ -1,5 +1,3 @@
|
||||
// compile-flags: -Zmiri-tag-raw-pointers
|
||||
|
||||
trait S: Sized {
|
||||
fn tpb(&mut self, _s: Self) {}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
// compile-flags: -Zmiri-tag-raw-pointers
|
||||
use std::cell::{Cell, RefCell, UnsafeCell};
|
||||
use std::mem::{self, MaybeUninit};
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
// compile-flags: -Zmiri-tag-raw-pointers
|
||||
use std::ptr;
|
||||
|
||||
// Test various stacked-borrows-related things.
|
||||
|
@ -1,3 +1,4 @@
|
||||
// compile-flags: -Zmiri-permissive-provenance
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct A;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user