Skip to content

[pointer] Support read-only aliasing; use in TryFromBytes::is_bit_valid #2336

Open
@joshlf

Description

@joshlf

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:

  • Src with validity Valid
  • Dst with validity Initialized
  • Dst with validity Valid

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

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.

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