summaryrefslogtreecommitdiffstats
path: root/rust/pin-init/src/alloc.rs
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-10-01 14:52:55 +0000
committerKumar Kartikeya Dwivedi <memxor@gmail.com>2026-10-01 18:39:27 +0200
commit33a154a96e71a34a1bcca9f40da343dbbf7b38b4 (patch)
tree543be8029ab3d8a3a63994ae23385a053ed30029 /rust/pin-init/src/alloc.rs
downloadlinux-stable-33a154a96e71a34a1bcca9f40da343dbbf7b38b4.tar.gz
linux-stable-33a154a96e71a34a1bcca9f40da343dbbf7b38b4.zip
selftests/bpf: Test packet range of pointers sharing an idgrafted
Add tests where two packet pointers share an id and tightening one pointer's umax from its var_off would put it less than their constant distance from the other's umax: with an index & 0x38 capped at 50, the base pointer keeps umax 50, so the pointer 8 bytes further on must keep umax 58, even though its known bits allow at most 56. These refused a valid program or accepted an out-of-bounds access before the fix: - check the advanced copy, load through the base: valid, was refused; - check the base, load the byte at base + 1 through a copy advanced by 8: was accepted; - check base + 4, load 4 bytes at base + 2 through base + 8: reads two bytes past the checked range, was accepted; - the same as the second with data_meta pointers checked against data: was accepted. These pass with and without the fix and cover nearby paths: - subtract an unknown scalar from a checked pointer and load below it (the range is kept across a new id); - reach a load through two paths whose checks cover 8 and 7 bytes after the loaded pointer; the second path must not be pruned by the first; - spill a copy of a pointer, check the pointer, fill the copy and load one byte past the checked range: the load is refused, and the copy has the range of the check. Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/20261001145255.855630-2-alexei.starovoitov@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Diffstat (limited to 'rust/pin-init/src/alloc.rs')
-rw-r--r--rust/pin-init/src/alloc.rs160
1 files changed, 160 insertions, 0 deletions
diff --git a/rust/pin-init/src/alloc.rs b/rust/pin-init/src/alloc.rs
new file mode 100644
index 000000000..471652e86
--- /dev/null
+++ b/rust/pin-init/src/alloc.rs
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+#[cfg(all(feature = "alloc", not(feature = "std")))]
+use alloc::{boxed::Box, sync::Arc};
+#[cfg(feature = "alloc")]
+use core::alloc::AllocError;
+use core::{mem::MaybeUninit, pin::Pin};
+#[cfg(feature = "std")]
+use std::sync::Arc;
+
+#[cfg(not(feature = "alloc"))]
+type AllocError = core::convert::Infallible;
+
+use crate::{
+ init_from_closure, pin_init_from_closure, InPlaceWrite, Init, PinInit, ZeroableOption,
+};
+
+pub extern crate alloc;
+
+// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee:
+// <https://doc.rust-lang.org/stable/std/option/index.html#representation>).
+unsafe impl<T> ZeroableOption for Box<T> {}
+
+/// Smart pointer that can initialize memory in-place.
+pub trait InPlaceInit<T>: Sized {
+ /// Use the given pin-initializer to pin-initialize a `T` inside of a new smart pointer of this
+ /// type.
+ ///
+ /// If `T: !Unpin` it will not be able to move afterwards.
+ fn try_pin_init<E>(init: impl PinInit<T, E>) -> Result<Pin<Self>, E>
+ where
+ E: From<AllocError>;
+
+ /// Use the given pin-initializer to pin-initialize a `T` inside of a new smart pointer of this
+ /// type.
+ ///
+ /// If `T: !Unpin` it will not be able to move afterwards.
+ #[inline]
+ fn pin_init(init: impl PinInit<T>) -> Result<Pin<Self>, AllocError> {
+ // SAFETY: We delegate to `init` and only change the error type.
+ let init = unsafe {
+ pin_init_from_closure(|slot| match init.__init(slot) {
+ Ok(()) => Ok(()),
+ Err(i) => match i {},
+ })
+ };
+ Self::try_pin_init(init)
+ }
+
+ /// Use the given initializer to in-place initialize a `T`.
+ fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
+ where
+ E: From<AllocError>;
+
+ /// Use the given initializer to in-place initialize a `T`.
+ #[inline]
+ fn init(init: impl Init<T>) -> Result<Self, AllocError> {
+ // SAFETY: We delegate to `init` and only change the error type.
+ let init = unsafe {
+ init_from_closure(|slot| match init.__init(slot) {
+ Ok(()) => Ok(()),
+ Err(i) => match i {},
+ })
+ };
+ Self::try_init(init)
+ }
+}
+
+#[cfg(feature = "alloc")]
+macro_rules! try_new_uninit {
+ ($type:ident) => {
+ $type::try_new_uninit()?
+ };
+}
+#[cfg(all(feature = "std", not(feature = "alloc")))]
+macro_rules! try_new_uninit {
+ ($type:ident) => {
+ $type::new_uninit()
+ };
+}
+
+impl<T> InPlaceInit<T> for Box<T> {
+ #[inline]
+ fn try_pin_init<E>(init: impl PinInit<T, E>) -> Result<Pin<Self>, E>
+ where
+ E: From<AllocError>,
+ {
+ try_new_uninit!(Box).write_pin_init(init)
+ }
+
+ #[inline]
+ fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
+ where
+ E: From<AllocError>,
+ {
+ try_new_uninit!(Box).write_init(init)
+ }
+}
+
+impl<T> InPlaceInit<T> for Arc<T> {
+ #[inline]
+ fn try_pin_init<E>(init: impl PinInit<T, E>) -> Result<Pin<Self>, E>
+ where
+ E: From<AllocError>,
+ {
+ let mut this = try_new_uninit!(Arc);
+ let Some(slot) = Arc::get_mut(&mut this) else {
+ // SAFETY: the Arc has just been created and has no external references
+ unsafe { core::hint::unreachable_unchecked() }
+ };
+ let slot = slot.as_mut_ptr();
+ // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
+ // slot is valid and will not be moved, because we pin it later.
+ unsafe { init.__init(slot)? };
+ // SAFETY: All fields have been initialized and this is the only `Arc` to that data.
+ Ok(unsafe { Pin::new_unchecked(this.assume_init()) })
+ }
+
+ #[inline]
+ fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
+ where
+ E: From<AllocError>,
+ {
+ let mut this = try_new_uninit!(Arc);
+ let Some(slot) = Arc::get_mut(&mut this) else {
+ // SAFETY: the Arc has just been created and has no external references
+ unsafe { core::hint::unreachable_unchecked() }
+ };
+ let slot = slot.as_mut_ptr();
+ // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
+ // slot is valid.
+ unsafe { init.__init(slot)? };
+ // SAFETY: All fields have been initialized.
+ Ok(unsafe { this.assume_init() })
+ }
+}
+
+impl<T> InPlaceWrite<T> for Box<MaybeUninit<T>> {
+ type Initialized = Box<T>;
+
+ #[inline]
+ fn write_init<E>(mut self, init: impl Init<T, E>) -> Result<Self::Initialized, E> {
+ let slot = self.as_mut_ptr();
+ // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
+ // slot is valid.
+ unsafe { init.__init(slot)? };
+ // SAFETY: All fields have been initialized.
+ Ok(unsafe { self.assume_init() })
+ }
+
+ #[inline]
+ fn write_pin_init<E>(mut self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E> {
+ let slot = self.as_mut_ptr();
+ // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
+ // slot is valid and will not be moved, because we pin it later.
+ unsafe { init.__init(slot)? };
+ // SAFETY: All fields have been initialized.
+ Ok(unsafe { self.assume_init() }.into())
+ }
+}