about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-04-23 06:24:57 +0200
committerGitHub <noreply@github.com>2024-04-23 06:24:57 +0200
commit8039488e595276ec3ae9ceb09af434d375281a57 (patch)
tree7ceb2d4387ba3732c7fa6caa66e05b72fee54d6a /src
parent5800e2a6f9e6d70adfe225e9163d3e31f4236021 (diff)
parent875f0c2da05da9a7620afd6e2be04fbcb9a4b395 (diff)
downloadrust-8039488e595276ec3ae9ceb09af434d375281a57.tar.gz
rust-8039488e595276ec3ae9ceb09af434d375281a57.zip
Rollup merge of #124220 - RalfJung:interpret-wrong-vtable, r=oli-obk
Miri: detect wrong vtables in wide pointers

Fixes https://github.com/rust-lang/miri/issues/3497.
Needed to catch the UB that https://github.com/rust-lang/rust/pull/123572 will start exploiting.

r? `@oli-obk`
Diffstat (limited to 'src')
-rw-r--r--src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs5
-rw-r--r--src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr4
-rw-r--r--src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs15
-rw-r--r--src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr15
-rw-r--r--src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs9
-rw-r--r--src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr6
-rw-r--r--src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs12
-rw-r--r--src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr15
-rw-r--r--src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs6
-rw-r--r--src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr15
-rw-r--r--src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs2
-rw-r--r--src/tools/miri/tests/pass/dyn-upcast.rs42
12 files changed, 116 insertions, 30 deletions
diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs
index 13f913454dc..f71df9a1c90 100644
--- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs
+++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs
@@ -1,3 +1,6 @@
+// Validation stops this too early.
+//@compile-flags: -Zmiri-disable-validation
+
 trait T1 {
     #[allow(dead_code)]
     fn method1(self: Box<Self>);
@@ -13,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: call on a pointer whose vtable does not match its type
+    r2.method2(); //~ERROR: using vtable for trait `T1` but trait `T2` was expected
 }
diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr
index 365186bcc4b..019a55bcdcb 100644
--- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr
+++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: `dyn` call on a pointer whose vtable does not match its type
+error: Undefined Behavior: using vtable for trait `T1` but trait `T2` was expected
   --> $DIR/dyn-call-trait-mismatch.rs:LL:CC
    |
 LL |     r2.method2();
-   |     ^^^^^^^^^^^^ `dyn` call on a pointer whose vtable does not match its type
+   |     ^^^^^^^^^^^^ using vtable for trait `T1` but trait `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
diff --git a/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs
new file mode 100644
index 00000000000..dff5a21c4c7
--- /dev/null
+++ b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs
@@ -0,0 +1,15 @@
+// This upcast is currently forbidden because it involves an invalid value.
+// However, if in the future we relax the validity requirements for raw pointer vtables,
+// we could consider allowing this again -- the cast itself isn't doing anything wrong,
+// only the transmutes needed to set up the testcase are wrong.
+
+use std::fmt;
+
+fn main() {
+    // vtable_mismatch_nop_cast
+    let ptr: &dyn fmt::Display = &0;
+    let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; //~ERROR: wrong trait
+    // Even though the vtable is for the wrong trait, this cast doesn't actually change the needed
+    // vtable so it should still be allowed -- if we ever allow the line above.
+    let _ptr2 = ptr as *const dyn fmt::Debug;
+}
diff --git a/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr
new file mode 100644
index 00000000000..4165d5ea15d
--- /dev/null
+++ b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug + std::marker::Send + std::marker::Sync`, but encountered `std::fmt::Display`
+  --> $DIR/dyn-upcast-nop-wrong-trait.rs:LL:CC
+   |
+LL |     let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) };
+   |                                                               ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug + std::marker::Send + std::marker::Sync`, but encountered `std::fmt::Display`
+   |
+   = 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 $DIR/dyn-upcast-nop-wrong-trait.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
+
diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs
index 982f33b0a31..1d6b6777032 100644
--- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs
+++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs
@@ -1,3 +1,6 @@
+// Validation stops this too early.
+//@compile-flags: -Zmiri-disable-validation
+
 #![feature(trait_upcasting)]
 #![allow(incomplete_features)]
 
@@ -57,7 +60,7 @@ impl Baz for i32 {
 
 fn main() {
     let baz: &dyn Baz = &1;
-    let baz_fake: &dyn Bar = unsafe { std::mem::transmute(baz) };
-    let _err = baz_fake as &dyn Foo;
-    //~^ERROR: upcast on a pointer whose vtable does not match its type
+    let baz_fake: *const dyn Bar = unsafe { std::mem::transmute(baz) };
+    let _err = baz_fake as *const dyn Foo;
+    //~^ERROR: using vtable for trait `Baz` but trait `Bar` was expected
 }
diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr
index 8bac908b864..6a2415cf57e 100644
--- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr
+++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: upcast on a pointer whose vtable does not match its type
+error: Undefined Behavior: using vtable for trait `Baz` but trait `Bar` was expected
   --> $DIR/dyn-upcast-trait-mismatch.rs:LL:CC
    |
-LL |     let _err = baz_fake as &dyn Foo;
-   |                ^^^^^^^^ upcast on a pointer whose vtable does not match its type
+LL |     let _err = baz_fake as *const dyn Foo;
+   |                ^^^^^^^^ using vtable for trait `Baz` but trait `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
diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs
new file mode 100644
index 00000000000..9b1cefc4b1d
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs
@@ -0,0 +1,12 @@
+use std::mem;
+
+// Make sure we notice the mismatch also if the difference is "only" in the generic
+// parameters of the trait.
+
+trait Trait<T> {}
+impl<T> Trait<T> for T {}
+
+fn main() {
+    let x: &dyn Trait<i32> = &0;
+    let _y: *const dyn Trait<u32> = unsafe { mem::transmute(x) }; //~ERROR: wrong trait
+}
diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr
new file mode 100644
index 00000000000..1219f9f88cf
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait<u32>`, but encountered `Trait<i32>`
+  --> $DIR/wrong-dyn-trait-generic.rs:LL:CC
+   |
+LL |     let _y: *const dyn Trait<u32> = unsafe { mem::transmute(x) };
+   |                                              ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait<u32>`, but encountered `Trait<i32>`
+   |
+   = 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 $DIR/wrong-dyn-trait-generic.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
+
diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs
new file mode 100644
index 00000000000..d6049196f26
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs
@@ -0,0 +1,6 @@
+use std::{fmt, mem};
+
+fn main() {
+    let x: &dyn Send = &0;
+    let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) }; //~ERROR: wrong trait
+}
diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr
new file mode 100644
index 00000000000..e3503323b31
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `<trivial>`
+  --> $DIR/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>`
+   |
+   = 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 $DIR/wrong-dyn-trait.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
+
diff --git a/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs b/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs
index 2491bda0917..adbf5df62cc 100644
--- a/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs
+++ b/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs
@@ -25,7 +25,7 @@ impl Foo<u32> for u32 {
 impl Bar for () {}
 
 unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo<u32> + 'a)) -> u32 {
-    let foo_e: *const dyn Foo<u16> = t as *const _;
+    let foo_e: *const dyn Foo<u32> = t as *const _;
     let r_1 = foo_e as *mut dyn Foo<u32>;
 
     (&*r_1).foo(0)
diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs
index 529b9c471d4..ddc4bdcf082 100644
--- a/src/tools/miri/tests/pass/dyn-upcast.rs
+++ b/src/tools/miri/tests/pass/dyn-upcast.rs
@@ -1,24 +1,26 @@
 #![feature(trait_upcasting)]
 #![allow(incomplete_features)]
 
+use std::fmt;
+
 fn main() {
     basic();
     diamond();
     struct_();
     replace_vptr();
-    vtable_mismatch_nop_cast();
+    vtable_nop_cast();
 }
 
-fn vtable_mismatch_nop_cast() {
-    let ptr: &dyn std::fmt::Display = &0;
-    // Even though the vtable is for the wrong trait, this cast doesn't actually change the needed
-    // vtable so it should still be allowed.
-    let ptr: *const (dyn std::fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) };
-    let _ptr2 = ptr as *const dyn std::fmt::Debug;
+fn vtable_nop_cast() {
+    let ptr: &dyn fmt::Debug = &0;
+    // We transmute things around, but the principal trait does not change, so this is allowed.
+    let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) };
+    // This cast is a NOP and should be allowed.
+    let _ptr2 = ptr as *const dyn fmt::Debug;
 }
 
 fn basic() {
-    trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+    trait Foo: PartialEq<i32> + fmt::Debug + Send + Sync {
         fn a(&self) -> i32 {
             10
         }
@@ -67,7 +69,7 @@ fn basic() {
     }
 
     let baz: &dyn Baz = &1;
-    let _: &dyn std::fmt::Debug = baz;
+    let _: &dyn fmt::Debug = baz;
     assert_eq!(*baz, 1);
     assert_eq!(baz.a(), 100);
     assert_eq!(baz.b(), 200);
@@ -77,7 +79,7 @@ fn basic() {
     assert_eq!(baz.w(), 21);
 
     let bar: &dyn Bar = baz;
-    let _: &dyn std::fmt::Debug = bar;
+    let _: &dyn fmt::Debug = bar;
     assert_eq!(*bar, 1);
     assert_eq!(bar.a(), 100);
     assert_eq!(bar.b(), 200);
@@ -86,14 +88,14 @@ fn basic() {
     assert_eq!(bar.w(), 21);
 
     let foo: &dyn Foo = baz;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
     assert_eq!(foo.z(), 11);
     assert_eq!(foo.y(), 12);
 
     let foo: &dyn Foo = bar;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
     assert_eq!(foo.z(), 11);
@@ -101,7 +103,7 @@ fn basic() {
 }
 
 fn diamond() {
-    trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+    trait Foo: PartialEq<i32> + fmt::Debug + Send + Sync {
         fn a(&self) -> i32 {
             10
         }
@@ -166,7 +168,7 @@ fn diamond() {
     }
 
     let baz: &dyn Baz = &1;
-    let _: &dyn std::fmt::Debug = baz;
+    let _: &dyn fmt::Debug = baz;
     assert_eq!(*baz, 1);
     assert_eq!(baz.a(), 100);
     assert_eq!(baz.b(), 200);
@@ -178,7 +180,7 @@ fn diamond() {
     assert_eq!(baz.v(), 31);
 
     let bar1: &dyn Bar1 = baz;
-    let _: &dyn std::fmt::Debug = bar1;
+    let _: &dyn fmt::Debug = bar1;
     assert_eq!(*bar1, 1);
     assert_eq!(bar1.a(), 100);
     assert_eq!(bar1.b(), 200);
@@ -187,7 +189,7 @@ fn diamond() {
     assert_eq!(bar1.w(), 21);
 
     let bar2: &dyn Bar2 = baz;
-    let _: &dyn std::fmt::Debug = bar2;
+    let _: &dyn fmt::Debug = bar2;
     assert_eq!(*bar2, 1);
     assert_eq!(bar2.a(), 100);
     assert_eq!(bar2.c(), 300);
@@ -196,17 +198,17 @@ fn diamond() {
     assert_eq!(bar2.v(), 31);
 
     let foo: &dyn Foo = baz;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
 
     let foo: &dyn Foo = bar1;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
 
     let foo: &dyn Foo = bar2;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
 }
@@ -215,7 +217,7 @@ fn struct_() {
     use std::rc::Rc;
     use std::sync::Arc;
 
-    trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+    trait Foo: PartialEq<i32> + fmt::Debug + Send + Sync {
         fn a(&self) -> i32 {
             10
         }