Rollup merge of #145585 - RalfJung:miri-inplace-arg-checks, r=compiler-errors
Miri: fix handling of in-place argument and return place handling This fixes two separate bugs (in two separate commits): - If the return place is `_local` and not `*ptr`, we didn't always properly protect it if there were other pointers pointing to that return place. - If two in-place arguments are *the same* local variable, we didn't always detect that aliasing.
This commit is contained in:
@@ -27,8 +27,9 @@ use crate::{enter_trace_span, fluent_generated as fluent};
|
|||||||
pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> {
|
pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||||
/// Pass a copy of the given operand.
|
/// Pass a copy of the given operand.
|
||||||
Copy(OpTy<'tcx, Prov>),
|
Copy(OpTy<'tcx, Prov>),
|
||||||
/// Allow for the argument to be passed in-place: destroy the value originally stored at that place and
|
/// Allow for the argument to be passed in-place: destroy the value originally stored at that
|
||||||
/// make the place inaccessible for the duration of the function call.
|
/// place and make the place inaccessible for the duration of the function call. This *must* be
|
||||||
|
/// an in-memory place so that we can do the proper alias checks.
|
||||||
InPlace(MPlaceTy<'tcx, Prov>),
|
InPlace(MPlaceTy<'tcx, Prov>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,6 +380,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// *Before* pushing the new frame, determine whether the return destination is in memory.
|
||||||
|
// Need to use `place_to_op` to be *sure* we get the mplace if there is one.
|
||||||
|
let destination_mplace = self.place_to_op(destination)?.as_mplace_or_imm().left();
|
||||||
|
|
||||||
|
// Push the "raw" frame -- this leaves locals uninitialized.
|
||||||
self.push_stack_frame_raw(instance, body, destination, cont)?;
|
self.push_stack_frame_raw(instance, body, destination, cont)?;
|
||||||
|
|
||||||
// If an error is raised here, pop the frame again to get an accurate backtrace.
|
// If an error is raised here, pop the frame again to get an accurate backtrace.
|
||||||
@@ -496,7 +502,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||||||
|
|
||||||
// Protect return place for in-place return value passing.
|
// Protect return place for in-place return value passing.
|
||||||
// We only need to protect anything if this is actually an in-memory place.
|
// We only need to protect anything if this is actually an in-memory place.
|
||||||
if let Left(mplace) = destination.as_mplace_or_local() {
|
if let Some(mplace) = destination_mplace {
|
||||||
M::protect_in_place_function_argument(self, &mplace)?;
|
M::protect_in_place_function_argument(self, &mplace)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -234,6 +234,12 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A place is either an mplace or some local.
|
/// A place is either an mplace or some local.
|
||||||
|
///
|
||||||
|
/// Note that the return value can be different even for logically identical places!
|
||||||
|
/// Specifically, if a local is stored in-memory, this may return `Local` or `MPlaceTy`
|
||||||
|
/// depending on how the place was constructed. In other words, seeing `Local` here does *not*
|
||||||
|
/// imply that this place does not point to memory. Every caller must therefore always handle
|
||||||
|
/// both cases.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_mplace_or_local(
|
pub fn as_mplace_or_local(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use rustc_abi::{FIRST_VARIANT, FieldIdx};
|
use rustc_abi::{FIRST_VARIANT, FieldIdx};
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_index::IndexSlice;
|
use rustc_index::IndexSlice;
|
||||||
use rustc_middle::ty::{self, Instance, Ty};
|
use rustc_middle::ty::{self, Instance, Ty};
|
||||||
use rustc_middle::{bug, mir, span_bug};
|
use rustc_middle::{bug, mir, span_bug};
|
||||||
@@ -389,8 +390,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||||||
|
|
||||||
/// Evaluate the arguments of a function call
|
/// Evaluate the arguments of a function call
|
||||||
fn eval_fn_call_argument(
|
fn eval_fn_call_argument(
|
||||||
&self,
|
&mut self,
|
||||||
op: &mir::Operand<'tcx>,
|
op: &mir::Operand<'tcx>,
|
||||||
|
move_definitely_disjoint: bool,
|
||||||
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
|
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
|
||||||
interp_ok(match op {
|
interp_ok(match op {
|
||||||
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
|
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
|
||||||
@@ -399,24 +401,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||||||
FnArg::Copy(op)
|
FnArg::Copy(op)
|
||||||
}
|
}
|
||||||
mir::Operand::Move(place) => {
|
mir::Operand::Move(place) => {
|
||||||
// If this place lives in memory, preserve its location.
|
|
||||||
// We call `place_to_op` which will be an `MPlaceTy` whenever there exists
|
|
||||||
// an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
|
|
||||||
// which can return a local even if that has an mplace.)
|
|
||||||
let place = self.eval_place(*place)?;
|
let place = self.eval_place(*place)?;
|
||||||
|
if move_definitely_disjoint {
|
||||||
|
// We still have to ensure that no *other* pointers are used to access this place,
|
||||||
|
// so *if* it is in memory then we have to treat it as `InPlace`.
|
||||||
|
// Use `place_to_op` to guarantee that we notice it being in memory.
|
||||||
let op = self.place_to_op(&place)?;
|
let op = self.place_to_op(&place)?;
|
||||||
|
|
||||||
match op.as_mplace_or_imm() {
|
match op.as_mplace_or_imm() {
|
||||||
Either::Left(mplace) => FnArg::InPlace(mplace),
|
Either::Left(mplace) => FnArg::InPlace(mplace),
|
||||||
Either::Right(_imm) => {
|
Either::Right(_imm) => FnArg::Copy(op),
|
||||||
// This argument doesn't live in memory, so there's no place
|
|
||||||
// to make inaccessible during the call.
|
|
||||||
// We rely on there not being any stray `PlaceTy` that would let the
|
|
||||||
// caller directly access this local!
|
|
||||||
// This is also crucial for tail calls, where we want the `FnArg` to
|
|
||||||
// stay valid when the old stack frame gets popped.
|
|
||||||
FnArg::Copy(op)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// We have to force this into memory to detect aliasing among `Move` arguments.
|
||||||
|
FnArg::InPlace(self.force_allocation(&place)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -425,15 +422,40 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||||||
/// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
|
/// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
|
||||||
/// necessary information about callee and arguments to make a call.
|
/// necessary information about callee and arguments to make a call.
|
||||||
fn eval_callee_and_args(
|
fn eval_callee_and_args(
|
||||||
&self,
|
&mut self,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &mir::Terminator<'tcx>,
|
||||||
func: &mir::Operand<'tcx>,
|
func: &mir::Operand<'tcx>,
|
||||||
args: &[Spanned<mir::Operand<'tcx>>],
|
args: &[Spanned<mir::Operand<'tcx>>],
|
||||||
) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> {
|
) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> {
|
||||||
let func = self.eval_operand(func, None)?;
|
let func = self.eval_operand(func, None)?;
|
||||||
|
|
||||||
|
// Evaluating function call arguments. The tricky part here is dealing with `Move`
|
||||||
|
// arguments: we have to ensure no two such arguments alias. This would be most easily done
|
||||||
|
// by just forcing them all into memory and then doing the usual in-place argument
|
||||||
|
// protection, but then we'd force *a lot* of arguments into memory. So we do some syntactic
|
||||||
|
// pre-processing here where if all `move` arguments are syntactically distinct local
|
||||||
|
// variables (and none is indirect), we can skip the in-memory forcing.
|
||||||
|
let move_definitely_disjoint = 'move_definitely_disjoint: {
|
||||||
|
let mut previous_locals = FxHashSet::<mir::Local>::default();
|
||||||
|
for arg in args {
|
||||||
|
let mir::Operand::Move(place) = arg.node else {
|
||||||
|
continue; // we can skip non-`Move` arguments.
|
||||||
|
};
|
||||||
|
if place.is_indirect_first_projection() {
|
||||||
|
// An indirect `Move` argument could alias with anything else...
|
||||||
|
break 'move_definitely_disjoint false;
|
||||||
|
}
|
||||||
|
if !previous_locals.insert(place.local) {
|
||||||
|
// This local is the base for two arguments! They might overlap.
|
||||||
|
break 'move_definitely_disjoint false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We found no violation so they are all definitely disjoint.
|
||||||
|
true
|
||||||
|
};
|
||||||
let args = args
|
let args = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| self.eval_fn_call_argument(&arg.node))
|
.map(|arg| self.eval_fn_call_argument(&arg.node, move_definitely_disjoint))
|
||||||
.collect::<InterpResult<'tcx, Vec<_>>>()?;
|
.collect::<InterpResult<'tcx, Vec<_>>>()?;
|
||||||
|
|
||||||
let fn_sig_binder = {
|
let fn_sig_binder = {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//@revisions: stack tree
|
||||||
|
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||||
|
// Validation forces more things into memory, which we can't have here.
|
||||||
|
//@compile-flags: -Zmiri-disable-validation
|
||||||
|
#![feature(custom_mir, core_intrinsics)]
|
||||||
|
use std::intrinsics::mir::*;
|
||||||
|
|
||||||
|
pub struct S(i32);
|
||||||
|
|
||||||
|
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||||
|
fn main() {
|
||||||
|
mir! {
|
||||||
|
let _unit: ();
|
||||||
|
{
|
||||||
|
let staging = S(42); // This forces `staging` into memory...
|
||||||
|
let non_copy = staging; // ... so we move it to a non-inmemory local here.
|
||||||
|
// This specifically uses a type with scalar representation to tempt Miri to use the
|
||||||
|
// efficient way of storing local variables (outside adressable memory).
|
||||||
|
Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||||
|
//~[stack]^ ERROR: not granting access
|
||||||
|
//~[tree]| ERROR: /read access .* forbidden/
|
||||||
|
}
|
||||||
|
after_call = {
|
||||||
|
Return()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn callee(x: S, mut y: S) {
|
||||||
|
// With the setup above, if `x` and `y` are both moved,
|
||||||
|
// then writing to `y` will change the value stored in `x`!
|
||||||
|
y.0 = 0;
|
||||||
|
assert_eq!(x.0, 42);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
|
||||||
|
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||||
|
|
|
||||||
|
= 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 here, as the root tag for ALLOC
|
||||||
|
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: <TAG> is this argument
|
||||||
|
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | y.0 = 0;
|
||||||
|
| ^^^^^^^
|
||||||
|
= note: BACKTRACE (of the first span):
|
||||||
|
= note: inside `main` at tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
error: Undefined Behavior: read access through <TAG> (root of the allocation) at ALLOC[0x0] is forbidden
|
||||||
|
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||||
|
|
|
||||||
|
= 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> (currently Active) to become Disabled
|
||||||
|
= help: protected tags must never be Disabled
|
||||||
|
help: the accessed tag <TAG> was created here
|
||||||
|
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: the protected tag <TAG> was created here, in the initial state Reserved
|
||||||
|
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | y.0 = 0;
|
||||||
|
| ^^^^^^^
|
||||||
|
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
|
||||||
|
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | y.0 = 0;
|
||||||
|
| ^^^^^^^
|
||||||
|
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||||
|
= note: BACKTRACE (of the first span):
|
||||||
|
= note: inside `main` at tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ LL | unsafe { ptr.read() };
|
|||||||
note: inside `main`
|
note: inside `main`
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
LL | Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
|
Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
|
||||||
ALLOC (stack variable, size: 4, align: 4) {
|
ALLOC (stack variable, size: 4, align: 4) {
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ use std::intrinsics::mir::*;
|
|||||||
pub fn main() {
|
pub fn main() {
|
||||||
mir! {
|
mir! {
|
||||||
{
|
{
|
||||||
let x = 0;
|
let _x = 0;
|
||||||
let ptr = &raw mut x;
|
let ptr = &raw mut _x;
|
||||||
// We arrange for `myfun` to have a pointer that aliases
|
// We arrange for `myfun` to have a pointer that aliases
|
||||||
// its return place. Even just reading from that pointer is UB.
|
// its return place. Even just reading from that pointer is UB.
|
||||||
Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
}
|
}
|
||||||
|
|
||||||
after_call = {
|
after_call = {
|
||||||
@@ -25,7 +25,7 @@ pub fn main() {
|
|||||||
|
|
||||||
fn myfun(ptr: *mut i32) -> i32 {
|
fn myfun(ptr: *mut i32) -> i32 {
|
||||||
unsafe { ptr.read() };
|
unsafe { ptr.read() };
|
||||||
//~[stack]^ ERROR: not granting access
|
//~[stack]^ ERROR: does not exist in the borrow stack
|
||||||
//~[tree]| ERROR: /read access .* forbidden/
|
//~[tree]| ERROR: /read access .* forbidden/
|
||||||
//~[none]| ERROR: uninitialized
|
//~[none]| ERROR: uninitialized
|
||||||
// Without an aliasing model, reads are "fine" but at least they return uninit data.
|
// Without an aliasing model, reads are "fine" but at least they return uninit data.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
|
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
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr.read() };
|
LL | unsafe { ptr.read() };
|
||||||
| ^^^^^^^^^^ Undefined Behavior occurred here
|
| ^^^^^^^^^^ 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: 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||||
@@ -11,12 +11,12 @@ help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x4]
|
|||||||
|
|
|
|
||||||
LL | / mir! {
|
LL | / mir! {
|
||||||
LL | | {
|
LL | | {
|
||||||
LL | | let x = 0;
|
LL | | let _x = 0;
|
||||||
LL | | let ptr = &raw mut x;
|
LL | | let ptr = &raw mut _x;
|
||||||
... |
|
... |
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_____^
|
| |_____^
|
||||||
help: <TAG> is this argument
|
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr.read() };
|
LL | unsafe { ptr.read() };
|
||||||
@@ -26,8 +26,8 @@ LL | unsafe { ptr.read() };
|
|||||||
note: inside `main`
|
note: inside `main`
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
LL | Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ help: the accessed tag <TAG> was created here
|
|||||||
|
|
|
|
||||||
LL | / mir! {
|
LL | / mir! {
|
||||||
LL | | {
|
LL | | {
|
||||||
LL | | let x = 0;
|
LL | | let _x = 0;
|
||||||
LL | | let ptr = &raw mut x;
|
LL | | let ptr = &raw mut _x;
|
||||||
... |
|
... |
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_____^
|
| |_____^
|
||||||
@@ -34,8 +34,8 @@ LL | unsafe { ptr.read() };
|
|||||||
note: inside `main`
|
note: inside `main`
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
LL | Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub fn main() {
|
|||||||
let ptr = &raw mut _x;
|
let ptr = &raw mut _x;
|
||||||
// We arrange for `myfun` to have a pointer that aliases
|
// We arrange for `myfun` to have a pointer that aliases
|
||||||
// its return place. Writing to that pointer is UB.
|
// its return place. Writing to that pointer is UB.
|
||||||
Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
}
|
}
|
||||||
|
|
||||||
after_call = {
|
after_call = {
|
||||||
@@ -26,7 +26,7 @@ pub fn main() {
|
|||||||
fn myfun(ptr: *mut i32) -> i32 {
|
fn myfun(ptr: *mut i32) -> i32 {
|
||||||
// This overwrites the return place, which shouldn't be possible through another pointer.
|
// This overwrites the return place, which shouldn't be possible through another pointer.
|
||||||
unsafe { ptr.write(0) };
|
unsafe { ptr.write(0) };
|
||||||
//~[stack]^ ERROR: strongly protected
|
//~[stack]^ ERROR: does not exist in the borrow stack
|
||||||
//~[tree]| ERROR: /write access .* forbidden/
|
//~[tree]| ERROR: /write access .* forbidden/
|
||||||
13
|
13
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
|
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
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr.write(0) };
|
LL | unsafe { ptr.write(0) };
|
||||||
| ^^^^^^^^^^^^ Undefined Behavior occurred here
|
| ^^^^^^^^^^^^ 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: 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||||
@@ -16,7 +16,7 @@ LL | | let ptr = &raw mut _x;
|
|||||||
... |
|
... |
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_____^
|
| |_____^
|
||||||
help: <TAG> is this argument
|
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr.write(0) };
|
LL | unsafe { ptr.write(0) };
|
||||||
@@ -26,8 +26,8 @@ LL | unsafe { ptr.write(0) };
|
|||||||
note: inside `main`
|
note: inside `main`
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
LL | Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ LL | unsafe { ptr.write(0) };
|
|||||||
note: inside `main`
|
note: inside `main`
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
LL | Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub fn main() {
|
|||||||
let ptr = &raw mut _x;
|
let ptr = &raw mut _x;
|
||||||
// We arrange for `myfun` to have a pointer that aliases
|
// We arrange for `myfun` to have a pointer that aliases
|
||||||
// its return place. Writing to that pointer is UB.
|
// its return place. Writing to that pointer is UB.
|
||||||
Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
}
|
}
|
||||||
|
|
||||||
after_call = {
|
after_call = {
|
||||||
@@ -32,7 +32,7 @@ fn myfun(ptr: *mut i32) -> i32 {
|
|||||||
fn myfun2(ptr: *mut i32) -> i32 {
|
fn myfun2(ptr: *mut i32) -> i32 {
|
||||||
// This overwrites the return place, which shouldn't be possible through another pointer.
|
// This overwrites the return place, which shouldn't be possible through another pointer.
|
||||||
unsafe { ptr.write(0) };
|
unsafe { ptr.write(0) };
|
||||||
//~[stack]^ ERROR: strongly protected
|
//~[stack]^ ERROR: does not exist in the borrow stack
|
||||||
//~[tree]| ERROR: /write access .* forbidden/
|
//~[tree]| ERROR: /write access .* forbidden/
|
||||||
13
|
13
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
|
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
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr.write(0) };
|
LL | unsafe { ptr.write(0) };
|
||||||
| ^^^^^^^^^^^^ Undefined Behavior occurred here
|
| ^^^^^^^^^^^^ 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: 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||||
@@ -16,18 +16,18 @@ LL | | let ptr = &raw mut _x;
|
|||||||
... |
|
... |
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_____^
|
| |_____^
|
||||||
help: <TAG> is this argument
|
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr.write(0) };
|
LL | become myfun2(ptr)
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
= note: BACKTRACE (of the first span):
|
= note: BACKTRACE (of the first span):
|
||||||
= note: inside `myfun2` at tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
= note: inside `myfun2` at tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||||
note: inside `main`
|
note: inside `main`
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
LL | Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ LL | unsafe { ptr.write(0) };
|
|||||||
note: inside `main`
|
note: inside `main`
|
||||||
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
LL | Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|||||||
Reference in New Issue
Block a user