diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-04-23 06:24:57 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-23 06:24:57 +0200 |
| commit | 8039488e595276ec3ae9ceb09af434d375281a57 (patch) | |
| tree | 7ceb2d4387ba3732c7fa6caa66e05b72fee54d6a /src | |
| parent | 5800e2a6f9e6d70adfe225e9163d3e31f4236021 (diff) | |
| parent | 875f0c2da05da9a7620afd6e2be04fbcb9a4b395 (diff) | |
| download | rust-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')
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 } |
