rust-lang / unsafe-code-guidelines

Forum for discussion about what unsafe code can and can't do
https://rust-lang.github.io/unsafe-code-guidelines
Apache License 2.0
670 stars 58 forks source link

Is `usize::MAX` an allocatable address? #508

Closed CAD97 closed 6 months ago

CAD97 commented 6 months ago

Because if so, an inbounds pointer offset doesn't actually imply nowrap.

We permit the use of non-wrapping pointer offsets where

I believe this has the effect of forbidding an allocation of n bytes from ever (soundly) being addressed at 0.wrapping_sub(n), as this would cause the one-past-the-end pointer to wrap around the address space and be null.

I don't think this should ever practically cause an issue, but it seems relevant to note, and could be exploited by unsafe code; e.g. while the traditional align_of::<T>() as *mut T aligned-dangling pointer can't be used as a sentinel, as it's a potentially valid address for a real allocated T, -(align_of::<T>() as isize) as *mut T would not be, if ptr.add(1) is guaranteed non-null for every (sound) allocated object.

CAD97 commented 6 months ago

This came up because I was about to claim that Weak::into_raw cannot guarantee to return an aligned non-null pointer in the face of sentinel dangling weak references (Weak::new) existing, but realized that -align could potentially be a valid sentinel which is both aligned and non-null but can't ever point to a valid object.

chorman0773 commented 6 months ago

Considering the fact you can take a reference to any allocation you own the base pointer to, and then slice-index that, I'd assume no, it's not a valid address.

Lokathor commented 6 months ago

I asked this recently and someone pointed out that you can put a ZST at that address, including a slice of ZST

chorman0773 commented 6 months ago

That's a ZST though. Zero sized allocations can exist anywhere. If Box::new([0u8]) were placed at usize::MAX, you could safely take &x[1..] and get a null reference, which is UB. Same with static or let, which takes care of all the possible ways you can allocate at the AM level.

Lokathor commented 6 months ago

Correct, only a ZST could be put at the last byte.

RalfJung commented 6 months ago

https://github.com/rust-lang/rust/pull/116675 answers this: we have base + size <= usize::MAX. So usize::MAX can never be inside an allocation but it can be one-past-the-end of an allocation. ZST are the only cases where the address the reference points to equals the one-past-the-end address, and therefore they are the only case where the reference can have address usize::MAX.