From 75bc5e67319861cc48cf6e7ffcd6691500605569 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Feb 2023 16:00:54 +0100 Subject: [PATCH 1/2] basic dyn* support for Miri --- .../branchless-select-i128-pointer.stderr | 4 +- tests/fail/validity/dangling_ref1.rs | 2 +- tests/fail/validity/dangling_ref1.stderr | 4 +- tests/pass/dyn-star.rs | 118 ++++++++++++++++++ tests/pass/dyn-star.stdout | 1 + 5 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 tests/pass/dyn-star.rs create mode 100644 tests/pass/dyn-star.stdout diff --git a/tests/fail/branchless-select-i128-pointer.stderr b/tests/fail/branchless-select-i128-pointer.stderr index 96f2ff3282..d68b4b8dfc 100644 --- a/tests/fail/branchless-select-i128-pointer.stderr +++ b/tests/fail/branchless-select-i128-pointer.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: constructing invalid value: encountered a dangling reference (address $HEX is unallocated) +error: Undefined Behavior: constructing invalid value: encountered a dangling reference ($HEX[noalloc] has no provenance) --> $DIR/branchless-select-i128-pointer.rs:LL:CC | LL | / transmute::<_, &str>( @@ -6,7 +6,7 @@ LL | | LL | | !mask & transmute::<_, TwoPtrs>("false !") LL | | | mask & transmute::<_, TwoPtrs>("true !"), LL | | ) - | |_____________^ constructing invalid value: encountered a dangling reference (address $HEX is unallocated) + | |_____________^ constructing invalid value: encountered a dangling reference ($HEX[noalloc] has no provenance) | = 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 diff --git a/tests/fail/validity/dangling_ref1.rs b/tests/fail/validity/dangling_ref1.rs index 6bf2d9295a..fc3a9f3446 100644 --- a/tests/fail/validity/dangling_ref1.rs +++ b/tests/fail/validity/dangling_ref1.rs @@ -3,5 +3,5 @@ use std::mem; fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference } diff --git a/tests/fail/validity/dangling_ref1.stderr b/tests/fail/validity/dangling_ref1.stderr index 01ef071e86..830ab9ca50 100644 --- a/tests/fail/validity/dangling_ref1.stderr +++ b/tests/fail/validity/dangling_ref1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: constructing invalid value: encountered a dangling reference (address 0x10 is unallocated) +error: Undefined Behavior: constructing invalid value: encountered a dangling reference (0x10[noalloc] has no provenance) --> $DIR/dangling_ref1.rs:LL:CC | LL | let _x: &i32 = unsafe { mem::transmute(16usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x10 is unallocated) + | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x10[noalloc] has no provenance) | = 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 diff --git a/tests/pass/dyn-star.rs b/tests/pass/dyn-star.rs new file mode 100644 index 0000000000..4cac7048fd --- /dev/null +++ b/tests/pass/dyn-star.rs @@ -0,0 +1,118 @@ +// Dyn* handling leads to some funky reentrancy in Stacked Borrows, for some reason +//@compile-flags: -Zmiri-disable-stacked-borrows +#![feature(dyn_star)] +#![allow(incomplete_features)] + +use std::fmt::{Debug, Display}; + +fn main() { + make_dyn_star(); + method(); + box_(); + dispatch_on_pin_mut(); + dyn_star_to_dyn(); + dyn_to_dyn_star(); +} + +fn dyn_star_to_dyn() { + let x: dyn* Debug = &42; + let x = Box::new(x) as Box; + assert_eq!("42", format!("{x:?}")); +} + +fn dyn_to_dyn_star() { + let x: Box = Box::new(42); + let x = &x as dyn* Debug; + assert_eq!("42", format!("{x:?}")); +} + +fn make_dyn_star() { + fn make_dyn_star_coercion(i: usize) { + let _dyn_i: dyn* Debug = i; + } + + fn make_dyn_star_explicit(i: usize) { + let _dyn_i: dyn* Debug = i as dyn* Debug; + } + + make_dyn_star_coercion(42); + make_dyn_star_explicit(42); +} + +fn method() { + trait Foo { + fn get(&self) -> usize; + } + + impl Foo for usize { + fn get(&self) -> usize { + *self + } + } + + fn invoke_dyn_star(i: dyn* Foo) -> usize { + i.get() + } + + fn make_and_invoke_dyn_star(i: usize) -> usize { + let dyn_i: dyn* Foo = i; + invoke_dyn_star(dyn_i) + } + + assert_eq!(make_and_invoke_dyn_star(42), 42); +} + +fn box_() { + fn make_dyn_star() -> dyn* Display { + Box::new(42) as dyn* Display + } + + let x = make_dyn_star(); + assert_eq!(format!("{x}"), "42"); +} + +fn dispatch_on_pin_mut() { + use std::future::Future; + + async fn foo(f: dyn* Future) { + println!("dispatch_on_pin_mut: value: {}", f.await); + } + + async fn async_main() { + foo(Box::pin(async { 1 })).await + } + + // ------------------------------------------------------------------------- // + // Implementation Details Below... + + use std::pin::Pin; + use std::task::*; + + pub fn noop_waker() -> Waker { + let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE); + + // SAFETY: the contracts for RawWaker and RawWakerVTable are upheld + unsafe { Waker::from_raw(raw) } + } + + const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); + + unsafe fn noop_clone(_p: *const ()) -> RawWaker { + RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE) + } + + unsafe fn noop(_p: *const ()) {} + + let mut fut = async_main(); + + // Poll loop, just to test the future... + let waker = noop_waker(); + let ctx = &mut Context::from_waker(&waker); + + loop { + match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } { + Poll::Pending => {} + Poll::Ready(()) => break, + } + } +} diff --git a/tests/pass/dyn-star.stdout b/tests/pass/dyn-star.stdout new file mode 100644 index 0000000000..e94427ee30 --- /dev/null +++ b/tests/pass/dyn-star.stdout @@ -0,0 +1 @@ +dispatch_on_pin_mut: value: 1 From 9df7fc31c2a37a9a8495986b7968a24c6d2b87e3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 10 Feb 2023 22:01:43 +0100 Subject: [PATCH 2/2] fix Stacked Borrows interaction with dyn* --- src/helpers.rs | 4 ++++ tests/pass/dyn-star.rs | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 5286023e0f..ed3dd741a8 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -478,6 +478,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } else if matches!(v.layout.fields, FieldsShape::Union(..)) { // A (non-frozen) union. We fall back to whatever the type says. (self.unsafe_cell_action)(v) + } else if matches!(v.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)) { + // This needs to read the vtable pointer to proceed type-driven, but we don't + // want to reentrantly read from memory here. + (self.unsafe_cell_action)(v) } else { // We want to not actually read from memory for this visit. So, before // walking this value, we have to make sure it is not a diff --git a/tests/pass/dyn-star.rs b/tests/pass/dyn-star.rs index 4cac7048fd..16a8cec6cd 100644 --- a/tests/pass/dyn-star.rs +++ b/tests/pass/dyn-star.rs @@ -1,5 +1,3 @@ -// Dyn* handling leads to some funky reentrancy in Stacked Borrows, for some reason -//@compile-flags: -Zmiri-disable-stacked-borrows #![feature(dyn_star)] #![allow(incomplete_features)]