-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pointer casts allow switching trait parameters for trait objects, which can be unsound with raw pointers as receiver types under feature(arbitrary_self_types)
#120217
Comments
Uhm… perhaps nevermind on that recent regression point, I guess? Apparently we can also do the following just fine™ since around Rust // edition 2015
#![allow(bare_trait_objects)]
trait Trait<T> {}
fn foo(x: *const Trait<u8>) -> *const Trait<u16> {
x as *const Trait<u16>
} (Let me work on a better code example for unsoundness from this.) Edit: For example: #![feature(arbitrary_self_types)]
trait Trait<S, T> {
fn convert(self: *const Self, x: S) -> T;
}
impl<T> Trait<T, T> for [T; 0] {
fn convert(self: *const Self, x: T) -> T {
x
}
}
fn transmute<S, T>(x: S) -> T {
(&[] as *const dyn Trait<S, S> as *const dyn Trait<S, T>).convert(x)
}
fn main() {
let n = 1;
println!(
"much data, such joy: {:?}",
transmute::<&u8, &[u8; u32::MAX as _]>(&n)
);
} |
feature(arbitrary_self_types)
Requiring pointers to have valid vtables while allowing these casts seems pretty conflicting and impossible. cc @rust-lang/opsem @rust-lang/lang who have previously been discussing whether raw pointers should have valid vtables. |
This wasn't just discussed, it was decided that raw ptrs have valid vtables as a safety invariant as part of dyn trait upcasting. However, this being about safety invariants, it's more a @rust-lang/types question than an opsem one. |
If a safety invariant can be broken by safe code that's uh, not good. |
Agreed - IMO if arbitrary self types supports calls into |
Ideally we can fix things so that safe code can't arbitrarily manipulate this. #120222 is another issue caused by vtable manipulation. Let's see how that one gets resolved (it's a lot more critical since the feature it affects almost got stabilized in 2 weeks), then we can use that to decide what to do about arbitrary-self. |
…r=<try> Make casts of pointers to trait objects stricter This is an attempt to `fix` rust-lang#120222 and rust-lang#120217. cc `@oli-obk` `@compiler-errors` `@lcnr`
Issue #101336 for anyone looking. |
@apiraino Both The unsoundness of Both issues could be fixed by disallowing casts like An alternative set of fixes is to change the vtable layout to fix |
@apiraino The summary from @lukas-code above is already good. As for what the revert of stabilization of |
WG-prioritization assigning priority (Zulip discussion). @rustbot label -I-prioritize +P-medium |
…r=<try> Make casts of pointers to trait objects stricter This is an attempt to `fix` rust-lang#120222 and rust-lang#120217. This is done by adding restrictions on casting pointers to trait objects. Currently the restriction is > When casting `*const X<dyn A>` -> `*const Y<dyn B>`, if `B` has a principal trait, `dyn A + 'erased: Unsize<dyn B + 'erased>` must hold This ensures that 1. Principal trait's generic arguments match (no `*const dyn Tr<A>` -> `*const dyn Tr<B>` casts, which are a problem for [rust-lang#120222](rust-lang#120222)) 2. Principal trait's lifetime arguments match (no `*const dyn Tr<'a>` -> `*const dyn Tr<'b>` casts, which are a problem for [rust-lang#120217](rust-lang#120217)) 3. No auto traits can be _added_ (this is a problem for arbitrary self types, see [this comment](rust-lang#120248 (comment))) Some notes: - We only care about the metadata/last field, so you can still cast `*const dyn T` to `*const WithHeader<dyn T>`, etc - The lifetime of the trait object itself (`dyn A + 'lt`) is not checked, so you can still cast `*mut FnOnce() + '_` to `*mut FnOnce() + 'static`, etc - This feels fishy, but I couldn't come up with a reason it must be checked - The new checks are only done if `B` has a principal, so you can still do any kinds of cast, if the target only has auto traits - This is because auto traits are not enough to get unsoundness issues that this PR fixes - ...and so it makes sense to minimize breakage The plan is to, ~~once the checks are properly implemented~~, run crate to determine how much damage does this do. The diagnostics are currently not great, to say the least, but as far as I can tell this correctly fixes the issues. cc `@oli-obk` `@compiler-errors` `@lcnr`
…, r=compiler-errors,oli-obk,lnicola Make casts of pointers to trait objects stricter This is an attempt to `fix` rust-lang#120222 and rust-lang#120217. This is done by adding restrictions on casting pointers to trait objects. Before this PR the rules were as follows: > When casting `*const X<dyn A>` -> `*const Y<dyn B>`, principal traits in `A` and `B` must refer to the same trait definition (or no trait). With this PR the rules are changed to > When casting `*const X<dyn Src>` -> `*const Y<dyn Dst>` > - if `Dst` has a principal trait `DstP`, > - `Src` must have a principal trait `SrcP` > - `dyn SrcP` and `dyn DstP` must be the same type (modulo the trait object lifetime, `dyn T+'a` -> `dyn T+'b` is allowed) > - Auto traits in `Dst` must be a subset of auto traits in `Src` > - Not adhering to this is currently a FCW (warn-by-default + `FutureReleaseErrorReportInDeps`), instead of an error > - if `Src` has a principal trait `Dst` must as well > - this restriction will be removed in a follow up PR This ensures that 1. Principal trait's generic arguments match (no `*const dyn Tr<A>` -> `*const dyn Tr<B>` casts, which are a problem for [rust-lang#120222](rust-lang#120222)) 2. Principal trait's lifetime arguments match (no `*const dyn Tr<'a>` -> `*const dyn Tr<'b>` casts, which are a problem for [rust-lang#120217](rust-lang#120217)) 3. No auto traits can be _added_ (this is a problem for arbitrary self types, see [this comment](rust-lang#120248 (comment))) Some notes: - We only care about the metadata/last field, so you can still cast `*const dyn T` to `*const WithHeader<dyn T>`, etc - The lifetime of the trait object itself (`dyn A + 'lt`) is not checked, so you can still cast `*mut FnOnce() + '_` to `*mut FnOnce() + 'static`, etc - This feels fishy, but I couldn't come up with a reason it must be checked The diagnostics are currently not great, to say the least, but as far as I can tell this correctly fixes the issues. cc `@oli-obk` `@compiler-errors` `@lcnr`
Rollup merge of rust-lang#120248 - WaffleLapkin:bonk-ptr-object-casts, r=compiler-errors,oli-obk,lnicola Make casts of pointers to trait objects stricter This is an attempt to `fix` rust-lang#120222 and rust-lang#120217. This is done by adding restrictions on casting pointers to trait objects. Before this PR the rules were as follows: > When casting `*const X<dyn A>` -> `*const Y<dyn B>`, principal traits in `A` and `B` must refer to the same trait definition (or no trait). With this PR the rules are changed to > When casting `*const X<dyn Src>` -> `*const Y<dyn Dst>` > - if `Dst` has a principal trait `DstP`, > - `Src` must have a principal trait `SrcP` > - `dyn SrcP` and `dyn DstP` must be the same type (modulo the trait object lifetime, `dyn T+'a` -> `dyn T+'b` is allowed) > - Auto traits in `Dst` must be a subset of auto traits in `Src` > - Not adhering to this is currently a FCW (warn-by-default + `FutureReleaseErrorReportInDeps`), instead of an error > - if `Src` has a principal trait `Dst` must as well > - this restriction will be removed in a follow up PR This ensures that 1. Principal trait's generic arguments match (no `*const dyn Tr<A>` -> `*const dyn Tr<B>` casts, which are a problem for [rust-lang#120222](rust-lang#120222)) 2. Principal trait's lifetime arguments match (no `*const dyn Tr<'a>` -> `*const dyn Tr<'b>` casts, which are a problem for [rust-lang#120217](rust-lang#120217)) 3. No auto traits can be _added_ (this is a problem for arbitrary self types, see [this comment](rust-lang#120248 (comment))) Some notes: - We only care about the metadata/last field, so you can still cast `*const dyn T` to `*const WithHeader<dyn T>`, etc - The lifetime of the trait object itself (`dyn A + 'lt`) is not checked, so you can still cast `*mut FnOnce() + '_` to `*mut FnOnce() + 'static`, etc - This feels fishy, but I couldn't come up with a reason it must be checked The diagnostics are currently not great, to say the least, but as far as I can tell this correctly fixes the issues. cc `@oli-obk` `@compiler-errors` `@lcnr`
This is believed to be fixed by: |
…ler-errors,oli-obk,lnicola Make casts of pointers to trait objects stricter This is an attempt to `fix` rust-lang/rust#120222 and rust-lang/rust#120217. This is done by adding restrictions on casting pointers to trait objects. Before this PR the rules were as follows: > When casting `*const X<dyn A>` -> `*const Y<dyn B>`, principal traits in `A` and `B` must refer to the same trait definition (or no trait). With this PR the rules are changed to > When casting `*const X<dyn Src>` -> `*const Y<dyn Dst>` > - if `Dst` has a principal trait `DstP`, > - `Src` must have a principal trait `SrcP` > - `dyn SrcP` and `dyn DstP` must be the same type (modulo the trait object lifetime, `dyn T+'a` -> `dyn T+'b` is allowed) > - Auto traits in `Dst` must be a subset of auto traits in `Src` > - Not adhering to this is currently a FCW (warn-by-default + `FutureReleaseErrorReportInDeps`), instead of an error > - if `Src` has a principal trait `Dst` must as well > - this restriction will be removed in a follow up PR This ensures that 1. Principal trait's generic arguments match (no `*const dyn Tr<A>` -> `*const dyn Tr<B>` casts, which are a problem for [#120222](rust-lang/rust#120222)) 2. Principal trait's lifetime arguments match (no `*const dyn Tr<'a>` -> `*const dyn Tr<'b>` casts, which are a problem for [#120217](rust-lang/rust#120217)) 3. No auto traits can be _added_ (this is a problem for arbitrary self types, see [this comment](rust-lang/rust#120248 (comment))) Some notes: - We only care about the metadata/last field, so you can still cast `*const dyn T` to `*const WithHeader<dyn T>`, etc - The lifetime of the trait object itself (`dyn A + 'lt`) is not checked, so you can still cast `*mut FnOnce() + '_` to `*mut FnOnce() + 'static`, etc - This feels fishy, but I couldn't come up with a reason it must be checked The diagnostics are currently not great, to say the least, but as far as I can tell this correctly fixes the issues. cc `@oli-obk` `@compiler-errors` `@lcnr`
…ler-errors,oli-obk,lnicola Make casts of pointers to trait objects stricter This is an attempt to `fix` rust-lang/rust#120222 and rust-lang/rust#120217. This is done by adding restrictions on casting pointers to trait objects. Before this PR the rules were as follows: > When casting `*const X<dyn A>` -> `*const Y<dyn B>`, principal traits in `A` and `B` must refer to the same trait definition (or no trait). With this PR the rules are changed to > When casting `*const X<dyn Src>` -> `*const Y<dyn Dst>` > - if `Dst` has a principal trait `DstP`, > - `Src` must have a principal trait `SrcP` > - `dyn SrcP` and `dyn DstP` must be the same type (modulo the trait object lifetime, `dyn T+'a` -> `dyn T+'b` is allowed) > - Auto traits in `Dst` must be a subset of auto traits in `Src` > - Not adhering to this is currently a FCW (warn-by-default + `FutureReleaseErrorReportInDeps`), instead of an error > - if `Src` has a principal trait `Dst` must as well > - this restriction will be removed in a follow up PR This ensures that 1. Principal trait's generic arguments match (no `*const dyn Tr<A>` -> `*const dyn Tr<B>` casts, which are a problem for [#120222](rust-lang/rust#120222)) 2. Principal trait's lifetime arguments match (no `*const dyn Tr<'a>` -> `*const dyn Tr<'b>` casts, which are a problem for [#120217](rust-lang/rust#120217)) 3. No auto traits can be _added_ (this is a problem for arbitrary self types, see [this comment](rust-lang/rust#120248 (comment))) Some notes: - We only care about the metadata/last field, so you can still cast `*const dyn T` to `*const WithHeader<dyn T>`, etc - The lifetime of the trait object itself (`dyn A + 'lt`) is not checked, so you can still cast `*mut FnOnce() + '_` to `*mut FnOnce() + 'static`, etc - This feels fishy, but I couldn't come up with a reason it must be checked The diagnostics are currently not great, to say the least, but as far as I can tell this correctly fixes the issues. cc `@oli-obk` `@compiler-errors` `@lcnr`
This particular exploitation is possible since #113262. I’m not certain if there wasn’t any way to convince the compiler to do such casts before that; if there wasn’t, then that PR was definitely a lot more than “a bugfix”.Edit: Turns out, there is a way, see my first answer below. (Does this mean the regression label should be removed, since the regression only extended the scope of the issue a bit? So this becomes an issue for
F-arbitrary_self_types
in general then? Should we also addrequires-nightly
label?)So apparently, the compiler doesn’t care about lifetimes for trait object metadata when checking casts between pointers. This means I can coerce
*const dyn Foo<'a>
into*const dyn Foo<'b>
without restrictions. Since vtables logically contain function pointers, like for example a vtable fortrait Foo<'a> { fn foo(&self) -> &'a str }
logically contains something likeunsafe fn(*const ()) -> &'a str
this results in casting the types of these function pointers.Now one could argue “it’s fine, they are raw pointers, you can’t do anything with
*const dyn NotQuiteTheRightTrait
”, but as far as I’m aware, the story on that is that you can, vtables must be valid (at least as a soundness invariant, not necessarily promising instant UB), and we should not breakarbitrary_self_types
’s soundness this way, for now.And with
arbitrary_self_types
, unsoundness there is!(playground)
@rustbot label +I-unsound +F-arbitrary_self_types +A-lifetimes +A-coercions +A-trait-objects +requires-nightly
The text was updated successfully, but these errors were encountered: