-
Notifications
You must be signed in to change notification settings - Fork 128
Description
TryFromBytes::is_bit_valid is currently generic over aliasing, permitting both Shared and Exclusive Ptrs.
In adding support for runtime-checked type-to-type transmutation (#1359), we ran into a problem. TryFromBytes::is_bit_valid needs to be able to read the bytes of its referent, but cannot assume that those bytes are bit-valid, so it takes a Ptr<Self> with Initialized validity. Such a Ptr is equivalent to a Ptr<[u8]> with Valid validity.
A checked transmute which uses TryFromBytes::is_bit_valid thus takes a type through three representations:
Srcwith validityValidDstwith validityInitializedDstwith validityValid
Current TryFromBytes methods always take &[u8] or &mut [u8] as a source type. As a result, two representation paths are possible (note that, here, & and &mut are used as shorthand for shared or exclusive Ptrs):
&[u8] -> &[u8] -> &Dst&mut [u8] -> &mut [u8] -> &mut Dst
The middle types in both cases are actually Initialized Ptr<Dst>s, but these have the same validity as [u8].
When supporting source types other than [u8], this can become problematic. Consider transmuting a bool into a bool:
&bool -> &[u8] -> &bool&mut bool -> &mut [u8] -> &mut bool
Shared references are not problematic, but exclusive references pose a problem: An Initialized Ptr<bool>, which is effectively a &mut [u8], can be used to write arbitrary bytes to its referent. However, this permits writing arbitrary bytes which will later be observed as a &mut bool, which is unsound. Thus, the first step (&mut bool -> &mut [u8]) is unsound.
It would be tempting to solve this using reborrowing: First reborrow &mut bool as &bool, then convert it to a Ptr<bool, (Shared, Valid)> in order to call <bool as TryFromBytes>::is_bit_valid. Unfortunately, this is insufficient: types with interior mutability can use this type to mutate their referent despite being behind a shared Ptr.
The solution proposed by this issue is a read-only type. This could be instantiated as a ReadOnly type a la #1760 or as a new aliasing mode, although the former is probably simpler. Unlike #1760, this ReadOnly type would disable any mutation whatsoever, including through &mut references. In particular, ReadOnly<T>: AsRef<T> where T: Immutable.
Since neither &ReadOnly<T> nor &mut ReadOnly<T> permit mutation, sequences like the following would be sound:
&mut bool -> &mut ReadOnly<[u8]> -> &mut bool&Cell<bool> -> &ReadOnly<Cell<[u8]>> -> &bool
IMPORTANT: In order to make this work, we'd need to figure out how to prevent a &mut ReadOnly<T> from being overwritten in its entirety (e.g. via mem::swap). This limitation may require us to abandon ReadOnly as a type and instead make it an aliasing invariant.