Skip to content

arbitrary_self_types + derive_coerce_pointee allows calling methods whose where clauses are violated #136702

@RalfJung

Description

@RalfJung

Here's an example by @steffahn:

#![forbid(unsafe_code)]
#![feature(arbitrary_self_types, derive_coerce_pointee)]

use std::any::TypeId;
use std::marker::CoercePointee;
use std::marker::PhantomData;

#[derive(CoercePointee)]
#[repr(transparent)]
struct SelfPtr<T: ?Sized>(*const T);

impl<T: ?Sized> std::ops::Deref for SelfPtr<T> {
    type Target = T;
    fn deref(&self) -> &T {
        panic!("please don't call me, I just want the `Receiver` impl!");
    }
}

trait GetTypeId {
    fn get_type_id(self: SelfPtr<Self>) -> TypeId
    where
        Self: 'static;
}

impl<T: ?Sized> GetTypeId for PhantomData<T> {
    fn get_type_id(self: SelfPtr<Self>) -> TypeId
    where
        Self: 'static,
    {
        TypeId::of::<T>()
    }
}

// no `T: 'static` bound 🐈‍⬛ necessary
fn type_id_of<T: ?Sized>() -> TypeId {
    SelfPtr(&PhantomData::<T> as *const (dyn GetTypeId + '_) as *const (dyn GetTypeId + 'static)).get_type_id()
}
fn type_id_of_val<T: ?Sized>(_: &T) -> TypeId {
    type_id_of::<T>()
}

fn main() {
    let mut x = 1234;
    let reference = &mut x;
    let tid_of_ref = type_id_of_val(&reference);
    let closure = || *reference += 1;
    let tid_of_closure = type_id_of_val(&closure);
    dbg!(tid_of_ref, tid_of_closure);
}

The double as *const dyn GetTypeId as *const dyn GetTypeId is necessary; the second as changes the lifetime something short-lived to 'static. That means we call get_type_id on a type that does not satisfy Self: 'static.

@BoxyUwU has a similar example:

#![feature(arbitrary_self_types, derive_coerce_pointee)]

use std::marker::CoercePointee;

#[derive(CoercePointee)]
#[repr(transparent)]
struct MyPtr<T: ?Sized>(*const T);

use std::ops::Receiver;

impl<T: ?Sized> Receiver for MyPtr<T> {
    type Target = T;
}

trait Trait {
    fn foo(self: MyPtr<Self>)
    where
        Self: Send;
}

impl Trait for *mut () {
    fn foo(self: MyPtr<Self>) {
        unreachable!()
    }
}

fn main() {
    let a = 0x1 as *const *mut ();
    let a = a as *const dyn Trait as *const (dyn Trait + Send);
    MyPtr(a).foo();
}

Boxy's example is mitigated by a FCW lint. However, the lifetime example doesn't trigger a lint, and while the code above is "fine", we should at least have a good idea for why this cannot cause UB in other ways. (My gut feeling is that this can cause UB but I haven't tried.)

Cc @rust-lang/types

Metadata

Metadata

Assignees

Labels

C-bugCategory: This is a bug.F-arbitrary_self_types`#![feature(arbitrary_self_types)]`F-derive_coerce_pointeeFeature: RFC 3621's oft-renamed implementationT-langRelevant to the language team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions