Skip to content

A really sad solution to extern type: special case reference-to-ZST #305

Open
@CAD97

Description

@CAD97

The topical issue is that it is common for FFI libraries (and even pure-Rust libraries, sometimes) to use &(), &[Align; 0], &c_void, or some newtype wrappers of such as an opaque shared pointer to "some opaque object" which otherwise fits with Rust's & semantics.

The rustnomicon recommends this ZST approach (with ?Sync, ?Send, ?Unpin markers) for opaque objects, and bindgen uses it, but both manage to only ever mention using it with raw pointers, never with references. However, they also don't warn off using references, and I know it's not uncommon to see in practice (e.g. cxx will freely emit references to extern FFI types).

The problem comes from SB's reborrowing: this reduces the provenance of the reference to just what the ZST type indicates – zero bytes – even though it's trying to represent a type of unknown size rather than one of known zero size.

The proper solution is extern type (and SB to accommodate it), no questions asked. But extern type has been unstable for a long time, and the longer it's unstable, the more code which is using &ZST will emerge.

So I thought of a really sad way of handling this without invasively changing a lot of SB's provenance handling: relax reborrowing for just reference-to-ZST. This explicitly ignores the &Header-to-VLA use-case in favor of minimally patching SB to support &OpaqueZST.

I don't think it'd be as simple as just not emitting reborrow guards for reference-to-ZST, but since real reference-to-ZST can't be used to access memory locations anyway, it should be sound to allow them to carry around (potentially invalidated) provenance, which could be then used when cast back to a pointer.

More formally, with the snippet

let a: *const [u8; 8];
let b = &*(a as *const [u8; 0]);
let c = a as *const [u8; 0] as *const [u8; 8]; 

No matter a's provenance, current SB (AIUI) would give b Shr provenance over 0 bytes, and thus c also has provenance over 0 bytes. Under this "solution", because b is a reference-to-ZST, it would retain Raw provenance equal to that of a (note that gaining Shr provenance to more than 0 bytes would be potentially an unsound race introduction), which would then be inherited by c as well, allowing c to be validly used.

I describe this as a sad solution, because the full solution would relax the requirements to handle &Header-to-VLA as well, and this is an ugly special case in an otherwise very uniform model.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions