zerocopy/pointer/mod.rs
1// Copyright 2023 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9//! Abstractions over raw pointers.
10
11mod inner;
12#[doc(hidden)]
13pub mod invariant;
14mod ptr;
15mod transmute;
16
17#[doc(hidden)]
18pub use {inner::PtrInner, transmute::*};
19#[doc(hidden)]
20pub use {
21 invariant::{BecauseExclusive, BecauseImmutable, Read},
22 ptr::Ptr,
23};
24
25/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
26/// to [`TryFromBytes::is_bit_valid`].
27///
28/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
29pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> =
30 Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;
31
32/// Checks if the referent is zeroed.
33pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, T, I>) -> bool
34where
35 T: crate::Immutable + crate::KnownLayout,
36 I: invariant::Invariants<Validity = invariant::Initialized>,
37 I::Aliasing: invariant::Reference,
38{
39 ptr.as_bytes::<BecauseImmutable>().as_ref().iter().all(|&byte| byte == 0)
40}
41
42#[doc(hidden)]
43pub mod cast {
44 use core::{marker::PhantomData, mem};
45
46 use crate::{
47 layout::{SizeInfo, TrailingSliceLayout},
48 HasField, KnownLayout, PtrInner,
49 };
50
51 /// A pointer cast or projection.
52 ///
53 /// # Safety
54 ///
55 /// The implementation of `project` must satisfy its safety post-condition.
56 pub unsafe trait Project<Src: ?Sized, Dst: ?Sized> {
57 /// Projects a pointer from `Src` to `Dst`.
58 ///
59 /// # Safety
60 ///
61 /// The returned pointer refers to a non-strict subset of the bytes of
62 /// `src`'s referent, and has the same provenance as `src`.
63 fn project(src: PtrInner<'_, Src>) -> *mut Dst;
64 }
65
66 /// A [`Project`] which preserves the address of the referent – a pointer
67 /// cast.
68 ///
69 /// # Safety
70 ///
71 /// A `Cast` projection must preserve the address of the referent. It may
72 /// shrink the set of referent bytes, and it may change the referent's type.
73 pub unsafe trait Cast<Src: ?Sized, Dst: ?Sized>: Project<Src, Dst> {}
74
75 /// A no-op pointer cast.
76 #[derive(Default, Copy, Clone)]
77 #[allow(missing_debug_implementations)]
78 pub struct IdCast;
79
80 // SAFETY: `project` returns its argument unchanged, and so it is a
81 // provenance-preserving projection which preserves the set of referent
82 // bytes.
83 unsafe impl<T: ?Sized> Project<T, T> for IdCast {
84 #[inline(always)]
85 fn project(src: PtrInner<'_, T>) -> *mut T {
86 src.as_ptr()
87 }
88 }
89
90 // SAFETY: The `Project::project` impl preserves referent address.
91 unsafe impl<T: ?Sized> Cast<T, T> for IdCast {}
92
93 /// A pointer cast which preserves or shrinks the set of referent bytes of
94 /// a statically-sized referent.
95 ///
96 /// # Safety
97 ///
98 /// The implementation of [`Project`] uses a compile-time assertion to
99 /// guarantee that `Dst` is no larger than `Src`. Thus, `CastSized` has a
100 /// sound implementation of [`Project`] for all `Src` and `Dst` – the caller
101 /// may pass any `Src` and `Dst` without being responsible for soundness.
102 #[allow(missing_debug_implementations, missing_copy_implementations)]
103 pub enum CastSized {}
104
105 // SAFETY: By the `static_assert!`, `Dst` is no larger than `Src`,
106 // and so all casts preserve or shrink the set of referent bytes. All
107 // operations preserve provenance.
108 unsafe impl<Src, Dst> Project<Src, Dst> for CastSized {
109 #[inline(always)]
110 fn project(src: PtrInner<'_, Src>) -> *mut Dst {
111 static_assert!(Src, Dst => mem::size_of::<Src>() >= mem::size_of::<Dst>());
112 src.as_ptr().cast::<Dst>()
113 }
114 }
115
116 // SAFETY: The `Project::project` impl preserves referent address.
117 unsafe impl<Src, Dst> Cast<Src, Dst> for CastSized {}
118
119 /// A pointer cast which preserves or shrinks the set of referent bytes of
120 /// a dynamically-sized referent.
121 ///
122 /// # Safety
123 ///
124 /// The implementation of [`Project`] uses a compile-time assertion to
125 /// guarantee that the cast preserves the set of referent bytes. Thus,
126 /// `CastUnsized` has a sound implementation of [`Project`] for all `Src`
127 /// and `Dst` – the caller may pass any `Src` and `Dst` without being
128 /// responsible for soundness.
129 #[allow(missing_debug_implementations, missing_copy_implementations)]
130 pub enum CastUnsized {}
131
132 // SAFETY: The `static_assert!` ensures that `Src` and `Dst` have the same
133 // `SizeInfo`. Thus, casting preserves the set of referent bytes. All
134 // operations are provenance-preserving.
135 unsafe impl<Src, Dst> Project<Src, Dst> for CastUnsized
136 where
137 Src: ?Sized + KnownLayout,
138 Dst: ?Sized + KnownLayout<PointerMetadata = Src::PointerMetadata>,
139 {
140 #[inline(always)]
141 fn project(src: PtrInner<'_, Src>) -> *mut Dst {
142 // FIXME:
143 // - Is the alignment check necessary for soundness? It's not
144 // necessary for the soundness of the `Project` impl, but what
145 // about the soundness of particular use sites?
146 // - Do we want this to support shrinking casts as well?
147 static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => {
148 let t = <Src as KnownLayout>::LAYOUT;
149 let u = <Dst as KnownLayout>::LAYOUT;
150 t.align.get() >= u.align.get() && match (t.size_info, u.size_info) {
151 (SizeInfo::Sized { size: t }, SizeInfo::Sized { size: u }) => t == u,
152 (
153 SizeInfo::SliceDst(TrailingSliceLayout { offset: t_offset, elem_size: t_elem_size }),
154 SizeInfo::SliceDst(TrailingSliceLayout { offset: u_offset, elem_size: u_elem_size })
155 ) => t_offset == u_offset && t_elem_size == u_elem_size,
156 _ => false,
157 }
158 });
159
160 let metadata = Src::pointer_to_metadata(src.as_ptr());
161 Dst::raw_from_ptr_len(src.as_non_null().cast::<u8>(), metadata).as_ptr()
162 }
163 }
164
165 // SAFETY: The `Project::project` impl preserves referent address.
166 unsafe impl<Src, Dst> Cast<Src, Dst> for CastUnsized
167 where
168 Src: ?Sized + KnownLayout,
169 Dst: ?Sized + KnownLayout<PointerMetadata = Src::PointerMetadata>,
170 {
171 }
172
173 /// A field projection
174 ///
175 /// A `Projection` is a [`Project`] which implements projection by
176 /// delegating to an implementation of [`HasField::project`].
177 #[allow(missing_debug_implementations, missing_copy_implementations)]
178 pub struct Projection<F: ?Sized, const VARIANT_ID: i128, const FIELD_ID: i128> {
179 _never: core::convert::Infallible,
180 _phantom: PhantomData<F>,
181 }
182
183 // SAFETY: `HasField::project` has the same safety post-conditions as
184 // `Project::project`.
185 unsafe impl<T: ?Sized, F, const VARIANT_ID: i128, const FIELD_ID: i128> Project<T, T::Type>
186 for Projection<F, VARIANT_ID, FIELD_ID>
187 where
188 T: HasField<F, VARIANT_ID, FIELD_ID>,
189 {
190 #[inline(always)]
191 fn project(src: PtrInner<'_, T>) -> *mut T::Type {
192 T::project(src)
193 }
194 }
195
196 /// A transitive sequence of projections.
197 ///
198 /// Given `TU: Project` and `UV: Project`, `TransitiveProject<_, TU, UV>` is
199 /// a [`Project`] which projects by applying `TU` followed by `UV`.
200 ///
201 /// If `TU: Cast` and `UV: Cast`, then `TransitiveProject<_, TU, UV>: Cast`.
202 #[allow(missing_debug_implementations)]
203 pub struct TransitiveProject<U: ?Sized, TU, UV> {
204 _never: core::convert::Infallible,
205 _projections: PhantomData<(TU, UV)>,
206 // On our MSRV (1.56), the debuginfo for a tuple containing both an
207 // uninhabited type and a DST causes an ICE. We split `U` from `TU` and
208 // `UV` to avoid this situation.
209 _u: PhantomData<U>,
210 }
211
212 // SAFETY: Since `TU::project` and `UV::project` are each
213 // provenance-preserving operations which preserve or shrink the set of
214 // referent bytes, so is their composition.
215 unsafe impl<T, U, V, TU, UV> Project<T, V> for TransitiveProject<U, TU, UV>
216 where
217 T: ?Sized,
218 U: ?Sized,
219 V: ?Sized,
220 TU: Project<T, U>,
221 UV: Project<U, V>,
222 {
223 #[inline(always)]
224 fn project(t: PtrInner<'_, T>) -> *mut V {
225 t.project::<_, TU>().project::<_, UV>().as_ptr()
226 }
227 }
228
229 // SAFETY: Since the `Project::project` impl delegates to `TU::project` and
230 // `UV::project`, and since `TU` and `UV` are `Cast`, the `Project::project`
231 // impl preserves the address of the referent.
232 unsafe impl<T, U, V, TU, UV> Cast<T, V> for TransitiveProject<U, TU, UV>
233 where
234 T: ?Sized,
235 U: ?Sized,
236 V: ?Sized,
237 TU: Cast<T, U>,
238 UV: Cast<U, V>,
239 {
240 }
241
242 /// A cast from `T` to `[u8]`.
243 pub(crate) struct AsBytesCast;
244
245 // SAFETY: `project` constructs a pointer with the same address as `src`
246 // and with a referent of the same size as `*src`. It does this using
247 // provenance-preserving operations.
248 //
249 // FIXME(https://github.com/rust-lang/unsafe-code-guidelines/issues/594):
250 // Technically, this proof assumes that `*src` is contiguous (the same is
251 // true of other proofs in this codebase). Is this guaranteed anywhere?
252 unsafe impl<T: ?Sized + KnownLayout> Project<T, [u8]> for AsBytesCast {
253 #[inline(always)]
254 fn project(src: PtrInner<'_, T>) -> *mut [u8] {
255 let bytes = match T::size_of_val_raw(src.as_non_null()) {
256 Some(bytes) => bytes,
257 // SAFETY: `KnownLayout::size_of_val_raw` promises to always
258 // return `Some` so long as the resulting size fits in a
259 // `usize`. By invariant on `PtrInner`, `src` refers to a range
260 // of bytes whose size fits in an `isize`, which implies that it
261 // also fits in a `usize`.
262 None => unsafe { core::hint::unreachable_unchecked() },
263 };
264
265 core::ptr::slice_from_raw_parts_mut(src.as_ptr().cast::<u8>(), bytes)
266 }
267 }
268
269 // SAFETY: The `Project::project` impl preserves referent address.
270 unsafe impl<T: ?Sized + KnownLayout> Cast<T, [u8]> for AsBytesCast {}
271}