Rollup merge of #105301 - RalfJung:miri, r=oli-obk
update Miri Let's ship the work-around for https://github.com/rust-lang/unsafe-code-guidelines/issues/381.
This commit is contained in:
commit
612e89a1fb
5 changed files with 117 additions and 55 deletions
|
@ -1 +1 @@
|
||||||
cef44f53034eac46be3a0e3eec7b2b3d4ef5140b
|
203c8765ea33c65d888febe0e8219c4bb11b0d89
|
||||||
|
|
|
@ -45,7 +45,9 @@ pub struct Stacks {
|
||||||
/// new pointer.
|
/// new pointer.
|
||||||
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
enum RefKind {
|
enum RefKind {
|
||||||
/// `&mut` and `Box`.
|
/// `Box`.
|
||||||
|
Box,
|
||||||
|
/// `&mut`.
|
||||||
Unique { two_phase: bool },
|
Unique { two_phase: bool },
|
||||||
/// `&` with or without interior mutability.
|
/// `&` with or without interior mutability.
|
||||||
Shared,
|
Shared,
|
||||||
|
@ -56,6 +58,7 @@ enum RefKind {
|
||||||
impl fmt::Display for RefKind {
|
impl fmt::Display for RefKind {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
RefKind::Box => write!(f, "Box"),
|
||||||
RefKind::Unique { two_phase: false } => write!(f, "unique reference"),
|
RefKind::Unique { two_phase: false } => write!(f, "unique reference"),
|
||||||
RefKind::Unique { two_phase: true } => write!(f, "unique reference (two-phase)"),
|
RefKind::Unique { two_phase: true } => write!(f, "unique reference (two-phase)"),
|
||||||
RefKind::Shared => write!(f, "shared reference"),
|
RefKind::Shared => write!(f, "shared reference"),
|
||||||
|
@ -654,15 +657,17 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
let (perm, access) = match kind {
|
let (perm, access) = match kind {
|
||||||
RefKind::Unique { two_phase } => {
|
RefKind::Unique { two_phase } => {
|
||||||
// Permission is Unique only if the type is `Unpin` and this is not twophase
|
// Permission is Unique only if the type is `Unpin` and this is not twophase
|
||||||
let perm = if !two_phase && place.layout.ty.is_unpin(*this.tcx, this.param_env()) {
|
if !two_phase && place.layout.ty.is_unpin(*this.tcx, this.param_env()) {
|
||||||
Permission::Unique
|
(Permission::Unique, Some(AccessKind::Write))
|
||||||
} else {
|
} else {
|
||||||
Permission::SharedReadWrite
|
// FIXME: We emit `dereferenceable` for `!Unpin` mutable references, so we
|
||||||
};
|
// should do fake accesses here. But then we run into
|
||||||
// We do an access for all full borrows, even if `!Unpin`.
|
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
|
||||||
let access = if !two_phase { Some(AccessKind::Write) } else { None };
|
// we don't do that.
|
||||||
(perm, access)
|
(Permission::SharedReadWrite, None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
RefKind::Box => (Permission::Unique, Some(AccessKind::Write)),
|
||||||
RefKind::Raw { mutable: true } => {
|
RefKind::Raw { mutable: true } => {
|
||||||
// Creating a raw ptr does not count as an access
|
// Creating a raw ptr does not count as an access
|
||||||
(Permission::SharedReadWrite, None)
|
(Permission::SharedReadWrite, None)
|
||||||
|
@ -853,7 +858,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
// Boxes get a weak protectors, since they may be deallocated.
|
// Boxes get a weak protectors, since they may be deallocated.
|
||||||
self.retag_place(
|
self.retag_place(
|
||||||
place,
|
place,
|
||||||
RefKind::Unique { two_phase: false },
|
RefKind::Box,
|
||||||
self.retag_cause,
|
self.retag_cause,
|
||||||
/*protector*/
|
/*protector*/
|
||||||
(self.kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
|
(self.kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
//! Reborrowing a `&mut !Unpin` must still act like a (fake) read.
|
|
||||||
use std::marker::PhantomPinned;
|
|
||||||
|
|
||||||
struct NotUnpin(i32, PhantomPinned);
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
unsafe {
|
|
||||||
let mut x = NotUnpin(0, PhantomPinned);
|
|
||||||
// Mutable borrow of `Unpin` field (with lifetime laundering)
|
|
||||||
let fieldref = &mut *(&mut x.0 as *mut i32);
|
|
||||||
// Mutable reborrow of the entire `x`, which is `!Unpin` but should
|
|
||||||
// still count as a read since we would add `dereferenceable`.
|
|
||||||
let _xref = &mut x;
|
|
||||||
// That read should have invalidated `fieldref`.
|
|
||||||
*fieldref = 0; //~ ERROR: /write access .* tag does not exist in the borrow stack/
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
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/notunpin_dereferenceable_fakeread.rs:LL:CC
|
|
||||||
|
|
|
||||||
LL | *fieldref = 0;
|
|
||||||
| ^^^^^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| 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 Unique retag at offsets [0x0..0x4]
|
|
||||||
--> $DIR/notunpin_dereferenceable_fakeread.rs:LL:CC
|
|
||||||
|
|
|
||||||
LL | let fieldref = &mut *(&mut x.0 as *mut i32);
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
help: <TAG> was later invalidated at offsets [0x0..0x4] by a SharedReadWrite retag
|
|
||||||
--> $DIR/notunpin_dereferenceable_fakeread.rs:LL:CC
|
|
||||||
|
|
|
||||||
LL | let _xref = &mut x;
|
|
||||||
| ^^^^^^
|
|
||||||
= note: BACKTRACE:
|
|
||||||
= note: inside `main` at $DIR/notunpin_dereferenceable_fakeread.rs:LL:CC
|
|
||||||
|
|
||||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
#![feature(pin_macro)]
|
||||||
|
|
||||||
|
use std::future::*;
|
||||||
|
use std::marker::PhantomPinned;
|
||||||
|
use std::pin::*;
|
||||||
|
use std::ptr;
|
||||||
|
use std::task::*;
|
||||||
|
|
||||||
|
struct Delay {
|
||||||
|
delay: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Delay {
|
||||||
|
fn new(delay: usize) -> Self {
|
||||||
|
Delay { delay }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for Delay {
|
||||||
|
type Output = ();
|
||||||
|
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
if self.delay > 0 {
|
||||||
|
self.delay -= 1;
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_stuff() {
|
||||||
|
(&mut Delay::new(1)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same thing implemented by hand
|
||||||
|
struct DoStuff {
|
||||||
|
state: usize,
|
||||||
|
delay: Delay,
|
||||||
|
delay_ref: *mut Delay,
|
||||||
|
_marker: PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DoStuff {
|
||||||
|
fn new() -> Self {
|
||||||
|
DoStuff {
|
||||||
|
state: 0,
|
||||||
|
delay: Delay::new(1),
|
||||||
|
delay_ref: ptr::null_mut(),
|
||||||
|
_marker: PhantomPinned,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for DoStuff {
|
||||||
|
type Output = ();
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
unsafe {
|
||||||
|
let this = self.get_unchecked_mut();
|
||||||
|
match this.state {
|
||||||
|
0 => {
|
||||||
|
// Set up self-ref.
|
||||||
|
this.delay_ref = &mut this.delay;
|
||||||
|
// Move to next state.
|
||||||
|
this.state = 1;
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let delay = &mut *this.delay_ref;
|
||||||
|
Pin::new_unchecked(delay).poll(cx)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
struct MyWaker;
|
||||||
|
impl Wake for MyWaker {
|
||||||
|
fn wake(self: Arc<Self>) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let waker = Waker::from(Arc::new(MyWaker));
|
||||||
|
let mut context = Context::from_waker(&waker);
|
||||||
|
|
||||||
|
let mut pinned = pin!(fut);
|
||||||
|
loop {
|
||||||
|
match pinned.as_mut().poll(&mut context) {
|
||||||
|
Poll::Pending => continue,
|
||||||
|
Poll::Ready(v) => return v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
run_fut(do_stuff());
|
||||||
|
run_fut(DoStuff::new());
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue