Auto merge of #114211 - RalfJung:miri, r=RalfJung

update Miri

r? `@ghost`
This commit is contained in:
bors 2023-07-29 20:41:45 +00:00
commit 32303b219d
34 changed files with 336 additions and 233 deletions

View File

@ -10,10 +10,7 @@ on:
branches:
- 'master'
schedule:
- cron: '6 6 * * *' # At 6:06 UTC every day.
env:
CARGO_UNSTABLE_SPARSE_REGISTRY: 'true'
- cron: '11 5 * * *' # At 5:11 UTC every day.
defaults:
run:
@ -21,10 +18,6 @@ defaults:
jobs:
build:
runs-on: ${{ matrix.os }}
env:
RUST_BACKTRACE: 1
HOST_TARGET: ${{ matrix.host_target }}
strategy:
fail-fast: false
matrix:
@ -35,6 +28,10 @@ jobs:
host_target: x86_64-apple-darwin
- os: windows-latest
host_target: i686-pc-windows-msvc
runs-on: ${{ matrix.os }}
env:
RUST_BACKTRACE: 1
HOST_TARGET: ${{ matrix.host_target }}
steps:
- uses: actions/checkout@v3

View File

@ -1 +1 @@
cec34a43b1b14f4e39363f3b283d7ac4f593ee81
d150dbb067e66f351a0b33a54e7d4b464ef51e47

View File

