Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it safe to treat an arbitrary reference as an &[()]? #557

Closed
Manishearth opened this issue Feb 16, 2025 · 5 comments
Closed

Is it safe to treat an arbitrary reference as an &[()]? #557

Manishearth opened this issue Feb 16, 2025 · 5 comments

Comments

@Manishearth
Copy link
Member

Manishearth commented Feb 16, 2025

&[()] and other ZST references/slices point to empty regions of memory, but they can have a pointer value.

Would there ever be a safety hazard with the following API?

fn to_zst(slice: &[u8]) -> &[()] {
  slice::from_raw_parts(slice as *const u8 as *const (), slice.len());
}

What about a generic source type T? What about cases where the produced slice is always empty?

This falls afoul of C++-style strict aliasing rules which we do not have in ICU4X Rust, but I'm not clear on what this means for pointer provenance stuff.

@CAD97
Copy link

CAD97 commented Feb 16, 2025

There's no issue with the cast in this direction, as references to zero bytes are always valid to dereference1. If the intent is to cast back in the other direction, though, that runs into the usual &Header issue with precise reborrowing.

It's also relevant to note that not all API which deals in slices is certain to maintain the input provenance for references to ZST (although std does attempt to do so).

Footnotes

  1. There's some potential for trickery with a reference to a deallocated object, but that's prevented in this case by the involved lifetimes. And I think we determined that that's not going to be an issue either, I'm just not fully sure atm.

@zachs18
Copy link

zachs18 commented Feb 16, 2025

Another possible issue for the other direction is interior mutability

[...] If there is no UnsafeCell in the type, we also assume there is no UnsafeCell "around" it. This is important, otherwise we would have to ditch all the shared reference optimizations. If you use type erasure schemes (like accessing the full data with just the Header) mixed with references, it is your responsibility to preserve all the important type information, like UnsafeCell.

#256 (comment)

i.e. if the generic source type T has interior mutability, and you "cast" &T to &[()] and back, the resulting &T is probably UB to modify through (at least), even if the cast would otherwise be okay for a type without interior mutability.

cc ferrilab/bitvec#283

@RalfJung
Copy link
Member

RalfJung commented Feb 16, 2025

Is it safe to treat an arbitrary reference as an &[()]?

Yes. For references to ZST, the only things you have to worry about are null and alignment. But if you start with a reference, it is nun-null, and [()] has an alignment requirement of 1 which is always satisfied.

"cast" &T to &[()] and back,

Casting back does not work at all under Stacked Borrows, even without an UnsafeCell being involved -- &[()] has permission to access 0 bytes and that also constrains all references and pointers derived from it.

But the OP did not ask about casting back.

This falls afoul of C++-style strict aliasing rules

Rust does not have those. The only aliasing rules we have are the ones coming from uniqueness of &mut and immutability of &, i.e. Stacked/Tree/... Borrows.

@Manishearth
Copy link
Member Author

Thanks!!

Rust does not have those

Yeah I tried to mention that in my post and I accidentally said ICU4X instead of Rust.

I figured it would be fine, but I've also learned to be wary of ZST semantics.

And yep, casting back is much more clearly a provenance issue, fortunately I don't care about that operation!

@RalfJung
Copy link
Member

The question seems to be answered then :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants