Skip to main content

kernel_api/ptr/
mod.rs

1//! Provides pointer wrappers for safely accessing userspace.
2//!
3//! # User pointers
4//!
5//! TODO(doc).
6//!
7//! ## Local pointers
8//!
9//! TODO(doc).
10//!
11//! All pointers provided from userspace should be accessed through a [`User`] to
12//! prevent kernelspace page faults from invalid or misaligned pointers.
13//! 
14//! # SMAP
15//! 
16//! When SMAP is supported on the system, all memory access through a [`User`] will
17//! automatically set and clear the `AC` flag to prevent trapping.
18
19#[cfg(feature = "full")]
20mod user_ptr;
21
22use core::fmt;
23use core::fmt::Formatter;
24use core::num::NonZero;
25use core::ptr::NonNull;
26#[cfg(feature = "full")]
27pub use user_ptr::*;
28
29#[cfg(feature = "full")]
30mod user_local;
31#[cfg(feature = "full")]
32pub use user_local::*;
33
34#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
35#[path = "x86_64.rs"]
36#[cfg(feature = "full")]
37mod impls;
38
39/// The error returned when a memory access to userspace failed.
40#[derive(Debug)]
41#[non_exhaustive]
42pub struct PointerError {}
43
44/// A [`NonNull<T>`] pointer with a numerical tag.
45///
46/// The tag is split between the low alignment bits of the inner type, and the high
47/// order bits that are free due to the architecture virtual address width.
48#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
49#[repr(transparent)]
50pub struct TaggedNonNull<T: ?Sized>(NonNull<T>);
51
52impl<T> TaggedNonNull<T> {
53	pub const TAG_HIGH_BITS: u32 = cfg_select! {
54		// FIXME(LA57)
55		target_arch = "x86_64" => 16,
56	};
57	pub const TAG_LOW_BITS: u32 = align_of::<T>().trailing_zeros();
58
59	const MAX_BOUND_VALUE: usize = {
60		assert!(
61			Self::TAG_HIGH_BITS + Self::TAG_LOW_BITS < usize::BITS,
62			"cannot have pointer which is only tag",
63		);
64
65		1 << (Self::TAG_HIGH_BITS + Self::TAG_LOW_BITS)
66	};
67
68	/// The (architecture- and type-dependent) maximum value the tag can hold.
69	pub const MAXIMUM_TAG: usize = Self::MAX_BOUND_VALUE - 1;
70
71	const TAG_HIGH_MASK: usize = ((1 << Self::TAG_HIGH_BITS) - 1) << (usize::BITS - Self::TAG_HIGH_BITS);
72	const TAG_LOW_MASK: usize = (1 << Self::TAG_LOW_BITS) - 1;
73
74	/// Create a new `TaggedNonNull` with the given `tag`.
75	pub fn new(ptr: NonNull<T>, tag: usize) -> Self {
76		assert!(tag <= Self::MAX_BOUND_VALUE, "requested tag cannot fit in pointer");
77		assert!(ptr.is_aligned(), "cannot tag unaligned pointer");
78		let tag_low = tag & Self::TAG_LOW_MASK;
79		let tag_high = tag >> Self::TAG_LOW_BITS;
80		let ptr = ptr.map_addr(|mut addr| {
81			// SAFETY: assertion that non-tag bits exist in the pointer, and original pointer came from NonNull
82			addr = unsafe { NonZero::new_unchecked(addr.get() & !(Self::TAG_HIGH_MASK | Self::TAG_LOW_MASK)) };
83			addr |= tag_low;
84			addr |= tag_high << (usize::BITS - Self::TAG_HIGH_BITS);
85			addr
86		});
87		Self(ptr)
88	}
89
90	/// Extract the pointer portion of the `TaggedNonNull`.
91	pub fn as_ptr(&self) -> NonNull<T> {
92		let ptr = self.0.as_ptr();
93		let ptr = ptr.map_addr(|addr| {
94			let masked = addr & !(Self::TAG_HIGH_MASK | Self::TAG_LOW_MASK);
95			let canonical = (masked << Self::TAG_HIGH_BITS).cast_signed() >> Self::TAG_HIGH_BITS;
96			canonical.cast_unsigned()
97		});
98		// SAFETY: assertion that non-tag bits exist in the pointer, and original pointer came from NonNull
99		unsafe { NonNull::new_unchecked(ptr) }
100	}
101
102	/// Extract the tag portion of the `TaggedNonNull`.
103	pub fn tag(&self) -> usize {
104		let addr = self.0.addr().get();
105		let low = addr & Self::TAG_LOW_MASK;
106		let high = addr & Self::TAG_HIGH_MASK;
107		low | ((high >> (usize::BITS - Self::TAG_HIGH_BITS)) << Self::TAG_LOW_BITS)
108	}
109
110	/// Compares the *addresses* of the two pointers for equality,
111	/// ignoring the tag value.
112	pub fn addr_eq(&self, other: &Self) -> bool {
113		core::ptr::addr_eq(self.as_ptr().as_ptr(), other.as_ptr().as_ptr())
114	}
115
116	pub fn as_tagged_ptr(&self) -> NonNull<T> {
117		self.0
118	}
119
120	pub fn from_tagged_ptr(ptr: NonNull<T>) -> Self {
121		Self(ptr)
122	}
123}
124
125impl<T> fmt::Pointer for TaggedNonNull<T> {
126	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
127		let addr = self.as_ptr().addr().get();
128		let meta = core::ptr::metadata(self.as_ptr().as_ptr());
129		let tag = self.tag();
130
131		// copied from `impl fmt::Pointer for *const T`
132		let mut ptr_options = f.options();
133		if f.options().get_alternate() {
134			ptr_options.sign_aware_zero_pad(true);
135
136			if f.options().get_width().is_none() {
137				ptr_options.width(Some((usize::BITS / 4) as u16 + 2));
138			}
139		}
140
141		f.debug_struct("TaggedNonNull")
142			.field_with("addr", move |f| fmt::LowerHex::fmt(&addr, &mut f.with_options(ptr_options)))
143			.field("metadata", &meta)
144			.field("tag", &tag)
145			.finish()
146	}
147}
148
149#[cfg(test)]
150mod tests {
151	use super::*;
152
153	#[test]
154	fn tagged_nonnull_round_trip() {
155		let x = &5;
156		let tag = 0xCAFEusize;
157		let ptr = TaggedNonNull::new(
158			NonNull::from_ref(x),
159			tag,
160		);
161		assert_eq!(ptr.tag(), tag);
162		assert_eq!(ptr.as_ptr(), NonNull::from_ref(x));
163	}
164}