@ -227,10 +227,10 @@ pub(super) enum TransitionError {
ChildAccessForbidden(Permission),
/// A protector was triggered due to an invalid transition that loses
/// too much permissions.
/// For example, if a protected tag goes from `Active` to `Frozen` due
/// to a foreign write this will produce a `ProtectedTransition(PermTransition(Active, Frozen))`.
/// For example, if a protected tag goes from `Active` to `Disabled` due
/// to a foreign write this will produce a `ProtectedDisabled(Active)`.
/// This kind of error can only occur on foreign accesses.
ProtectedTransition(PermTransition),
ProtectedDisabled(Permission),
/// Cannot deallocate because some tag in the allocation is strongly protected.
/// This kind of error can only occur on deallocations.
ProtectedDealloc,
@ -302,7 +302,7 @@ pub fn build<'tcx>(self) -> InterpError<'tcx> {
));
(title, details, conflicting_tag_name)
}
ProtectedTransition(transition) => {
ProtectedDisabled(before_disabled) => {
let conflicting_tag_name = "protected";
let access = cause.print_as_access(/* is_foreign */ true);
let details = vec![
@ -310,12 +310,9 @@ pub fn build<'tcx>(self) -> InterpError<'tcx> {
"the accessed tag {accessed} is foreign to the {conflicting_tag_name} tag {conflicting} (i.e., it is not a child)"
),
format!(
"this {access} would cause the {conflicting_tag_name} tag {conflicting} to transition {transition}"
),
format!(
"this transition would be {loss}, which is not allowed for protected tags",
loss = transition.summary(),
"this {access} would cause the {conflicting_tag_name} tag {conflicting} (currently {before_disabled}) to become Disabled"
),
format!("protected tags must never be Disabled"),
];
(title, details, conflicting_tag_name)
}

View File

@ -72,7 +72,12 @@ fn foreign_read(state: PermissionPriv, protected: bool) -> Option<PermissionPriv
// accesses, since the data is not being mutated. Hence the `{ .. }`
res @ Reserved { .. } if !protected => res,
Reserved { .. } => Frozen, // protected reserved
Active => Frozen,
Active =>
if protected {
Disabled
} else {
Frozen
},
non_writeable @ (Frozen | Disabled) => non_writeable,
})
}
@ -189,34 +194,9 @@ pub fn started(self) -> Permission {
Permission { inner: self.from }
}
/// Determines whether a transition that occured is compatible with the presence
/// of a Protector. This is not included in the `transition` functions because
/// it would distract from the few places where the transition is modified
/// because of a protector, but not forbidden.
///
/// Note: this is not in charge of checking that there *is* a protector,
/// it should be used as
/// ```
/// let no_protector_error = if is_protected(tag) {
/// transition.is_allowed_by_protector()
/// };
/// ```
pub fn is_allowed_by_protector(&self) -> bool {
assert!(self.is_possible());
match (self.from, self.to) {
_ if self.from == self.to => true,
// It is always a protector violation to not be readable anymore
(_, Disabled) => false,
// In the case of a `Reserved` under a protector, both transitions
// `Reserved => Active` and `Reserved => Frozen` can legitimately occur.
// The first is standard (Child Write), the second is for Foreign Writes
// on protected Reserved where we must ensure that the pointer is not
// written to in the future.
(Reserved { .. }, Active) | (Reserved { .. }, Frozen) => true,
// This pointer should have stayed writeable for the whole function
(Active, Frozen) => false,
_ => unreachable!("Transition {} should never be possible", self),
}
/// Determines if this transition would disable the permission.
pub fn produces_disabled(self) -> bool {
self.to == Disabled
}
}
@ -298,14 +278,15 @@ pub fn summary(&self) -> &'static str {
///
/// This function assumes that its arguments apply to the same location
/// and that they were obtained during a normal execution. It will panic otherwise.
/// - `err` cannot be a `ProtectedTransition(_)` of a noop transition, as those
/// never trigger protectors;
/// - all transitions involved in `self` and `err` should be increasing
/// (Reserved < Active < Frozen < Disabled);
/// - between `self` and `err` the permission should also be increasing,
/// so all permissions inside `err` should be greater than `self.1`;
/// - `Active` and `Reserved` cannot cause an error due to insufficient permissions,
/// so `err` cannot be a `ChildAccessForbidden(_)` of either of them;
/// - `err` should not be `ProtectedDisabled(Disabled)`, because the protected
/// tag should not have been `Disabled` in the first place (if this occurs it means
/// we have unprotected tags that become protected)
pub(in super::super) fn is_relevant(&self, err: TransitionError) -> bool {
// NOTE: `super::super` is the visibility of `TransitionError`
assert!(self.is_possible());
@ -342,17 +323,16 @@ pub(in super::super) fn is_relevant(&self, err: TransitionError) -> bool {
unreachable!("permissions between self and err must be increasing"),
}
}
TransitionError::ProtectedTransition(forbidden) => {
assert!(!forbidden.is_noop());
TransitionError::ProtectedDisabled(before_disabled) => {
// Show how we got to the starting point of the forbidden transition,
// but ignore what came before.
// This eliminates transitions like `Reserved -> Active`
// when the error is a `Frozen -> Disabled`.
match (self.to, forbidden.from, forbidden.to) {
match (self.to, before_disabled.inner) {
// We absolutely want to know where it was activated.
(Active, Active, Frozen | Disabled) => true,
(Active, Active) => true,
// And knowing where it became Frozen is also important.
(Frozen, Frozen, Disabled) => true,
(Frozen, Frozen) => true,
// If the error is a transition `Frozen -> Disabled`, then we don't really
// care whether before that was `Reserved -> Active -> Frozen` or
// `Reserved -> Frozen` or even `Frozen` directly.
@ -360,27 +340,22 @@ pub(in super::super) fn is_relevant(&self, err: TransitionError) -> bool {
// - created as Frozen, then Frozen -> Disabled is forbidden
// - created as Reserved, later became Frozen, then Frozen -> Disabled is forbidden
// In both cases the `Reserved -> Active` part is inexistant or irrelevant.
(Active, Frozen, Disabled) => false,
(Active, Frozen) => false,
// `Reserved -> Frozen` does not trigger protectors.
(_, Reserved { .. }, Frozen) =>
unreachable!("this transition cannot cause an error"),
(_, Disabled) =>
unreachable!(
"permission that results in Disabled should not itself be Disabled in the first place"
),
// No transition has `Reserved` as its `.to` unless it's a noop.
(Reserved { .. }, _, _) => unreachable!("self is a noop transition"),
(_, Disabled, Disabled) | (_, Frozen, Frozen) | (_, Active, Active) =>
unreachable!("err contains a noop transition"),
(Reserved { .. }, _) => unreachable!("self is a noop transition"),
// Permissions only evolve in the order `Reserved -> Active -> Frozen -> Disabled`,
// so permissions found must be increasing in the order
// `self.from < self.to <= forbidden.from < forbidden.to`.
(Disabled, Reserved { .. } | Active | Frozen, _)
| (Frozen, Reserved { .. } | Active, _)
| (Active, Reserved { .. }, _) =>
(Disabled, Reserved { .. } | Active | Frozen)
| (Frozen, Reserved { .. } | Active)
| (Active, Reserved { .. }) =>
unreachable!("permissions between self and err must be increasing"),
(_, Disabled, Reserved { .. } | Active | Frozen)
| (_, Frozen, Reserved { .. } | Active)
| (_, Active, Reserved { .. }) =>
unreachable!("permissions within err must be increasing"),
}
}
// We don't care because protectors evolve independently from
@ -406,7 +381,7 @@ mod util {
pub use super::*;
impl PermissionPriv {
/// Enumerate all states
pub fn all() -> impl Iterator<Item = PermissionPriv> {
pub fn all() -> impl Iterator<Item = Self> {
vec![
Active,
Reserved { ty_is_freeze: true },
@ -418,9 +393,15 @@ pub fn all() -> impl Iterator<Item = PermissionPriv> {
}
}
impl Permission {
pub fn all() -> impl Iterator<Item = Self> {
PermissionPriv::all().map(|inner| Self { inner })
}
}
impl AccessKind {
/// Enumerate all AccessKind.
pub fn all() -> impl Iterator<Item = AccessKind> {
pub fn all() -> impl Iterator<Item = Self> {
use AccessKind::*;
[Read, Write].into_iter()
}
@ -428,7 +409,7 @@ pub fn all() -> impl Iterator<Item = AccessKind> {
impl AccessRelatedness {
/// Enumerate all relative positions
pub fn all() -> impl Iterator<Item = AccessRelatedness> {
pub fn all() -> impl Iterator<Item = Self> {
use AccessRelatedness::*;
[This, StrictChildAccess, AncestorAccess, DistantAccess].into_iter()
}

View File

@ -19,6 +19,7 @@
use crate::borrow_tracker::tree_borrows::{
diagnostics::{self, NodeDebugInfo, TbError, TransitionError},
perms::PermTransition,
unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap},
Permission,
};
@ -28,17 +29,19 @@
/// Data for a single *location*.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct LocationState {
/// This pointer's current permission
permission: Permission,
/// A location is initialized when it is child accessed for the first time,
/// and it then stays initialized forever.
/// Before initialization we still apply some preemptive transitions on
/// `permission` to know what to do in case it ever gets initialized,
/// but these can never cause any immediate UB. There can however be UB
/// the moment we attempt to initialize (i.e. child-access) because some
/// foreign access done between the creation and the initialization is
/// incompatible with child accesses.
/// A location is initialized when it is child-accessed for the first time (and the initial
/// retag initializes the location for the range covered by the type), and it then stays
/// initialized forever.
/// For initialized locations, "permission" is the current permission. However, for
/// uninitialized locations, we still need to track the "future initial permission": this will
/// start out to be `default_initial_perm`, but foreign accesses need to be taken into account.
/// Crucially however, while transitions to `Disabled` would usually be UB if this location is
/// protected, that is *not* the case for uninitialized locations. Instead we just have a latent
/// "future initial permission" of `Disabled`, causing UB only if an access is ever actually
/// performed.
initialized: bool,
/// This pointer's current permission / future initial permission.
permission: Permission,
/// Strongest foreign access whose effects have already been applied to
/// this node and all its children since the last child access.
/// This is `None` if the most recent access is a child access,
@ -69,6 +72,104 @@ pub fn is_initialized(&self) -> bool {
pub fn permission(&self) -> Permission {
self.permission
}
/// Apply the effect of an access to one location, including
/// - applying `Permission::perform_access` to the inner `Permission`,
/// - emitting protector UB if the location is initialized,
/// - updating the initialized status (child accesses produce initialized locations).
fn perform_access(
&mut self,
access_kind: AccessKind,
rel_pos: AccessRelatedness,
protected: bool,
) -> Result<PermTransition, TransitionError> {
let old_perm = self.permission;
let transition = Permission::perform_access(access_kind, rel_pos, old_perm, protected)
.ok_or(TransitionError::ChildAccessForbidden(old_perm))?;
// Why do only initialized locations cause protector errors?
// Consider two mutable references `x`, `y` into disjoint parts of
// the same allocation. A priori, these may actually both be used to
// access the entire allocation, as long as only reads occur. However,
// a write to `y` needs to somehow record that `x` can no longer be used
// on that location at all. For these uninitialized locations (i.e., locations
// that haven't been accessed with `x` yet), we track the "future initial state":
// it defaults to whatever the initial state of the tag is,
// but the access to `y` moves that "future initial state" of `x` to `Disabled`.
// However, usually a `Reserved -> Disabled` transition would be UB due to the protector!
// So clearly protectors shouldn't fire for such "future initial state" transitions.
//
// See the test `two_mut_protected_same_alloc` in `tests/pass/tree_borrows/tree-borrows.rs`
// for an example of safe code that would be UB if we forgot to check `self.initialized`.
if protected && self.initialized && transition.produces_disabled() {
return Err(TransitionError::ProtectedDisabled(old_perm));
}
self.permission = transition.applied(old_perm).unwrap();
self.initialized |= !rel_pos.is_foreign();
Ok(transition)
}
// Helper to optimize the tree traversal.
// The optimization here consists of observing thanks to the tests
// `foreign_read_is_noop_after_write` and `all_transitions_idempotent`,
// that there are actually just three possible sequences of events that can occur
// in between two child accesses that produce different results.
//
// Indeed,
// - applying any number of foreign read accesses is the same as applying
// exactly one foreign read,
// - applying any number of foreign read or write accesses is the same
// as applying exactly one foreign write.
// therefore the three sequences of events that can produce different
// outcomes are
// - an empty sequence (`self.latest_foreign_access = None`)
// - a nonempty read-only sequence (`self.latest_foreign_access = Some(Read)`)
// - a nonempty sequence with at least one write (`self.latest_foreign_access = Some(Write)`)
//
// This function not only determines if skipping the propagation right now
// is possible, it also updates the internal state to keep track of whether
// the propagation can be skipped next time.
// It is a performance loss not to call this function when a foreign access occurs.
// It is unsound not to call this function when a child access occurs.
fn skip_if_known_noop(
&mut self,
access_kind: AccessKind,
rel_pos: AccessRelatedness,
) -> ContinueTraversal {
if rel_pos.is_foreign() {
let new_access_noop = match (self.latest_foreign_access, access_kind) {
// Previously applied transition makes the new one a guaranteed
// noop in the two following cases:
// (1) justified by `foreign_read_is_noop_after_write`
(Some(AccessKind::Write), AccessKind::Read) => true,
// (2) justified by `all_transitions_idempotent`
(Some(old), new) if old == new => true,
// In all other cases there has been a recent enough
// child access that the effects of the new foreign access
// need to be applied to this subtree.
_ => false,
};
if new_access_noop {
// Abort traversal if the new transition is indeed guaranteed
// to be noop.
// No need to update `self.latest_foreign_access`,
// the type of the current streak among nonempty read-only
// or nonempty with at least one write has not changed.
ContinueTraversal::SkipChildren
} else {
// Otherwise propagate this time, and also record the
// access that just occurred so that we can skip the propagation
// next time.
self.latest_foreign_access = Some(access_kind);
ContinueTraversal::Recurse
}
} else {
// A child access occurred, this breaks the streak of foreign
// accesses in a row and the sequence since the previous child access
// is now empty.
self.latest_foreign_access = None;
ContinueTraversal::Recurse
}
}
}
/// Tree structure with both parents and children since we want to be
@ -387,11 +488,15 @@ pub fn dealloc(
Ok(())
}
/// Maps the following propagation procedure to each range:
/// - initialize if needed;
/// - compute new state after transition;
/// - check that there is no protector that would forbid this;
/// - record this specific location as accessed.
/// Map the per-node and per-location `LocationState::perform_access`
/// to each location of `access_range`, on every tag of the allocation.
///
/// `LocationState::perform_access` will take care of raising transition
/// errors and updating the `initialized` status of each location,
/// this traversal adds to that:
/// - inserting into the map locations that do not exist yet,
/// - trimming the traversal,
/// - recording the history.
pub fn perform_access(
&mut self,
access_kind: AccessKind,
@ -411,55 +516,16 @@ pub fn perform_access(
let old_state =
perm.or_insert_with(|| LocationState::new(node.default_initial_perm));
// Optimize the tree traversal.
// The optimization here consists of observing thanks to the tests
// `foreign_read_is_noop_after_write` and `all_transitions_idempotent`
// that if we apply twice in a row the effects of a foreign access
// we can skip some branches.
// "two foreign accesses in a row" occurs when `perm.latest_foreign_access` is `Some(_)`
// AND the `rel_pos` of the current access corresponds to a foreign access.
if rel_pos.is_foreign() {
let new_access_noop =
match (old_state.latest_foreign_access, access_kind) {
// Previously applied transition makes the new one a guaranteed
// noop in the two following cases:
// (1) justified by `foreign_read_is_noop_after_write`
(Some(AccessKind::Write), AccessKind::Read) => true,
// (2) justified by `all_transitions_idempotent`
(Some(old), new) if old == new => true,
// In all other cases there has been a recent enough
// child access that the effects of the new foreign access
// need to be applied to this subtree.
_ => false,
};
if new_access_noop {
// Abort traversal if the new transition is indeed guaranteed
// to be noop.
return Ok(ContinueTraversal::SkipChildren);
} else {
// Otherwise propagate this time, and also record the
// access that just occurred so that we can skip the propagation
// next time.
old_state.latest_foreign_access = Some(access_kind);
}
} else {
// A child access occurred, this breaks the streak of "two foreign
// accesses in a row" and we reset this field.
old_state.latest_foreign_access = None;
match old_state.skip_if_known_noop(access_kind, rel_pos) {
ContinueTraversal::SkipChildren =>
return Ok(ContinueTraversal::SkipChildren),
_ => {}
}
let old_perm = old_state.permission;
let protected = global.borrow().protected_tags.contains_key(&node.tag);
let transition =
Permission::perform_access(access_kind, rel_pos, old_perm, protected)
.ok_or(TransitionError::ChildAccessForbidden(old_perm))?;
if protected
// Can't trigger Protector on uninitialized locations
&& old_state.initialized
&& !transition.is_allowed_by_protector()
{
return Err(TransitionError::ProtectedTransition(transition));
}
old_state.perform_access(access_kind, rel_pos, protected)?;
// Record the event as part of the history
if !transition.is_noop() {
node.debug_info.history.push(diagnostics::Event {
@ -470,10 +536,7 @@ pub fn perform_access(
transition_range: perms_range.clone(),
span,
});
old_state.permission =
transition.applied(old_state.permission).unwrap();
}
old_state.initialized |= !rel_pos.is_foreign();
Ok(ContinueTraversal::Recurse)
},
|args: ErrHandlerArgs<'_, TransitionError>| -> InterpError<'tcx> {
@ -602,3 +665,57 @@ pub fn for_child(self) -> Self {
}
}
}
#[cfg(test)]
mod commutation_tests {
use super::*;
impl LocationState {
pub fn all_without_access() -> impl Iterator<Item = Self> {
Permission::all().flat_map(|permission| {
[false, true].into_iter().map(move |initialized| {
Self { permission, initialized, latest_foreign_access: None }
})
})
}
}
#[test]
#[rustfmt::skip]
// Exhaustive check that for any starting configuration loc,
// for any two read accesses r1 and r2, if `loc + r1 + r2` is not UB
// and results in `loc'`, then `loc + r2 + r1` is also not UB and results
// in the same final state `loc'`.
// This lets us justify arbitrary read-read reorderings.
fn all_read_accesses_commute() {
let kind = AccessKind::Read;
// Two of the four combinations of `AccessRelatedness` are trivial,
// but we might as well check them all.
for rel1 in AccessRelatedness::all() {
for rel2 in AccessRelatedness::all() {
// Any protector state works, but we can't move reads across function boundaries
// so the two read accesses occur under the same protector.
for &protected in &[true, false] {
for loc in LocationState::all_without_access() {
// Apply 1 then 2. Failure here means that there is UB in the source
// and we skip the check in the target.
let mut loc12 = loc;
let Ok(_) = loc12.perform_access(kind, rel1, protected) else { continue; };
let Ok(_) = loc12.perform_access(kind, rel2, protected) else { continue; };
// If 1 followed by 2 succeeded, then 2 followed by 1 must also succeed...
let mut loc21 = loc;
loc21.perform_access(kind, rel2, protected).unwrap();
loc21.perform_access(kind, rel1, protected).unwrap();
// ... and produce the same final result.
assert_eq!(
loc12, loc21,
"Read accesses {:?} followed by {:?} do not commute !",
rel1, rel2
);
}
}
}
}
}
}

View File

@ -194,14 +194,8 @@ fn handle_miri_resolve_frame(
let filename_alloc =
this.allocate_str(&filename, MiriMemoryKind::Rust.into(), Mutability::Mut)?;
this.write_immediate(
name_alloc.to_ref(this),
&this.project_field(&dest, 0)?,
)?;
this.write_immediate(
filename_alloc.to_ref(this),
&this.project_field(&dest, 1)?,
)?;
this.write_immediate(name_alloc.to_ref(this), &this.project_field(&dest, 0)?)?;
this.write_immediate(filename_alloc.to_ref(this), &this.project_field(&dest, 1)?)?;
}
1 => {
this.write_scalar(

View File

@ -156,10 +156,7 @@ fn QueryPerformanceCounter(
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
})?;
this.write_scalar(
Scalar::from_i64(qpc),
&this.deref_operand(lpPerformanceCount_op)?,
)?;
this.write_scalar(Scalar::from_i64(qpc), &this.deref_operand(lpPerformanceCount_op)?)?;
Ok(Scalar::from_i32(-1)) // return non-zero on success
}

View File

@ -86,7 +86,12 @@ fn emulate_foreign_item_by_name(
"_NSGetEnviron" => {
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.write_pointer(
this.machine.env_vars.environ.as_ref().expect("machine must be initialized").ptr,
this.machine
.env_vars
.environ
.as_ref()
.expect("machine must be initialized")
.ptr,
dest,
)?;
}
@ -139,10 +144,7 @@ fn emulate_foreign_item_by_name(
if written {
this.write_null(dest)?;
} else {
this.write_scalar(
Scalar::from_u32(size_needed.try_into().unwrap()),
&bufsize,
)?;
this.write_scalar(Scalar::from_u32(size_needed.try_into().unwrap()), &bufsize)?;
this.write_int(-1, dest)?;
}
}

View File

@ -867,9 +867,7 @@ fn pthread_cond_destroy(
cond_get_clock_id(this, cond_op)?;
// This might lead to false positives, see comment in pthread_mutexattr_destroy
this.write_uninit(
&this.deref_operand_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?,
)?;
this.write_uninit(&this.deref_operand_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?)?;
// FIXME: delete interpreter state associated with this condvar.
Ok(0)

View File

@ -191,6 +191,8 @@ macro_rules! regexes {
// erase borrow tags
"<[0-9]+>" => "<TAG>",
"<[0-9]+=" => "<TAG=",
// normalize width of Tree Borrows diagnostic borders (which otherwise leak borrow tag info)
"(─{50})─+" => "$1",
// erase whitespace that differs between platforms
r" +at (.*\.rs)" => " at $1",
// erase generics in backtraces

View File

@ -6,8 +6,8 @@ LL | ptr::write(dest, src);
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> to transition from Frozen to Disabled
= help: this transition would be a loss of read permissions, which is not allowed for protected tags
= help: this foreign write access would cause the protected tag <TAG> (currently Frozen) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/aliasing_mut4.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | *y
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign read access would cause the protected tag <TAG> to transition from Active to Frozen
= help: this transition would be a loss of write permissions, which is not allowed for protected tags
= help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/box_noalias_violation.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | unsafe { *y = 2 };
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> to transition from Active to Disabled
= help: this transition would be a loss of read and write permissions, which is not allowed for protected tags
= help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/illegal_write6.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | unsafe { *x = 0 };
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> to transition from Frozen to Disabled
= help: this transition would be a loss of read permissions, which is not allowed for protected tags
= help: this foreign write access would cause the protected tag <TAG> (currently Frozen) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/invalidate_against_protector2.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | unsafe { *x = 0 };
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> to transition from Frozen to Disabled
= help: this transition would be a loss of read permissions, which is not allowed for protected tags
= help: this foreign write access would cause the protected tag <TAG> (currently Frozen) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/invalidate_against_protector3.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) }
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this deallocation (acting as a foreign write access) would cause the protected tag <TAG> to transition from Frozen to Disabled
= help: this transition would be a loss of read permissions, which is not allowed for protected tags
= help: this deallocation (acting as a foreign write access) would cause the protected tag <TAG> (currently Frozen) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/newtype_pair_retagging.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) }
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this deallocation (acting as a foreign write access) would cause the protected tag <TAG> to transition from Frozen to Disabled
= help: this transition would be a loss of read permissions, which is not allowed for protected tags
= help: this deallocation (acting as a foreign write access) would cause the protected tag <TAG> (currently Frozen) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/newtype_retagging.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | unsafe { ptr.write(S(0)) };
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> to transition from Active to Disabled
= help: this transition would be a loss of read and write permissions, which is not allowed for protected tags
= help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/arg_inplace_mutate.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | unsafe { ptr.read() };
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign read access would cause the protected tag <TAG> to transition from Active to Frozen
= help: this transition would be a loss of write permissions, which is not allowed for protected tags
= help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/arg_inplace_observe_during.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | unsafe { ptr.read() };
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign read access would cause the protected tag <TAG> to transition from Active to Frozen
= help: this transition would be a loss of write permissions, which is not allowed for protected tags
= help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/return_pointer_aliasing.rs:LL:CC
|

View File

@ -6,8 +6,8 @@ LL | *y.add(3) = 42;
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> to transition from Reserved to Disabled
= help: this transition would be a loss of read and write permissions, which is not allowed for protected tags
= help: this foreign write access would cause the protected tag <TAG> (currently Reserved) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/outside-range.rs:LL:CC
|

View File

@ -1,4 +1,4 @@
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
@ -7,7 +7,7 @@ Warning: this tree is indicative only. Some tags may have been hidden.
| Re*| │ └─┬──<TAG=caller:x>
| Re*| │ └────<TAG=callee:x> Strongly protected
| Re*| └────<TAG=y, callee:y, caller:y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
error: Undefined Behavior: write access through <TAG> (y, callee:y, caller:y) is forbidden
--> $DIR/cell-protected-write.rs:LL:CC
|
@ -16,8 +16,8 @@ LL | *y = 1;
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> (y, callee:y, caller:y) is foreign to the protected tag <TAG> (callee:x) (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> (callee:x) to transition from Reserved to Disabled
= help: this transition would be a loss of read and write permissions, which is not allowed for protected tags
= help: this foreign write access would cause the protected tag <TAG> (callee:x) (currently Reserved) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/cell-protected-write.rs:LL:CC
|

View File

@ -1,4 +1,4 @@
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
@ -7,7 +7,7 @@ Warning: this tree is indicative only. Some tags may have been hidden.
| Res| │ └─┬──<TAG=caller:x>
| Res| │ └────<TAG=callee:x> Strongly protected
| Res| └────<TAG=y, callee:y, caller:y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
error: Undefined Behavior: write access through <TAG> (y, callee:y, caller:y) is forbidden
--> $DIR/int-protected-write.rs:LL:CC
|
@ -16,8 +16,8 @@ LL | *y = 0;
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> (y, callee:y, caller:y) is foreign to the protected tag <TAG> (callee:x) (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> (callee:x) to transition from Reserved to Disabled
= help: this transition would be a loss of read and write permissions, which is not allowed for protected tags
= help: this foreign write access would cause the protected tag <TAG> (callee:x) (currently Reserved) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> $DIR/int-protected-write.rs:LL:CC
|

View File

@ -3,7 +3,7 @@
//! Tests for various intrinsics that do not fit anywhere else.
use std::intrinsics;
use std::mem::{size_of, size_of_val, size_of_val_raw, discriminant};
use std::mem::{discriminant, size_of, size_of_val, size_of_val_raw};
struct Bomb;

View File

@ -1,12 +1,12 @@
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Re*| └────<TAG=data, x, y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Act| └────<TAG=data, x, y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────

View File

@ -1,11 +1,11 @@
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Res| └─┬──<TAG=data>
| Res| └────<TAG=x>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
@ -13,8 +13,8 @@ Warning: this tree is indicative only. Some tags may have been hidden.
| Res| └─┬──<TAG=x>
| Res| └─┬──<TAG=caller:x>
| Res| └────<TAG=callee:x> Strongly protected
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
@ -23,8 +23,8 @@ Warning: this tree is indicative only. Some tags may have been hidden.
| Res| │ └─┬──<TAG=caller:x>
| Res| │ └────<TAG=callee:x>
| Res| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
@ -33,4 +33,4 @@ Warning: this tree is indicative only. Some tags may have been hidden.
| Dis| │ └─┬──<TAG=caller:x>
| Dis| │ └────<TAG=callee:x>
| Act| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────

View File

@ -1,4 +1,4 @@
───────────────────────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1.. 2.. 10.. 11..100..101..1000..1001..1024
| Act| Act| Act| Act| Act| Act| Act| Act| Act| └─┬──<TAG=root of the allocation>
@ -7,8 +7,8 @@ Warning: this tree is indicative only. Some tags may have been hidden.
|----|----|----| Act|----|?Dis| ----| ?Dis| ----| ├────<TAG=data[10]>
|----|----|----|----|----| Frz| ----| ?Dis| ----| ├────<TAG=data[100]>
|----|----|----|----|----|----| ----| Act| ----| └────<TAG=data[1000]>
───────────────────────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
@ -28,4 +28,4 @@ Warning: this tree is indicative only. Some tags may have been hidden.
| Frz| └─┬──<TAG=xcb>
| Frz| ├────<TAG=xcba>
| Frz| └────<TAG=xcbb>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────

View File

@ -1,15 +1,15 @@
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Act| └─┬──<TAG=parent>
| Act| └────<TAG=x>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Act| └─┬──<TAG=parent>
| Frz| ├────<TAG=x>
| Res| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────

View File

@ -1,5 +1,5 @@
[interior mut + protected] Foreign Read: Re* -> Frz
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
@ -8,27 +8,27 @@ Warning: this tree is indicative only. Some tags may have been hidden.
| Re*| │ └─┬──<TAG=caller:x>
| Frz| │ └────<TAG=callee:x>
| Re*| └────<TAG=y, caller:y, callee:y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
[interior mut] Foreign Read: Re* -> Re*
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 8
| Act| └─┬──<TAG=root of the allocation>
| Re*| └─┬──<TAG=base>
| Re*| ├────<TAG=x>
| Re*| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
[interior mut] Foreign Write: Re* -> Re*
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 8
| Act| └─┬──<TAG=root of the allocation>
| Act| └─┬──<TAG=base>
| Re*| ├────<TAG=x>
| Act| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
[protected] Foreign Read: Res -> Frz
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
@ -37,22 +37,22 @@ Warning: this tree is indicative only. Some tags may have been hidden.
| Res| │ └─┬──<TAG=caller:x>
| Frz| │ └────<TAG=callee:x>
| Res| └────<TAG=y, caller:y, callee:y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
[] Foreign Read: Res -> Res
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Res| └─┬──<TAG=base>
| Res| ├────<TAG=x>
| Res| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
[] Foreign Write: Res -> Dis
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Act| └─┬──<TAG=base>
| Dis| ├────<TAG=x>
| Act| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────

View File

@ -9,6 +9,7 @@
fn main() {
aliasing_read_only_mutable_refs();
string_as_mut_ptr();
two_mut_protected_same_alloc();
// Stacked Borrows tests
read_does_not_invalidate1();
@ -62,6 +63,23 @@ pub fn string_as_mut_ptr() {
}
}
// This function checks that there is no issue with having two mutable references
// from the same allocation both under a protector.
// This is safe code, it must absolutely not be UB.
// This test failing is a symptom of forgetting to check that only initialized
// locations can cause protector UB.
fn two_mut_protected_same_alloc() {
fn write_second(_x: &mut u8, y: &mut u8) {
// write through `y` will make some locations of `x` (protected)
// become Disabled. Those locations are outside of the range on which
// `x` is initialized, and the protector must not trigger.
*y = 1;
}
let mut data = (0u8, 1u8);
write_second(&mut data.0, &mut data.1);
}
// ----- The tests below were taken from Stacked Borrows ----
// Make sure that reading from an `&mut` does, like reborrowing to `&`,

View File

@ -1,21 +1,21 @@
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Res| └─┬──<TAG=base>
| Res| └────<TAG=raw, uniq, uniq>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Act| └─┬──<TAG=base>
| Act| └────<TAG=raw, uniq, uniq>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Act| └─┬──<TAG=base>
| Act| └────<TAG=raw, uniq, uniq>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────

View File

@ -1,24 +1,24 @@
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Res| └─┬──<TAG=base>
| Res| └─┬──<TAG=raw>
|----| └────<TAG=uniq, uniq>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Act| └─┬──<TAG=base>
| Act| └─┬──<TAG=raw>
| Act| └────<TAG=uniq, uniq>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=root of the allocation>
| Act| └─┬──<TAG=base>
| Act| └─┬──<TAG=raw>
| Dis| └────<TAG=uniq, uniq>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────

View File

@ -1,6 +1,6 @@
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 2
| Act| └─┬──<TAG=root of the allocation>
| Res| └────<TAG=base.as_ptr(), base.as_ptr(), raw_parts.0, reconstructed.as_ptr(), reconstructed.as_ptr()>
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────

View File

@ -1,8 +1,8 @@
──────────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 2
| Act| └─┬──<TAG=root of the allocation>
|----| └─┬──<TAG=base.as_ptr(), base.as_ptr()>
|----| └─┬──<TAG=raw_parts.0>
|----| └────<TAG=reconstructed.as_ptr(), reconstructed.as_ptr()>
──────────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────