Closed arielb1 closed 4 years ago
Related:
use std::fmt;
trait Foo {
type Assoc: 'static;
}
fn foo<T: Foo+?Sized>(t: T::Assoc) -> Box<fmt::Display+'static>
where T::Assoc: fmt::Display {
Box::new(t)
}
fn wat() -> Box<fmt::Display+'static> {
let x = 42;
foo::<Foo<Assoc=&u32>>(&x)
}
fn main() {
println!("{}", wat());
}
Interesting. I agree that it should be illegal and we should amend WF relations for object types to check that the bindings cover the requirements of the trait -- the RFC was incomplete on this topic.
The example from the first comment (https://github.com/rust-lang/rust/issues/27675#issuecomment-130067761) is actually scarier than the original bug description; at least, I usually can think "oh, an ICE, well that might just be something that won't actually ever compile"; but the first comment is showing code that is indeed a case of "wat"!
triage: P-medium
Since this has not been demonstrated: We can indeed build a safe, arbitrary transmutation primitive from this, though this will ICE in practice except if used only for the purpose of the second comment, casting away lifetimes.
trait Id<T>: Sized {
fn id(self) -> T;
}
impl<T> Id<T> for T {
fn id(self) -> T { self }
}
trait Setup<T> {
type From: Id<T>;
}
fn transmute<T, U: Setup<T> + ?Sized>(from: U::From) -> T {
Id::id(from)
}
// compiles fine
pub fn safe_transmute<T, U>(t: T) -> U {
transmute::<U, dyn Setup<U, From=T>>(t)
}
fn main() {
let static_word = {
let st = String::from("Hello!");
safe_transmute::<_, &'static mut str>(&*st)
};
println!("{:?}", static_word);
}
// ICE
fn main() {
safe_transmute::<usize, &'static str>(0usize);
}
Uuups, we can make any type Copy
. That's quite severe, wouldn't you say?
trait Setup {
type From: Copy;
}
fn copy<U: Setup + ?Sized>(from: &U::From) -> U::From {
*from
}
pub fn copy_any<T>(t: &T) -> T {
copy::<dyn Setup<From=T>>(t)
}
fn main() {
let st = String::from("Hello");
copy_any(&st);
}
Feature unsize
gives the ability to do some other interesting coercions that enable arbitrary sized reference transmute.
#![feature(unsize)]
trait Setup<T> {
type From: std::marker::Unsize<[T]>;
}
fn unsize<T, U: Setup<T> + ?Sized>(from: &U::From) -> &[T] {
from
}
fn unsize_to_any<T, U>(t: &T) -> &U {
&unsize::<U, dyn Setup<U, From=[T]>>(core::slice::from_ref(t))[0]
}
fn main() {
let trust_me_im_64: u8 = 0;
println!("{}", unsize_to_any::<u8, u64>(&trust_me_im_64))
}
Decided on P-high
as discussed in the prioritization working group procedure.
For completeness sake. This may be used to send any reference across threads:
trait Id<T> {
fn id(&self) -> &T;
}
impl<T> Id<T> for T {
fn id(&self) -> &T { self }
}
trait SyncSetup<T> {
type From: Id<T> + Sync + 'static;
}
fn cast<T, U>(from: &T) -> &(dyn Id<U> + Sync) {
fn do_it<T, U: ?Sized + SyncSetup<T>>(val: &U::From) -> &(dyn Id<T> + Sync) {
val
}
do_it::<U, dyn SyncSetup<U, From=T>>(from)
}
fn syncify<T>(from: &T) -> &'static (dyn Id<T> + Sync) {
// Very carefully avoid ICE. This only adds marker trait to an unsized trait.
let x: &(dyn Id<T> + Sync) = cast(from);
// Indirect over another reference and make `x` appear `&'static`.
// Lifetime change is invisible to trait materialization checks.
let y: &(dyn Id<&'static (dyn Id<T> + Sync)>) = cast(&x);
// .. and grab that one.
let x: &'static (dyn Id<T> + Sync) = *(y.id());
x
}
use std::cell::Cell;
fn main() {
let st = Cell::new(0u32);
let x = syncify(&st);
std::thread::spawn(move || {
x.id().set(1);
});
while st.get() == 0 {}
println!("Spooky");
}
I suspect that this will be fixed by https://github.com/rust-lang/rust/pull/73905
The 4 weaponized examples above will indeed be rejected with #73905
A regression test was added in https://github.com/rust-lang/rust/pull/77663.
STR
Result
I guess this should be banned.
cc @nikomatsakis