Skip to content

HRTB on subtrait unsoundly provides HTRB on supertrait with weaker implied bounds #84591

Open
@steffahn

Description

@steffahn

I’m giving an exploitation below at the end of this description. This is my interpretation of where exactly the unsoundness lies:

If I have a trait hierarchy

trait Subtrait<'a, 'b, R>: Supertrait<'a, 'b> {}
trait Supertrait<'a, 'b> {}

then a higher ranked trait bound (HRTB) like this

for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>

does/should only apply to such lifetimes 'a, 'b that fulfill the outlives-relation 'a: 'b.
('a: 'b is needed for &'b &'a () to be a valid type.)

However, the bound

for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>

appears to imply the bound

for<'a, 'b> Supertrait<'a, 'b>,

and in this one, the lifetimes 'a and 'b are universally quantified without any implicit outlives-relation.

This implication is demonstrated below:

fn need_hrtb_subtrait<S>()
where
    S: for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>,
{
    need_hrtb_supertrait::<S>() // <- this call works
}

fn need_hrtb_supertrait<S>()
where
    S: for<'a, 'b> Supertrait<'a, 'b>,
{
}

(playground)

One could of course debate whether for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> should perhaps in-fact include a for<'a, 'b> Supertrait<'a, 'b> bound, but that seems kind-of weird IMO, and also the following code demonstrates that you only need Supertrait<'a, 'b> with 'a: 'b for a fully generic Subtrait<'a, 'b, &'b &'a ()> implementation:

struct MyStruct;
impl<'a: 'b, 'b> Supertrait<'a, 'b> for MyStruct {}
impl<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {}

fn main() {
    need_hrtb_subtrait::<MyStruct>();
}

(playground)


Finally, here’s how to turn this into actual UB:

trait Subtrait<'a, 'b, R>: Supertrait<'a, 'b> {}
trait Supertrait<'a, 'b> {
    fn convert<T: ?Sized>(x: &'a T) -> &'b T;
}

fn need_hrtb_subtrait<'a_, 'b_, S, T: ?Sized>(x: &'a_ T) -> &'b_ T
where
    S: for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>,
{
    need_hrtb_supertrait::<S, T>(x) // <- this call works
}

fn need_hrtb_supertrait<'a_, 'b_, S, T: ?Sized>(x: &'a_ T) -> &'b_ T
where
    S: for<'a, 'b> Supertrait<'a, 'b>,
{
    S::convert(x)
}

struct MyStruct;
impl<'a: 'b, 'b> Supertrait<'a, 'b> for MyStruct {
    fn convert<T: ?Sized>(x: &'a T) -> &'b T {
        x
    }
}
impl<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {}

fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
    need_hrtb_subtrait::<MyStruct, T>(x)
}

fn main() {
    let d;
    {
        let x = "Hello World".to_string();
        d = extend_lifetime(&x);
    }
    println!("{}", d);
}
��~��U�

(playground)

This demonstration compiles since Rust 1.7.

@rustbot modify labels: T-compiler, A-traits, A-lifetimes, A-typesystem
and someone please add “I-unsound 💥”.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)A-implied-boundsArea: Implied bounds / inferred outlives-boundsA-lifetimesArea: Lifetimes / regionsA-trait-systemArea: Trait systemA-type-systemArea: Type systemC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-highHigh priorityS-bug-has-testStatus: This bug is tracked inside the repo by a `known-bug` test.T-compilerRelevant to the compiler 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

    Status

    new solver everywhere

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions