Skip to content

Commit f41941a

Browse files
nbdd0121Danilo Krummrich
authored andcommitted
rust: ptr: add projection infrastructure
Add a generic infrastructure for performing field and index projections on raw pointers. This will form the basis of performing I/O projections. Pointers manipulations are intentionally using the safe wrapping variants instead of the unsafe variants, as the latter requires pointers to be inside an allocation which is not necessarily true for I/O pointers. This projection macro protects against rogue `Deref` implementation, which can causes the projected pointer to be outside the bounds of starting pointer. This is extremely unlikely and Rust has a lint to catch this, but is unsoundness regardless. The protection works by inducing type inference ambiguity when `Deref` is implemented. This projection macro also stops projecting into unaligned fields (i.e. fields of `#[repr(packed)]` structs), as misaligned pointers require special handling. This is implemented by attempting to create reference to projected field inside a `if false` block. Despite being unreachable, Rust still checks that they're not unaligned fields. The projection macro supports both fallible and infallible index projections. These are described in detail inside the documentation. Signed-off-by: Gary Guo <gary@garyguo.net> Reviewed-by: Benno Lossin <lossin@kernel.org> Acked-by: Miguel Ojeda <ojeda@kernel.org> Link: https://patch.msgid.link/20260302164239.284084-3-gary@kernel.org [ * Add intro-doc links where possible, * Fix typos and slightly improve wording, e.g. "as documentation describes" -> "as the documentation of [`Self::proj`] describes", * Add an empty line between regular and safety comments, before examples, and between logically independent comments, * Capitalize various safety comments. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 08da98f commit f41941a

4 files changed

Lines changed: 314 additions & 1 deletion

File tree

rust/kernel/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
#![feature(const_ptr_write)]
3939
#![feature(const_refs_to_cell)]
4040
//
41+
// Stable since Rust 1.84.0.
42+
#![feature(strict_provenance)]
43+
//
4144
// Expected to become stable.
4245
#![feature(arbitrary_self_types)]
4346
//

rust/kernel/ptr.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
//! Types and functions to work with pointers and addresses.
44
5+
pub mod projection;
6+
pub use crate::project_pointer as project;
7+
58
use core::mem::{
69
align_of,
710
size_of, //

rust/kernel/ptr/projection.rs

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Infrastructure for handling projections.
4+
5+
use core::{
6+
mem::MaybeUninit,
7+
ops::Deref, //
8+
};
9+
10+
use crate::prelude::*;
11+
12+
/// Error raised when a projection is attempted on an array or slice out of bounds.
13+
pub struct OutOfBound;
14+
15+
impl From<OutOfBound> for Error {
16+
#[inline(always)]
17+
fn from(_: OutOfBound) -> Self {
18+
ERANGE
19+
}
20+
}
21+
22+
/// A helper trait to perform index projection.
23+
///
24+
/// This is similar to [`core::slice::SliceIndex`], but operates on raw pointers safely and
25+
/// fallibly.
26+
///
27+
/// # Safety
28+
///
29+
/// The implementation of `index` and `get` (if [`Some`] is returned) must ensure that, if provided
30+
/// input pointer `slice` and returned pointer `output`, then:
31+
/// - `output` has the same provenance as `slice`;
32+
/// - `output.byte_offset_from(slice)` is between 0 to
33+
/// `KnownSize::size(slice) - KnownSize::size(output)`.
34+
///
35+
/// This means that if the input pointer is valid, then pointer returned by `get` or `index` is
36+
/// also valid.
37+
#[diagnostic::on_unimplemented(message = "`{Self}` cannot be used to index `{T}`")]
38+
#[doc(hidden)]
39+
pub unsafe trait ProjectIndex<T: ?Sized>: Sized {
40+
type Output: ?Sized;
41+
42+
/// Returns an index-projected pointer, if in bounds.
43+
fn get(self, slice: *mut T) -> Option<*mut Self::Output>;
44+
45+
/// Returns an index-projected pointer; fail the build if it cannot be proved to be in bounds.
46+
#[inline(always)]
47+
fn index(self, slice: *mut T) -> *mut Self::Output {
48+
Self::get(self, slice).unwrap_or_else(|| build_error!())
49+
}
50+
}
51+
52+
// Forward array impl to slice impl.
53+
//
54+
// SAFETY: Safety requirement guaranteed by the forwarded impl.
55+
unsafe impl<T, I, const N: usize> ProjectIndex<[T; N]> for I
56+
where
57+
I: ProjectIndex<[T]>,
58+
{
59+
type Output = <I as ProjectIndex<[T]>>::Output;
60+
61+
#[inline(always)]
62+
fn get(self, slice: *mut [T; N]) -> Option<*mut Self::Output> {
63+
<I as ProjectIndex<[T]>>::get(self, slice)
64+
}
65+
66+
#[inline(always)]
67+
fn index(self, slice: *mut [T; N]) -> *mut Self::Output {
68+
<I as ProjectIndex<[T]>>::index(self, slice)
69+
}
70+
}
71+
72+
// SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
73+
// not exceed the required bound.
74+
unsafe impl<T> ProjectIndex<[T]> for usize {
75+
type Output = T;
76+
77+
#[inline(always)]
78+
fn get(self, slice: *mut [T]) -> Option<*mut T> {
79+
if self >= slice.len() {
80+
None
81+
} else {
82+
Some(slice.cast::<T>().wrapping_add(self))
83+
}
84+
}
85+
}
86+
87+
// SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
88+
// not exceed the required bound.
89+
unsafe impl<T> ProjectIndex<[T]> for core::ops::Range<usize> {
90+
type Output = [T];
91+
92+
#[inline(always)]
93+
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
94+
let new_len = self.end.checked_sub(self.start)?;
95+
if self.end > slice.len() {
96+
return None;
97+
}
98+
Some(core::ptr::slice_from_raw_parts_mut(
99+
slice.cast::<T>().wrapping_add(self.start),
100+
new_len,
101+
))
102+
}
103+
}
104+
105+
// SAFETY: Safety requirement guaranteed by the forwarded impl.
106+
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeTo<usize> {
107+
type Output = [T];
108+
109+
#[inline(always)]
110+
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
111+
(0..self.end).get(slice)
112+
}
113+
}
114+
115+
// SAFETY: Safety requirement guaranteed by the forwarded impl.
116+
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFrom<usize> {
117+
type Output = [T];
118+
119+
#[inline(always)]
120+
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
121+
(self.start..slice.len()).get(slice)
122+
}
123+
}
124+
125+
// SAFETY: `get` returned the pointer as is, so it always has the same provenance and offset of 0.
126+
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFull {
127+
type Output = [T];
128+
129+
#[inline(always)]
130+
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
131+
Some(slice)
132+
}
133+
}
134+
135+
/// A helper trait to perform field projection.
136+
///
137+
/// This trait has a `DEREF` generic parameter so it can be implemented twice for types that
138+
/// implement [`Deref`]. This will cause an ambiguity error and thus block [`Deref`] types being
139+
/// used as base of projection, as they can inject unsoundness. Users therefore must not specify
140+
/// `DEREF` and should always leave it to be inferred.
141+
///
142+
/// # Safety
143+
///
144+
/// `proj` may only invoke `f` with a valid allocation, as the documentation of [`Self::proj`]
145+
/// describes.
146+
#[doc(hidden)]
147+
pub unsafe trait ProjectField<const DEREF: bool> {
148+
/// Project a pointer to a type to a pointer of a field.
149+
///
150+
/// `f` may only be invoked with a valid allocation so it can safely obtain raw pointers to
151+
/// fields using `&raw mut`.
152+
///
153+
/// This is needed because `base` might not point to a valid allocation, while `&raw mut`
154+
/// requires pointers to be in bounds of a valid allocation.
155+
///
156+
/// # Safety
157+
///
158+
/// `f` must return a pointer in bounds of the provided pointer.
159+
unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F;
160+
}
161+
162+
// NOTE: in theory, this API should work for `T: ?Sized` and `F: ?Sized`, too. However, we cannot
163+
// currently support that as we need to obtain a valid allocation that `&raw const` can operate on.
164+
//
165+
// SAFETY: `proj` invokes `f` with valid allocation.
166+
unsafe impl<T> ProjectField<false> for T {
167+
#[inline(always)]
168+
unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
169+
// Create a valid allocation to start projection, as `base` is not necessarily so. The
170+
// memory is never actually used so it will be optimized out, so it should work even for
171+
// very large `T` (`memoffset` crate also relies on this). To be extra certain, we also
172+
// annotate `f` closure with `#[inline(always)]` in the macro.
173+
let mut place = MaybeUninit::uninit();
174+
let place_base = place.as_mut_ptr();
175+
let field = f(place_base);
176+
// SAFETY: `field` is in bounds from `base` per safety requirement.
177+
let offset = unsafe { field.byte_offset_from(place_base) };
178+
// Use `wrapping_byte_offset` as `base` does not need to be of valid allocation.
179+
base.wrapping_byte_offset(offset).cast()
180+
}
181+
}
182+
183+
// SAFETY: Vacuously satisfied.
184+
unsafe impl<T: Deref> ProjectField<true> for T {
185+
#[inline(always)]
186+
unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
187+
build_error!("this function is a guard against `Deref` impl and is never invoked");
188+
}
189+
}
190+
191+
/// Create a projection from a raw pointer.
192+
///
193+
/// The projected pointer is within the memory region marked by the input pointer. There is no
194+
/// requirement that the input raw pointer needs to be valid, so this macro may be used for
195+
/// projecting pointers outside normal address space, e.g. I/O pointers. However, if the input
196+
/// pointer is valid, the projected pointer is also valid.
197+
///
198+
/// Supported projections include field projections and index projections.
199+
/// It is not allowed to project into types that implement custom [`Deref`] or
200+
/// [`Index`](core::ops::Index).
201+
///
202+
/// The macro has basic syntax of `kernel::ptr::project!(ptr, projection)`, where `ptr` is an
203+
/// expression that evaluates to a raw pointer which serves as the base of projection. `projection`
204+
/// can be a projection expression of form `.field` (normally identifier, or numeral in case of
205+
/// tuple structs) or of form `[index]`.
206+
///
207+
/// If a mutable pointer is needed, the macro input can be prefixed with the `mut` keyword, i.e.
208+
/// `kernel::ptr::project!(mut ptr, projection)`. By default, a const pointer is created.
209+
///
210+
/// `ptr::project!` macro can perform both fallible indexing and build-time checked indexing.
211+
/// `[index]` form performs build-time bounds checking; if compiler fails to prove `[index]` is in
212+
/// bounds, compilation will fail. `[index]?` can be used to perform runtime bounds checking;
213+
/// `OutOfBound` error is raised via `?` if the index is out of bounds.
214+
///
215+
/// # Examples
216+
///
217+
/// Field projections are performed with `.field_name`:
218+
///
219+
/// ```
220+
/// struct MyStruct { field: u32, }
221+
/// let ptr: *const MyStruct = core::ptr::dangling();
222+
/// let field_ptr: *const u32 = kernel::ptr::project!(ptr, .field);
223+
///
224+
/// struct MyTupleStruct(u32, u32);
225+
///
226+
/// fn proj(ptr: *const MyTupleStruct) {
227+
/// let field_ptr: *const u32 = kernel::ptr::project!(ptr, .1);
228+
/// }
229+
/// ```
230+
///
231+
/// Index projections are performed with `[index]`:
232+
///
233+
/// ```
234+
/// fn proj(ptr: *const [u8; 32]) -> Result {
235+
/// let field_ptr: *const u8 = kernel::ptr::project!(ptr, [1]);
236+
/// // The following invocation, if uncommented, would fail the build.
237+
/// //
238+
/// // kernel::ptr::project!(ptr, [128]);
239+
///
240+
/// // This will raise an `OutOfBound` error (which is convertible to `ERANGE`).
241+
/// kernel::ptr::project!(ptr, [128]?);
242+
/// Ok(())
243+
/// }
244+
/// ```
245+
///
246+
/// If you need to match on the error instead of propagate, put the invocation inside a closure:
247+
///
248+
/// ```
249+
/// let ptr: *const [u8; 32] = core::ptr::dangling();
250+
/// let field_ptr: Result<*const u8> = (|| -> Result<_> {
251+
/// Ok(kernel::ptr::project!(ptr, [128]?))
252+
/// })();
253+
/// assert!(field_ptr.is_err());
254+
/// ```
255+
///
256+
/// For mutable pointers, put `mut` as the first token in macro invocation.
257+
///
258+
/// ```
259+
/// let ptr: *mut [(u8, u16); 32] = core::ptr::dangling_mut();
260+
/// let field_ptr: *mut u16 = kernel::ptr::project!(mut ptr, [1].1);
261+
/// ```
262+
#[macro_export]
263+
macro_rules! project_pointer {
264+
(@gen $ptr:ident, ) => {};
265+
// Field projection. `$field` needs to be `tt` to support tuple index like `.0`.
266+
(@gen $ptr:ident, .$field:tt $($rest:tt)*) => {
267+
// SAFETY: The provided closure always returns an in-bounds pointer.
268+
let $ptr = unsafe {
269+
$crate::ptr::projection::ProjectField::proj($ptr, #[inline(always)] |ptr| {
270+
// Check unaligned field. Not all users (e.g. DMA) can handle unaligned
271+
// projections.
272+
if false {
273+
let _ = &(*ptr).$field;
274+
}
275+
// SAFETY: `$field` is in bounds, and no implicit `Deref` is possible (if the
276+
// type implements `Deref`, Rust cannot infer the generic parameter `DEREF`).
277+
&raw mut (*ptr).$field
278+
})
279+
};
280+
$crate::ptr::project!(@gen $ptr, $($rest)*)
281+
};
282+
// Fallible index projection.
283+
(@gen $ptr:ident, [$index:expr]? $($rest:tt)*) => {
284+
let $ptr = $crate::ptr::projection::ProjectIndex::get($index, $ptr)
285+
.ok_or($crate::ptr::projection::OutOfBound)?;
286+
$crate::ptr::project!(@gen $ptr, $($rest)*)
287+
};
288+
// Build-time checked index projection.
289+
(@gen $ptr:ident, [$index:expr] $($rest:tt)*) => {
290+
let $ptr = $crate::ptr::projection::ProjectIndex::index($index, $ptr);
291+
$crate::ptr::project!(@gen $ptr, $($rest)*)
292+
};
293+
(mut $ptr:expr, $($proj:tt)*) => {{
294+
let ptr: *mut _ = $ptr;
295+
$crate::ptr::project!(@gen ptr, $($proj)*);
296+
ptr
297+
}};
298+
($ptr:expr, $($proj:tt)*) => {{
299+
let ptr = <*const _>::cast_mut($ptr);
300+
// We currently always project using mutable pointer, as it is not decided whether `&raw
301+
// const` allows the resulting pointer to be mutated (see documentation of `addr_of!`).
302+
$crate::ptr::project!(@gen ptr, $($proj)*);
303+
ptr.cast_const()
304+
}};
305+
}

scripts/Makefile.build

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,18 @@ $(obj)/%.lst: $(obj)/%.c FORCE
310310

311311
# The features in this list are the ones allowed for non-`rust/` code.
312312
#
313+
# - Stable since Rust 1.79.0: `feature(slice_ptr_len)`.
313314
# - Stable since Rust 1.81.0: `feature(lint_reasons)`.
314315
# - Stable since Rust 1.82.0: `feature(asm_const)`,
315316
# `feature(offset_of_nested)`, `feature(raw_ref_op)`.
317+
# - Stable since Rust 1.84.0: `feature(strict_provenance)`.
316318
# - Stable since Rust 1.87.0: `feature(asm_goto)`.
317319
# - Expected to become stable: `feature(arbitrary_self_types)`.
318320
# - To be determined: `feature(used_with_arg)`.
319321
#
320322
# Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
321323
# the unstable features in use.
322-
rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,used_with_arg
324+
rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,slice_ptr_len,strict_provenance,used_with_arg
323325

324326
# `--out-dir` is required to avoid temporaries being created by `rustc` in the
325327
# current working directory, which may be not accessible in the out-of-tree

0 commit comments

Comments
 (0)