Is this lifetime transmute sound?

Code:

use higher_kinded_types::ForLifetime;

fn shorten<'big: 'small, 'small, T: ForLifetime>(
    arg: &'small T::Of<'big>,
) -> &'small T::Of<'small> {
    unsafe { core::mem::transmute(arg) }
}

Here we are transmuting reference to some T: 'static to reference to same T but with non-static lifetime.

I guess it is not sound because layout of T is not guaranteed and can "possibly" be different for different lifetimes, but in practice afaik it can't happen.

Another thing is I'm not sure about variance stuff involved here. What are your thoughts about it?

You are assuming that the type is covariant in the lifetime. If it isn't, then it can lead to unsoundness. For example, imagine that T::Of<'a> = fn(&'a str). Then I could use your API to transmute from &'a fn(&'static str) to &'a fn(&'a str).

However, if I then call shorten with a function that takes a &'static str and stores it in a global, then you could use the above transmute to instead store a &'a str in the same global. Then you access the global after the end of 'a and you get a use-after-free.

5 Likes

So if I somehow force T to be covariant over lifetime then it would be sound?

You need T::Of to be covariant, not T. AFAIK there are two ways to do this:

  • write an unsafe trait that in order to be implemented by a type T requires T::Of to be covariant as a safety requirement, then this allows you to soundly write that transmute;
  • add a method to ForLifetime (or a separate trait) to make that conversion, which logically requires the type to be covariant. This is implemented in higher_kinded_types 0.2.0-rc1 as CovariantForLt. However note that in this case it would be unsound to do the transmute, you have to use the provided method since the type could still be invariant/contravariant and the method implemented by panicking/returning some dummy value.
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.