summaryrefslogtreecommitdiff
path: root/rust/kernel/ptr/projection.rs
blob: 140ea8e21617db97fe832707e5c60de872a9b3e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
// SPDX-License-Identifier: GPL-2.0

//! Infrastructure for handling projections.

use core::{
    mem::MaybeUninit,
    ops::Deref, //
};

use crate::prelude::*;

/// Error raised when a projection is attempted on an array or slice out of bounds.
pub struct OutOfBound;

impl From<OutOfBound> for Error {
    #[inline(always)]
    fn from(_: OutOfBound) -> Self {
        ERANGE
    }
}

/// A helper trait to perform index projection.
///
/// This is similar to [`core::slice::SliceIndex`], but operates on raw pointers safely and
/// fallibly.
///
/// # Safety
///
/// The implementation of `index` and `get` (if [`Some`] is returned) must ensure that, if provided
/// input pointer `slice` and returned pointer `output`, then:
/// - `output` has the same provenance as `slice`;
/// - `output.byte_offset_from(slice)` is between 0 to
///   `KnownSize::size(slice) - KnownSize::size(output)`.
///
/// This means that if the input pointer is valid, then pointer returned by `get` or `index` is
/// also valid.
#[diagnostic::on_unimplemented(message = "`{Self}` cannot be used to index `{T}`")]
#[doc(hidden)]
pub unsafe trait ProjectIndex<T: ?Sized>: Sized {
    type Output: ?Sized;

    /// Returns an index-projected pointer, if in bounds.
    fn get(self, slice: *mut T) -> Option<*mut Self::Output>;

    /// Returns an index-projected pointer; fail the build if it cannot be proved to be in bounds.
    #[inline(always)]
    fn index(self, slice: *mut T) -> *mut Self::Output {
        Self::get(self, slice).unwrap_or_else(|| build_error!())
    }
}

// Forward array impl to slice impl.
//
// SAFETY: Safety requirement guaranteed by the forwarded impl.
unsafe impl<T, I, const N: usize> ProjectIndex<[T; N]> for I
where
    I: ProjectIndex<[T]>,
{
    type Output = <I as ProjectIndex<[T]>>::Output;

    #[inline(always)]
    fn get(self, slice: *mut [T; N]) -> Option<*mut Self::Output> {
        <I as ProjectIndex<[T]>>::get(self, slice)
    }

    #[inline(always)]
    fn index(self, slice: *mut [T; N]) -> *mut Self::Output {
        <I as ProjectIndex<[T]>>::index(self, slice)
    }
}

// SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
// not exceed the required bound.
unsafe impl<T> ProjectIndex<[T]> for usize {
    type Output = T;

    #[inline(always)]
    fn get(self, slice: *mut [T]) -> Option<*mut T> {
        if self >= slice.len() {
            None
        } else {
            Some(slice.cast::<T>().wrapping_add(self))
        }
    }
}

// SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
// not exceed the required bound.
unsafe impl<T> ProjectIndex<[T]> for core::ops::Range<usize> {
    type Output = [T];

    #[inline(always)]
    fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
        let new_len = self.end.checked_sub(self.start)?;
        if self.end > slice.len() {
            return None;
        }
        Some(core::ptr::slice_from_raw_parts_mut(
            slice.cast::<T>().wrapping_add(self.start),
            new_len,
        ))
    }
}

// SAFETY: Safety requirement guaranteed by the forwarded impl.
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeTo<usize> {
    type Output = [T];

    #[inline(always)]
    fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
        (0..self.end).get(slice)
    }
}

// SAFETY: Safety requirement guaranteed by the forwarded impl.
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFrom<usize> {
    type Output = [T];

    #[inline(always)]
    fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
        (self.start..slice.len()).get(slice)
    }
}

// SAFETY: `get` returned the pointer as is, so it always has the same provenance and offset of 0.
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFull {
    type Output = [T];

    #[inline(always)]
    fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
        Some(slice)
    }
}

/// A helper trait to perform field projection.
///
/// This trait has a `DEREF` generic parameter so it can be implemented twice for types that
/// implement [`Deref`]. This will cause an ambiguity error and thus block [`Deref`] types being
/// used as base of projection, as they can inject unsoundness. Users therefore must not specify
/// `DEREF` and should always leave it to be inferred.
///
/// # Safety
///
/// `proj` may only invoke `f` with a valid allocation, as the documentation of [`Self::proj`]
/// describes.
#[doc(hidden)]
pub unsafe trait ProjectField<const DEREF: bool> {
    /// Project a pointer to a type to a pointer of a field.
    ///
    /// `f` may only be invoked with a valid allocation so it can safely obtain raw pointers to
    /// fields using `&raw mut`.
    ///
    /// This is needed because `base` might not point to a valid allocation, while `&raw mut`
    /// requires pointers to be in bounds of a valid allocation.
    ///
    /// # Safety
    ///
    /// `f` must return a pointer in bounds of the provided pointer.
    unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F;
}

// NOTE: in theory, this API should work for `T: ?Sized` and `F: ?Sized`, too. However, we cannot
// currently support that as we need to obtain a valid allocation that `&raw const` can operate on.
//
// SAFETY: `proj` invokes `f` with valid allocation.
unsafe impl<T> ProjectField<false> for T {
    #[inline(always)]
    unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
        // Create a valid allocation to start projection, as `base` is not necessarily so. The
        // memory is never actually used so it will be optimized out, so it should work even for
        // very large `T` (`memoffset` crate also relies on this). To be extra certain, we also
        // annotate `f` closure with `#[inline(always)]` in the macro.
        let mut place = MaybeUninit::uninit();
        let place_base = place.as_mut_ptr();
        let field = f(place_base);
        // SAFETY: `field` is in bounds from `base` per safety requirement.
        let offset = unsafe { field.byte_offset_from(place_base) };
        // Use `wrapping_byte_offset` as `base` does not need to be of valid allocation.
        base.wrapping_byte_offset(offset).cast()
    }
}

