Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to transmute::<&'a Arc<T>, &'a Weak<T>>(…)?

Tags:

rust

unsafe

Is it safe to transmute a shared reference & to a strong Arc<T> into a shared reference & to a Weak<T>?

To ask another way: is the following safe function sound, or is it a vulnerability waiting to happen?

pub fn as_weak<'a, T>(strong: &'a Arc<T>) -> &'a Weak<T> {
    unsafe { transmute::<&'a Arc<T>, &'a Weak<T>>(strong) }
}

Why I want to do this

We have an existing function that returns a &Weak<T>. The internal data structure has changed a bit, and I now have an Arc<T> where I previously had a Weak<T>, but I need to maintain semver compatibility with this function's interface. I'd rather avoid needing to stash an actual Weak<T> copy just for the sake of this function if I don't need to.

Why I hope this is safe

The underlying memory representations of Arc<T> and Weak<T> are the same: a not-null pointer (or pointer-like value for Weak::new()) to an internal ArcInner struct, which contains the strong and weak reference counts and the inner T value.

Arc<T> also contains a PhantomData<T>, but my understanding is that if that changes anything, it would only apply on drop, which isn't relevant for the case here as we're only transmuting a shared reference, not an owned value.

The operations that an Arc<T> will perform on its inner pointer are presumably a superset of those that may be performed by a Weak<T>, since they have the same representation but Arc carries a guarantee that the inner T value is still alive, while Weak does not.

Given these facts, it seems to me like nothing could go wrong. However, I haven't written much unsafe code before, and never for a production case like this. I'm not confident that I fully understand the possible issues. Is this transmutation safe and sound, or are are there other factors that need to be considered?


1 Answers

No, this is not sound.

Neither Arc nor Weak has a #[repr] forcing a particular layout, therefore they are both #[repr(Rust)] by default. According to the Rustonomicon section about repr(Rust):

struct A {
    a: i32,
    b: u64,
}

struct B {
    a: i32,
    b: u64,
}

Rust does guarantee that two instances of A have their data laid out in exactly the same way. However Rust does not currently guarantee that an instance of A has the same field ordering or padding as an instance of B.

You cannot therefore assume that Arc<T> and Weak<T> have the same layout.

like image 75
cdhowie Avatar answered Sep 20 '25 00:09

cdhowie