Skip to content

Commit

Permalink
Rollup merge of #130727 - compiler-errors:objects, r=RalfJung
Browse files Browse the repository at this point in the history
Check vtable projections for validity in miri

Currently, miri does not catch when we transmute `dyn Trait<Assoc = A>` to `dyn Trait<Assoc = B>`. This PR implements such a check, and fixes #3905.

To do this, we modify `GlobalAlloc::VTable` to contain the *whole* list of `PolyExistentialPredicate`, and then modify `check_vtable_for_type` to validate the `PolyExistentialProjection`s of the vtable, along with the principal trait that was already being validated.

cc ``@RalfJung``
r? ``@lcnr`` or types

I also tweaked the diagnostics a bit.

---

**Open question:** We don't validate the auto traits. You can transmute `dyn Foo` into `dyn Foo + Send`. Should we check that? We currently have a test that *exercises* this as not being UB:

https://github.com/rust-lang/rust/blob/6c6d210089e4589afee37271862b9f88ba1d7755/src/tools/miri/tests/pass/dyn-upcast.rs#L14-L20

I'm not actually sure if we ever decided that's actually UB or not 🤔

We could perhaps still check that the underlying type of the object (i.e. the concrete type that was unsized) implements the auto traits, to catch UB like:

```rust
fn main() {
    let x: &dyn Trait = &std::ptr::null_mut::<()>();
    let _: &(dyn Trait + Send) = std::mem::transmute(x);
    //~^ this vtable is not allocated for a type that is `Send`!
}
```
  • Loading branch information
compiler-errors authored Sep 24, 2024
2 parents 3cfd3f3 + 4dda1eb commit 6588caf
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 8 deletions.
2 changes: 1 addition & 1 deletion tests/fail/dyn-call-trait-mismatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ impl T1 for i32 {
fn main() {
let r = Box::new(0) as Box<dyn T1>;
let r2: Box<dyn T2> = unsafe { std::mem::transmute(r) };
r2.method2(); //~ERROR: using vtable for trait `T1` but trait `T2` was expected
r2.method2(); //~ERROR: using vtable for `T1` but `T2` was expected
}
4 changes: 2 additions & 2 deletions tests/fail/dyn-call-trait-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: Undefined Behavior: using vtable for trait `T1` but trait `T2` was expected
error: Undefined Behavior: using vtable for `T1` but `T2` was expected
--> tests/fail/dyn-call-trait-mismatch.rs:LL:CC
|
LL | r2.method2();
| ^^^^^^^^^^^^ using vtable for trait `T1` but trait `T2` was expected
| ^^^^^^^^^^^^ using vtable for `T1` but `T2` was expected
|
= 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
Expand Down
2 changes: 1 addition & 1 deletion tests/fail/dyn-upcast-trait-mismatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ fn main() {
let baz: &dyn Baz = &1;
let baz_fake: *const dyn Bar = std::mem::transmute(baz);
let _err = baz_fake as *const dyn Foo;
//~^ERROR: using vtable for trait `Baz` but trait `Bar` was expected
//~^ERROR: using vtable for `Baz` but `Bar` was expected
}
}
4 changes: 2 additions & 2 deletions tests/fail/dyn-upcast-trait-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: Undefined Behavior: using vtable for trait `Baz` but trait `Bar` was expected
error: Undefined Behavior: using vtable for `Baz` but `Bar` was expected
--> tests/fail/dyn-upcast-trait-mismatch.rs:LL:CC
|
LL | let _err = baz_fake as *const dyn Foo;
| ^^^^^^^^ using vtable for trait `Baz` but trait `Bar` was expected
| ^^^^^^^^ using vtable for `Baz` but `Bar` was expected
|
= 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
Expand Down
18 changes: 18 additions & 0 deletions tests/fail/validity/wrong-dyn-trait-assoc-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
trait Trait {
type Assoc;
fn foo(&self) -> Self::Assoc;
}

impl<T: Copy> Trait for T {
type Assoc = T;
fn foo(&self) -> T { *self }
}

fn main() {
let v: Box<dyn Trait<Assoc = u8>> = Box::new(2);
let v: Box<dyn Trait<Assoc = bool>> = unsafe { std::mem::transmute(v) }; //~ERROR: wrong trait

if v.foo() {
println!("huh");
}
}
15 changes: 15 additions & 0 deletions tests/fail/validity/wrong-dyn-trait-assoc-type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait<Assoc = bool>`, but encountered `Trait<Assoc = u8>`
--> tests/fail/validity/wrong-dyn-trait-assoc-type.rs:LL:CC
|
LL | let v: Box<dyn Trait<Assoc = bool>> = unsafe { std::mem::transmute(v) };
| ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait<Assoc = bool>`, but encountered `Trait<Assoc = u8>`
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at tests/fail/validity/wrong-dyn-trait-assoc-type.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

4 changes: 2 additions & 2 deletions tests/fail/validity/wrong-dyn-trait.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `<trivial>`
error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `std::marker::Send`
--> tests/fail/validity/wrong-dyn-trait.rs:LL:CC
|
LL | let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) };
| ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `<trivial>`
| ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `std::marker::Send`
|
= 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
Expand Down

0 comments on commit 6588caf

Please sign in to comment.