// SAFETY: Vacuously satisfied.
unsafe impl<T: Deref> ProjectField<true> for T {
    #[inline(always)]
    unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
        build_error!("this function is a guard against `Deref` impl and is never invoked");
    }
}

/// Create a projection from a raw pointer.
///
/// The projected pointer is within the memory region marked by the input pointer. There is no
/// requirement that the input raw pointer needs to be valid, so this macro may be used for
/// projecting pointers outside normal address space, e.g. I/O pointers. However, if the input
/// pointer is valid, the projected pointer is also valid.
///
/// Supported projections include field projections and index projections.
/// It is not allowed to project into types that implement custom [`Deref`] or
/// [`Index`](core::ops::Index).
///
/// The macro has basic syntax of `kernel::ptr::project!(ptr, projection)`, where `ptr` is an
/// expression that evaluates to a raw pointer which serves as the base of projection. `projection`
/// can be a projection expression of form `.field` (normally identifier, or numeral in case of
/// tuple structs) or of form `[index]`.
///
/// If a mutable pointer is needed, the macro input can be prefixed with the `mut` keyword, i.e.
/// `kernel::ptr::project!(mut ptr, projection)`. By default, a const pointer is created.
///
/// `ptr::project!` macro can perform both fallible indexing and build-time checked indexing.
/// `[index]` form performs build-time bounds checking; if compiler fails to prove `[index]` is in
/// bounds, compilation will fail. `[index]?` can be used to perform runtime bounds checking;
/// `OutOfBound` error is raised via `?` if the index is out of bounds.
///
/// # Examples
///
/// Field projections are performed with `.field_name`:
///
/// ```
/// struct MyStruct { field: u32, }
/// let ptr: *const MyStruct = core::ptr::dangling();
/// let field_ptr: *const u32 = kernel::ptr::project!(ptr, .field);
///
/// struct MyTupleStruct(u32, u32);
///
/// fn proj(ptr: *const MyTupleStruct) {
///     let field_ptr: *const u32 = kernel::ptr::project!(ptr, .1);
/// }
/// ```
///
/// Index projections are performed with `[index]`:
///
/// ```
/// fn proj(ptr: *const [u8; 32]) -> Result {
///     let field_ptr: *const u8 = kernel::ptr::project!(ptr, [1]);
///     // The following invocation, if uncommented, would fail the build.
///     //
///     // kernel::ptr::project!(ptr, [128]);
///
///     // This will raise an `OutOfBound` error (which is convertible to `ERANGE`).
///     kernel::ptr::project!(ptr, [128]?);
///     Ok(())
/// }
/// ```
///
/// If you need to match on the error instead of propagate, put the invocation inside a closure:
///
/// ```
/// let ptr: *const [u8; 32] = core::ptr::dangling();
/// let field_ptr: Result<*const u8> = (|| -> Result<_> {
///     Ok(kernel::ptr::project!(ptr, [128]?))
/// })();
/// assert!(field_ptr.is_err());
/// ```
///
/// For mutable pointers, put `mut` as the first token in macro invocation.
///
/// ```
/// let ptr: *mut [(u8, u16); 32] = core::ptr::dangling_mut();
/// let field_ptr: *mut u16 = kernel::ptr::project!(mut ptr, [1].1);
/// ```
#[macro_export]
macro_rules! project_pointer {
    (@gen $ptr:ident, ) => {};
    // Field projection. `$field` needs to be `tt` to support tuple index like `.0`.
    (@gen $ptr:ident, .$field:tt $($rest:tt)*) => {
        // SAFETY: The provided closure always returns an in-bounds pointer.
        let $ptr = unsafe {
            $crate::ptr::projection::ProjectField::proj($ptr, #[inline(always)] |ptr| {
                // Check unaligned field. Not all users (e.g. DMA) can handle unaligned
                // projections.
                if false {
                    let _ = &(*ptr).$field;
                }
                // SAFETY: `$field` is in bounds, and no implicit `Deref` is possible (if the
                // type implements `Deref`, Rust cannot infer the generic parameter `DEREF`).
                &raw mut (*ptr).$field
            })
        };
        $crate::ptr::project!(@gen $ptr, $($rest)*)
    };
    // Fallible index projection.
    (@gen $ptr:ident, [$index:expr]? $($rest:tt)*) => {
        let $ptr = $crate::ptr::projection::ProjectIndex::get($index, $ptr)
            .ok_or($crate::ptr::projection::OutOfBound)?;
        $crate::ptr::project!(@gen $ptr, $($rest)*)
    };
    // Build-time checked index projection.
    (@gen $ptr:ident, [$index:expr] $($rest:tt)*) => {
        let $ptr = $crate::ptr::projection::ProjectIndex::index($index, $ptr);
        $crate::ptr::project!(@gen $ptr, $($rest)*)
    };
    (mut $ptr:expr, $($proj:tt)*) => {{
        let ptr: *mut _ = $ptr;
        $crate::ptr::project!(@gen ptr, $($proj)*);
        ptr
    }};
    ($ptr:expr, $($proj:tt)*) => {{
        let ptr = <*const _>::cast_mut($ptr);
        // We currently always project using mutable pointer, as it is not decided whether `&raw
        // const` allows the resulting pointer to be mutated (see documentation of `addr_of!`).
        $crate::ptr::project!(@gen ptr, $($proj)*);
        ptr.cast_const()
    }};
}