* [PATCH v19 0/8] rust: add `Ownable` trait and `Owned` type
From: Andreas Hindborg @ 2026-06-26 11:53 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Asahi Lina, Oliver Mangold, Viresh Kumar, Boqun Feng, Asahi Lina,
Igor Korotin, Andreas Hindborg
Add a new trait `Ownable` and type `Owned` for types that specify their
own way of performing allocation and destruction. This is useful for
types from the C side.
Implement `ForeignOwnable` for `Owned`.
Convert `Page` to be `Ownable` and add a `from_raw` method.
Add the trait `OwnableRefCounted` that allows conversion between
`ARef` and `Owned`. This is analogous to conversion between `Arc` and
`UniqueArc`.
Patches 1-4 implement `Ownable` and applies it to `Page`. These patches
can be merged on their own.
Patches 5-7 add `Ownable` -> `ARef` interop and can be merged later if
consensus on their shape cannot be reached.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
Changes in v19:
- Update `workqueue.rs` and the `aref` module docs to use `RefCounted` instead of `AlwaysRefCounted` (Sashiko).
- Remove the default `OwnableRefCounted::into_shared` implementation to keep the trait safe (Sashiko).
- Fix the `ARef<Self>` rustdoc link (Sashiko).
- Reuse `Owned::into_raw`/`from_raw` in the `ForeignOwnable` implementation (Gary).
- Link to v18: https://msgid.link/20260625-unique-ref-v18-0-4e06b5896d47@kernel.org
Changes in v18:
- Rebase on `rust-next` (2026-06-24).
- Drop the `'static` bound on `ForeignOwnable for Owned` (Gary).
- Make `Ownable::release` take a raw pointer instead of `&mut self` (Alice, Sashiko).
- Drop `types::ARef` re-export (Alice).
- Drop unneeded `#[repr(transparent)]` on `Owned` (Gary).
- Fix `FOREIGN_ALIGN` for `Owned` to report the pointee alignment (Sashiko).
- Remove `BorrowedPage`; use `&Page` directly (Alice).
- Update Rust Binder for the `Owned<Page>` conversion (Alice).
- Update `pwm.rs` for the `RefCounted`/`AlwaysRefCounted` split (Sashiko).
- Fix documentation nits: missing `// INVARIANT:` comments, stale `Page` docs, and a stray `mut` (Sashiko).
- Expand the `use` statements touched by the rename patch to the multi-line style (Onur).
- Link to v17: https://msgid.link/20260604-unique-ref-v17-0-7b4c3d2930b9@kernel.org
Changes in v17:
- Rebase on v7.1-rc2.
- Reorder patches so that `Ownable` can merge without `OwnableRefCounted` (Alice).
- Add `#[inline]` directives to short functions added by the series (Gary).
- Link to v16: https://msgid.link/20260224-unique-ref-v16-0-c21afcb118d3@kernel.org
Changes in v16:
- Simplify pointer to reference cast in `Page::from_raw`.
- Use `NonNull<Page>` rather than `Owned<Page>` for `BorrowedPage` internals.
- Use "convertible to reference" wording when converting pointers to references.
- Fix formatting for `Page::from_raw` docs.
- Leave imports alone when adding safety comment to aref example.
- Use `KBox::into_nonnull` for examples.
- Add patch for `KBox::into_nonnull`.
- Change invariants and safety comments of `Ownable` and make the trait safe.
- Make `Ownable::release` take a mutable reference.
- Fix error handling in example for `Ownable`
- Link to v15: https://msgid.link/20260220-unique-ref-v15-0-893ed86b06cc@kernel.org
Changes in v15:
- Update series with original SoB's.
- Rename `AlwaysRefCounted` in `kernel::usb`.
- Rename `Owned::get_pin_mut` to `Owned::as_pin_mut`.
- Link to v14: https://msgid.link/20260204-unique-ref-v14-0-17cb29ebacbb@kernel.org
Changes in v14:
- Rebase on v6.19-rc7.
- Rewrite cover letter.
- Update documentation and safety comments based on v13 feedback.
- Update commit messages.
- Reorder implementation blocks in owned.rs.
- Update example in owned.rs to use try operator rather than `expect`.
- Reformat use statements.
- Add patch: rust: page: convert to `Ownable`.
- Add patch: rust: implement `ForeignOwnable` for `Owned`.
- Add patch: rust: page: add `from_raw()`.
- Link to v13: https://lore.kernel.org/r/20251117-unique-ref-v13-0-b5b243df1250@pm.me
Changes in v13:
- Rebase onto v6.18-rc1 (Andreas's work).
- Documentation and style fixes contributed by Andreas
- Link to v12: https://lore.kernel.org/r/20251001-unique-ref-v12-0-fa5c31f0c0c4@pm.me
Changes in v12:
-
- Rebase onto v6.17-rc1 (Andreas's work).
- moved kernel/types/ownable.rs to kernel/owned.rs
- Drop OwnableMut, make DerefMut depend on Unpin instead. I understood
ML discussion as that being okay, but probably needs further scrunity.
- Lots of more documentation changes suggested by reviewers.
- Usage example for Ownable/Owned.
- Link to v11: https://lore.kernel.org/r/20250618-unique-ref-v11-0-49eadcdc0aa6@pm.me
Changes in v11:
- Rework of documentation. I tried to honor all requests for changes "in
spirit" plus some clearifications and corrections of my own.
- Dropping `SimpleOwnedRefCounted` by request from Alice, as it creates a
potentially problematic blanket implementation (which a derive macro that
could be created later would not have).
- Dropping Miguel's "kbuild: provide `RUSTC_HAS_DO_NOT_RECOMMEND` symbol"
patch, as it is not needed anymore after dropping `SimpleOwnedRefCounted`.
(I can add it again, if it is considered useful anyway).
- Link to v10: https://lore.kernel.org/r/20250502-unique-ref-v10-0-25de64c0307f@pm.me
Changes in v10:
- Moved kernel/ownable.rs to kernel/types/ownable.rs
- Fixes in documentation / comments as suggested by Andreas Hindborg
- Added Reviewed-by comment for Andreas Hindborg
- Fix rustfmt of pid_namespace.rs
- Link to v9: https://lore.kernel.org/r/20250325-unique-ref-v9-0-e91618c1de26@pm.me
Changes in v9:
- Rebase onto v6.14-rc7
- Move Ownable/OwnedRefCounted/Ownable, etc., into separate module
- Documentation fixes to Ownable/OwnableMut/OwnableRefCounted
- Add missing SAFETY documentation to ARef example
- Link to v8: https://lore.kernel.org/r/20250313-unique-ref-v8-0-3082ffc67a31@pm.me
Changes in v8:
- Fix Co-developed-by and Suggested-by tags as suggested by Miguel and Boqun
- Some small documentation fixes in Owned/Ownable patch
- removing redundant trait constraint on DerefMut for Owned as suggested by Boqun Feng
- make SimpleOwnedRefCounted no longer implement RefCounted as suggested by Boqun Feng
- documentation for RefCounted as suggested by Boqun Feng
- Link to v7: https://lore.kernel.org/r/20250310-unique-ref-v7-0-4caddb78aa05@pm.me
Changes in v7:
- Squash patch to make Owned::from_raw/into_raw public into parent
- Added Signed-off-by to other people's commits
- Link to v6: https://lore.kernel.org/r/20250310-unique-ref-v6-0-1ff53558617e@pm.me
Changes in v6:
- Changed comments/formatting as suggested by Miguel Ojeda
- Included and used new config flag RUSTC_HAS_DO_NOT_RECOMMEND,
thus no changes to types.rs will be needed when the attribute
becomes available.
- Fixed commit message for Owned patch.
- Link to v5: https://lore.kernel.org/r/20250307-unique-ref-v5-0-bffeb633277e@pm.me
Changes in v5:
- Rebase the whole thing on top of the Ownable/Owned traits by Asahi Lina.
- Rename AlwaysRefCounted to RefCounted and make AlwaysRefCounted a
marker trait instead to allow to obtain an ARef<T> from an &T,
which (as Alice pointed out) is unsound when combined with UniqueRef/Owned.
- Change the Trait design and naming to implement this feature,
UniqueRef/UniqueRefCounted is dropped in favor of Ownable/Owned and
OwnableRefCounted is used to provide the functions to convert
between Owned and ARef.
- Link to v4: https://lore.kernel.org/r/20250305-unique-ref-v4-1-a8fdef7b1c2c@pm.me
Changes in v4:
- Just a minor change in naming by request from Andreas Hindborg,
try_shared_to_unique() -> try_from_shared(),
unique_to_shared() -> into_shared(),
which is more in line with standard Rust naming conventions.
- Link to v3: https://lore.kernel.org/r/Z8Wuud2UQX6Yukyr@mango
To: Danilo Krummrich <dakr@kernel.org>
To: Lorenzo Stoakes <ljs@kernel.org>
To: Vlastimil Babka <vbabka@kernel.org>
To: "Liam R. Howlett" <liam@infradead.org>
To: Uladzislau Rezki <urezki@gmail.com>
To: Miguel Ojeda <ojeda@kernel.org>
To: Boqun Feng <boqun@kernel.org>
To: Gary Guo <gary@garyguo.net>
To: Björn Roy Baron <bjorn3_gh@protonmail.com>
To: Benno Lossin <lossin@kernel.org>
To: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>
To: Trevor Gross <tmgross@umich.edu>
To: Daniel Almeida <daniel.almeida@collabora.com>
To: Tamir Duberstein <tamird@kernel.org>
To: Alexandre Courbot <acourbot@nvidia.com>
To: Onur Özkan <work@onurozkan.dev>
To: Lyude Paul <lyude@redhat.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: Arve Hjønnevåg <arve@android.com>
To: Todd Kjos <tkjos@android.com>
To: Christian Brauner <brauner@kernel.org>
To: Carlos Llamas <cmllamas@google.com>
To: "Rafael J. Wysocki" <rafael@kernel.org>
To: Dave Ertman <david.m.ertman@intel.com>
To: Ira Weiny <ira.weiny@intel.com>
To: Leon Romanovsky <leon@kernel.org>
To: Paul Moore <paul@paul-moore.com>
To: Serge Hallyn <sergeh@kernel.org>
To: David Airlie <airlied@gmail.com>
To: Simona Vetter <simona@ffwll.ch>
To: Alexander Viro <viro@zeniv.linux.org.uk>
To: Jan Kara <jack@suse.cz>
To: Igor Korotin <igor.korotin@linux.dev>
To: Viresh Kumar <vireshk@kernel.org>
To: Nishanth Menon <nm@ti.com>
To: Stephen Boyd <sboyd@kernel.org>
To: Bjorn Helgaas <bhelgaas@google.com>
To: Krzysztof Wilczyński <kwilczynski@kernel.org>
To: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
To: Michal Wilczynski <m.wilczynski@samsung.com>
Cc: Philipp Stanner <phasta@kernel.org>
Cc: rust-for-linux@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: driver-core@lists.linux.dev
Cc: linux-block@vger.kernel.org
Cc: linux-security-module@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-pm@vger.kernel.org
Cc: linux-pci@vger.kernel.org
Cc: linux-pwm@vger.kernel.org
---
Andreas Hindborg (3):
rust: alloc: add `KBox::into_non_null`
rust: implement `ForeignOwnable` for `Owned`
rust: page: add `from_raw()`
Asahi Lina (2):
rust: types: Add Ownable/Owned types
rust: page: convert to `Ownable`
Oliver Mangold (3):
rust: rename `AlwaysRefCounted` to `RefCounted`.
rust: Add missing SAFETY documentation for `ARef` example
rust: Add `OwnableRefCounted`
drivers/android/binder/page_range.rs | 10 +-
rust/kernel/alloc/allocator.rs | 19 +-
rust/kernel/alloc/allocator/iter.rs | 6 +-
rust/kernel/alloc/kbox.rs | 9 +
rust/kernel/auxiliary.rs | 10 +-
rust/kernel/block/mq/request.rs | 19 +-
rust/kernel/cred.rs | 16 +-
rust/kernel/device.rs | 12 +-
rust/kernel/device/property.rs | 11 +-
rust/kernel/drm/device.rs | 9 +-
rust/kernel/drm/gem/mod.rs | 16 +-
rust/kernel/fs/file.rs | 23 ++-
rust/kernel/i2c.rs | 13 +-
rust/kernel/lib.rs | 1 +
rust/kernel/mm.rs | 22 ++-
rust/kernel/mm/mmput_async.rs | 12 +-
rust/kernel/opp.rs | 16 +-
rust/kernel/owned.rs | 373 +++++++++++++++++++++++++++++++++++
rust/kernel/page.rs | 136 +++++--------
rust/kernel/pci.rs | 10 +-
rust/kernel/pid_namespace.rs | 15 +-
rust/kernel/platform.rs | 10 +-
rust/kernel/pwm.rs | 12 +-
rust/kernel/sync/aref.rs | 84 +++++---
rust/kernel/task.rs | 13 +-
rust/kernel/types.rs | 12 ++
rust/kernel/usb.rs | 17 +-
rust/kernel/workqueue.rs | 8 +-
28 files changed, 728 insertions(+), 186 deletions(-)
---
base-commit: 43a393185e33e573a374c1d4f7ddf6481484ef8d
change-id: 20250305-unique-ref-29fcd675f9e9
Best regards,
--
Andreas Hindborg <a.hindborg@kernel.org>
^ permalink raw reply
* [PATCH v19 2/8] rust: types: Add Ownable/Owned types
From: Andreas Hindborg @ 2026-06-26 11:53 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Asahi Lina, Oliver Mangold, Boqun Feng
In-Reply-To: <20260626-unique-ref-v19-0-2607ca88dfdf@kernel.org>
From: Asahi Lina <lina+kernel@asahilina.net>
By analogy to `AlwaysRefCounted` and `ARef`, an `Ownable` type is a
(typically C FFI) type that *may* be owned by Rust, but need not be. Unlike
`AlwaysRefCounted`, this mechanism expects the reference to be unique
within Rust, and does not allow cloning.
Conceptually, this is similar to a `KBox<T>`, except that it delegates
resource management to the `T` instead of using a generic allocator.
[ om:
- Split code into separate file and `pub use` it from types.rs.
- Make from_raw() and into_raw() public.
- Remove OwnableMut, and make DerefMut dependent on Unpin instead.
- Usage example/doctest for Ownable/Owned.
- Fixes to documentation and commit message.
]
Link: https://lore.kernel.org/all/20250202-rust-page-v1-1-e3170d7fe55e@asahilina.net/
Signed-off-by: Asahi Lina <lina+kernel@asahilina.net>
Co-developed-by: Oliver Mangold <oliver.mangold@pm.me>
Signed-off-by: Oliver Mangold <oliver.mangold@pm.me>
Reviewed-by: Boqun Feng <boqun.feng@gmail.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
[ Andreas: Updated documentation, examples, and formatting. Change safety
requirements, safety comments. ]
Co-developed-by: Andreas Hindborg <a.hindborg@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/lib.rs | 1 +
rust/kernel/owned.rs | 188 +++++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/sync/aref.rs | 5 ++
rust/kernel/types.rs | 5 ++
4 files changed, 199 insertions(+)
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9512af7156df2..eb5256204a174 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -101,6 +101,7 @@
pub mod of;
#[cfg(CONFIG_PM_OPP)]
pub mod opp;
+pub mod owned;
pub mod page;
#[cfg(CONFIG_PCI)]
pub mod pci;
diff --git a/rust/kernel/owned.rs b/rust/kernel/owned.rs
new file mode 100644
index 0000000000000..7fe9ec3e55126
--- /dev/null
+++ b/rust/kernel/owned.rs
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Unique owned pointer types for objects with custom drop logic.
+//!
+//! These pointer types are useful for C-allocated objects which by API-contract
+//! are owned by Rust, but need to be freed through the C API.
+
+use core::{
+ mem::ManuallyDrop,
+ ops::{
+ Deref,
+ DerefMut, //
+ },
+ pin::Pin,
+ ptr::NonNull, //
+};
+
+/// Types that specify their own way of performing allocation and destruction. Typically, this trait
+/// is implemented on types from the C side.
+///
+/// Implementing this trait allows types to be referenced via the [`Owned<Self>`] pointer type. This
+/// is useful when it is desirable to tie the lifetime of the reference to an owned object, rather
+/// than pass around a bare reference. [`Ownable`] types can define custom drop logic that is
+/// executed when the owned reference [`Owned<Self>`] pointing to the object is dropped.
+///
+/// Note: The underlying object is not required to provide internal reference counting, because it
+/// represents a unique, owned reference. If reference counting (on the Rust side) is required,
+/// [`AlwaysRefCounted`](crate::sync::aref::AlwaysRefCounted) should be implemented.
+///
+/// # Examples
+///
+/// A minimal example implementation of [`Ownable`] and its usage with [`Owned`] looks like
+/// this:
+///
+/// ```
+/// # #![expect(clippy::disallowed_names)]
+/// # use core::cell::Cell;
+/// # use core::ptr::NonNull;
+/// # use kernel::sync::global_lock;
+/// # use kernel::alloc::{flags, kbox::KBox, AllocError};
+/// # use kernel::types::{Owned, Ownable};
+///
+/// // Let's count the allocations to see if freeing works.
+/// kernel::sync::global_lock! {
+/// // SAFETY: we call `init()` right below, before doing anything else.
+/// unsafe(uninit) static FOO_ALLOC_COUNT: Mutex<usize> = 0;
+/// }
+/// // SAFETY: We call `init()` only once, here.
+/// unsafe { FOO_ALLOC_COUNT.init() };
+///
+/// struct Foo;
+///
+/// impl Foo {
+/// fn new() -> Result<Owned<Self>> {
+/// // We are just using a `KBox` here to handle the actual allocation, as our `Foo` is
+/// // not actually a C-allocated object.
+/// let result = KBox::new(
+/// Foo {},
+/// flags::GFP_KERNEL,
+/// )?;
+/// let result = KBox::into_non_null(result);
+/// // Count new allocation
+/// *FOO_ALLOC_COUNT.lock() += 1;
+/// // SAFETY:
+/// // - We just allocated the `Self`, thus it is valid and we own it.
+/// // - We can transfer this ownership to the `from_raw` method.
+/// Ok(unsafe { Owned::from_raw(result) })
+/// }
+/// }
+///
+/// impl Ownable for Foo {
+/// unsafe fn release(this: NonNull<Self>) {
+/// // SAFETY: The [`KBox<Self>`] is still alive. We can pass ownership to the [`KBox`], as
+/// // by requirement on calling this function.
+/// drop(unsafe { KBox::from_raw(this.as_ptr()) });
+/// // Count released allocation
+/// *FOO_ALLOC_COUNT.lock() -= 1;
+/// }
+/// }
+///
+/// {
+/// let foo = Foo::new()?;
+/// assert!(*FOO_ALLOC_COUNT.lock() == 1);
+/// }
+/// // `foo` is out of scope now, so we expect no live allocations.
+/// assert!(*FOO_ALLOC_COUNT.lock() == 0);
+/// # Ok::<(), Error>(())
+/// ```
+pub trait Ownable {
+ /// Tear down this `Ownable`.
+ ///
+ /// Implementers of `Ownable` can use this function to clean up the use of `Self`. This can
+ /// include freeing the underlying object.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that they have exclusive ownership of the `Self` pointed to by `this`,
+ /// and that this ownership is transferred to the `release` method. `this` must not be used
+ /// after calling this method, as the underlying object may have been freed.
+ unsafe fn release(this: NonNull<Self>);
+}
+
+/// A mutable reference to an owned `T`.
+///
+/// The [`Ownable`] is automatically freed or released when an instance of [`Owned`] is
+/// dropped.
+///
+/// # Invariants
+///
+/// - Until `T::release` is called, this `Owned<T>` exclusively owns the underlying `T`.
+/// - The `T` value is pinned.
+pub struct Owned<T: Ownable> {
+ ptr: NonNull<T>,
+}
+
+impl<T: Ownable> Owned<T> {
+ /// Creates a new instance of [`Owned`].
+ ///
+ /// This function takes over ownership of the underlying object.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that:
+ /// - `ptr` points to a valid instance of `T`.
+ /// - Until `T::release` is called, the returned `Owned<T>` exclusively owns the underlying `T`.
+ #[inline]
+ pub unsafe fn from_raw(ptr: NonNull<T>) -> Self {
+ // INVARIANT: By function safety requirement we satisfy the first invariant of `Self`.
+ // We treat `T` as pinned from now on.
+ Self { ptr }
+ }
+
+ /// Consumes the [`Owned`], returning a raw pointer.
+ ///
+ /// This function does not drop the underlying `T`. When this function returns, ownership of the
+ /// underlying `T` is with the caller.
+ #[inline]
+ pub fn into_raw(me: Self) -> NonNull<T> {
+ ManuallyDrop::new(me).ptr
+ }
+
+ /// Get a pinned mutable reference to the data owned by this `Owned<T>`.
+ #[inline]
+ pub fn as_pin_mut(&mut self) -> Pin<&mut T> {
+ // SAFETY: The type invariants guarantee that the object is valid, and that we can safely
+ // return a mutable reference to it.
+ let unpinned = unsafe { self.ptr.as_mut() };
+
+ // SAFETY: By type invariant `T` is pinned.
+ unsafe { Pin::new_unchecked(unpinned) }
+ }
+}
+
+// SAFETY: It is safe to send an [`Owned<T>`] to another thread when the underlying `T` is [`Send`],
+// because of the ownership invariant. Sending an [`Owned<T>`] is equivalent to sending the `T`.
+unsafe impl<T: Ownable + Send> Send for Owned<T> {}
+
+// SAFETY: It is safe to send [`&Owned<T>`] to another thread when the underlying `T` is [`Sync`],
+// because of the ownership invariant. Sending an [`&Owned<T>`] is equivalent to sending the `&T`.
+unsafe impl<T: Ownable + Sync> Sync for Owned<T> {}
+
+impl<T: Ownable> Deref for Owned<T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: The type invariants guarantee that the object is valid.
+ unsafe { self.ptr.as_ref() }
+ }
+}
+
+impl<T: Ownable + Unpin> DerefMut for Owned<T> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ // SAFETY: The type invariants guarantee that the object is valid, and that we can safely
+ // return a mutable reference to it.
+ unsafe { self.ptr.as_mut() }
+ }
+}
+
+impl<T: Ownable> Drop for Owned<T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: By existence of `&mut self` we exclusively own `self` and the underlying `T`. As
+ // we are dropping `self`, we can transfer ownership of the `T` to the `release` method.
+ unsafe { T::release(self.ptr) };
+ }
+}
diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
index b721b2e00b986..3bd5eb8a1a526 100644
--- a/rust/kernel/sync/aref.rs
+++ b/rust/kernel/sync/aref.rs
@@ -34,6 +34,11 @@
/// Rust code, the recommendation is to use [`Arc`](crate::sync::Arc) to create reference-counted
/// instances of a type.
///
+/// Note: Implementing this trait allows types to be wrapped in an [`ARef<Self>`]. It requires an
+/// internal reference count and provides only shared references. If unique references are required
+/// [`Ownable`](crate::types::Ownable) should be implemented which allows types to be wrapped in an
+/// [`Owned<Self>`](crate::types::Owned).
+///
/// # Safety
///
/// Implementers must ensure that increments to the reference count keep the object alive in memory
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index ac316fd7b538f..c41eab0ec983c 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -15,6 +15,11 @@
pub mod for_lt;
pub use for_lt::ForLt;
+pub use crate::owned::{
+ Ownable,
+ Owned, //
+};
+
/// Used to transfer ownership to and from foreign (non-Rust) languages.
///
/// Ownership is transferred from Rust to a foreign language by calling [`Self::into_foreign`] and
--
2.51.2
^ permalink raw reply related
* [PATCH v19 8/8] rust: page: add `from_raw()`
From: Andreas Hindborg @ 2026-06-26 11:54 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Andreas Hindborg
In-Reply-To: <20260626-unique-ref-v19-0-2607ca88dfdf@kernel.org>
From: Andreas Hindborg <a.hindborg@samsung.com>
Add a method to `Page` that allows construction of an instance from `struct
page` pointer.
Signed-off-by: Andreas Hindborg <a.hindborg@samsung.com>
Reviewed-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/page.rs | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
index 6dc1c2395acaf..c88fda09ead5a 100644
--- a/rust/kernel/page.rs
+++ b/rust/kernel/page.rs
@@ -143,6 +143,20 @@ pub fn nid(&self) -> i32 {
unsafe { bindings::page_to_nid(self.as_ptr()) }
}
+ /// Create a `&Page` from a raw `struct page` pointer.
+ ///
+ /// # Safety
+ ///
+ /// `ptr` must be convertible to a shared reference with a lifetime of `'a`.
+ #[inline]
+ pub unsafe fn from_raw<'a>(ptr: *const bindings::page) -> &'a Self {
+ // INVARIANT: By the function safety requirements, `ptr` refers to a valid `struct page`, so
+ // the returned reference upholds the type invariant of `Page`.
+ // SAFETY: By function safety requirements, `ptr` is not null and is convertible to a shared
+ // reference.
+ unsafe { &*ptr.cast() }
+ }
+
/// Runs a piece of code with this page mapped to an address.
///
/// The page is unmapped when this call returns.
--
2.51.2
^ permalink raw reply related
* [RFC PATCH 4/4] capability: unexport has_capability_noaudit
From: cem @ 2026-06-26 11:45 UTC (permalink / raw)
To: linux-fsdevel
Cc: jack, djwong, hch, serge, linux-security-module, linux-kernel,
linux-xfs, Carlos Maiolino
In-Reply-To: <20260626114533.102138-1-cem@kernel.org>
From: Carlos Maiolino <cem@kernel.org>
This has been originally exported to be used in xfs. Givin we are not
using it anymore, unexport for consistency.
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
---
kernel/capability.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/kernel/capability.c b/kernel/capability.c
index 2c2d1e8300bd..3d0387fb93a3 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -326,7 +326,6 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
{
return has_ns_capability_noaudit(t, &init_user_ns, cap);
}
-EXPORT_SYMBOL(has_capability_noaudit);
static bool ns_capable_common(struct user_namespace *ns,
int cap,
--
2.54.0
^ permalink raw reply related
* [RFC PATCH 3/4] xfs: replace ns_capable_noaudit()
From: cem @ 2026-06-26 11:45 UTC (permalink / raw)
To: linux-fsdevel
Cc: jack, djwong, hch, serge, linux-security-module, linux-kernel,
linux-xfs, Carlos Maiolino
In-Reply-To: <20260626114533.102138-1-cem@kernel.org>
From: Carlos Maiolino <cem@kernel.org>
We don't need to use ns_capable_noaudit() as all we care is the initial
user namespace, use capable_noaudit() instead.
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
---
fs/xfs/xfs_trans_dquot.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index 50e5b323f7f1..30c2f6ec0aac 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -835,7 +835,7 @@ xfs_trans_dqresv(
if ((flags & XFS_QMOPT_FORCE_RES) == 0 &&
dqp->q_id &&
xfs_dquot_is_enforced(dqp) &&
- !ns_capable_noaudit(&init_user_ns, CAP_SYS_RESOURCE)) {
+ !capable_noaudit(CAP_SYS_RESOURCE)) {
int quota_nl;
bool fatal;
--
2.54.0
^ permalink raw reply related
* [RFC PATCH 2/4] quota: Don't issue audit messages on quota enforcing
From: cem @ 2026-06-26 11:45 UTC (permalink / raw)
To: linux-fsdevel
Cc: jack, djwong, hch, serge, linux-security-module, linux-kernel,
linux-xfs, Carlos Maiolino
In-Reply-To: <20260626114533.102138-1-cem@kernel.org>
From: Carlos Maiolino <cem@kernel.org>
Calling capable() to determine if we can bypass quota enforcement or not
can trigger spurious audit messages. We don't really require it here so
just use the capable_noaudit() version.
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
---
fs/quota/dquot.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 64cf42721496..1122a29215f7 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1308,7 +1308,7 @@ static int ignore_hardlimit(struct dquot *dquot)
{
struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type];
- return capable(CAP_SYS_RESOURCE) &&
+ return capable_noaudit(CAP_SYS_RESOURCE) &&
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD ||
!(info->dqi_flags & DQF_ROOT_SQUASH));
}
--
2.54.0
^ permalink raw reply related
* [RFC PATCH 1/4] capabily: Add new capable_noaudit
From: cem @ 2026-06-26 11:45 UTC (permalink / raw)
To: linux-fsdevel
Cc: jack, djwong, hch, serge, linux-security-module, linux-kernel,
linux-xfs, Carlos Maiolino
In-Reply-To: <20260626114533.102138-1-cem@kernel.org>
From: Carlos Maiolino <cem@kernel.org>
In some situations (quota enforcement bypass in this case) we'd like to
check for a specific capability without triggering spurious audit
messages from security modules like selinux.
Add a new helper so we don't need to use ns_capable_noaudit() directly.
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
---
include/linux/capability.h | 5 +++++
kernel/capability.c | 17 +++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 37db92b3d6f8..873416ba884c 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -145,6 +145,7 @@ extern bool has_capability_noaudit(struct task_struct *t, int cap);
extern bool has_ns_capability_noaudit(struct task_struct *t,
struct user_namespace *ns, int cap);
extern bool capable(int cap);
+extern bool capable_noaudit(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
extern bool ns_capable_setid(struct user_namespace *ns, int cap);
@@ -167,6 +168,10 @@ static inline bool capable(int cap)
{
return true;
}
+static inline bool capable_noaudit(int cap)
+{
+ return true;
+}
static inline bool ns_capable(struct user_namespace *ns, int cap)
{
return true;
diff --git a/kernel/capability.c b/kernel/capability.c
index 829f49ae07b9..2c2d1e8300bd 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -416,6 +416,23 @@ bool capable(int cap)
return ns_capable(&init_user_ns, cap);
}
EXPORT_SYMBOL(capable);
+
+/**
+ * capable_noaudit - Determine if the current task has a superior
+ * capability in effect (unaudited).
+ * @cap: The capability to be tested for
+ *
+ * This is the same as capable(), except it uses CAP_OPT_NOAUDIT as to prevent
+ * issuing spurious audit messages.
+ *
+ * This sets PF_SUPERPRIV on the task if the capability is available on the
+ * assumption that it's about to be used.
+ */
+bool capable_noaudit(int cap)
+{
+ return ns_capable_noaudit(&init_user_ns, cap);
+}
+EXPORT_SYMBOL(capable_noaudit);
#endif /* CONFIG_MULTIUSER */
/**
--
2.54.0
^ permalink raw reply related
* [RFC PATCH 0/4] Introduce capable_noaudit
From: cem @ 2026-06-26 11:45 UTC (permalink / raw)
To: linux-fsdevel
Cc: jack, djwong, hch, serge, linux-security-module, linux-kernel,
linux-xfs, Carlos Maiolino
From: Carlos Maiolino <cem@kernel.org>
In some cases - filesystems quota specifically here - we'd like to check
for effective capabilities without issuing spurious audit messages and
without the need to specify a namespace for that.
This series introduce capable_noaudit() which has the same goal as
capable() but without firing audit messages.
Also, this updates both generic quota and xfs quota code to use that.
The last patch unexports has_capability_noaudit() which was originally
exported to be used in xfs but turns out it does not meet our needs.
Note this is based on top of a current series I have to remove
has_capability_noaudit() calls from xfs so the xfs patch won't
apply cleanly without that series.
If adding this helper is acceptable, I'll turn this into a non-rfc
series with the required changes to apply properly.
Comments? Flames?
Cheers
Carlos Maiolino (4):
capabily: Add new capable_noaudit
quota: Don't issue audit messages on quota enforcing
xfs: replace ns_capable_noaudit()
capability: unexport has_capability_noaudit
fs/quota/dquot.c | 2 +-
fs/xfs/xfs_trans_dquot.c | 2 +-
include/linux/capability.h | 5 +++++
kernel/capability.c | 18 +++++++++++++++++-
4 files changed, 24 insertions(+), 3 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH v2 stable/linux-6.18.y 2/2] selinux: fix overlayfs mmap() and mprotect() access checks
From: Cai Xinchen @ 2026-06-26 7:50 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, bboscaccy, caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260626075035.143419-1-caixinchen1@huawei.com>
From: Paul Moore <paul@paul-moore.com>
[ Upstream commit 82544d36b1729153c8aeb179e84750f0c085d3b1 ]
The existing SELinux security model for overlayfs is to allow access if
the current task is able to access the top level file (the "user" file)
and the mounter's credentials are sufficient to access the lower
level file (the "backing" file). Unfortunately, the current code does
not properly enforce these access controls for both mmap() and mprotect()
operations on overlayfs filesystems.
This patch makes use of the newly created security_mmap_backing_file()
LSM hook to provide the missing backing file enforcement for mmap()
operations, and leverages the backing file API and new LSM blob to
provide the necessary information to properly enforce the mprotect()
access controls.
Cc: stable@vger.kernel.org
Acked-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
security/selinux/hooks.c | 242 ++++++++++++++++++++++--------
security/selinux/include/objsec.h | 11 ++
2 files changed, 189 insertions(+), 64 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3da3017ad2ca..f96ee8f372e3 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1739,49 +1739,72 @@ static inline int file_path_has_perm(const struct cred *cred,
static int bpf_fd_pass(const struct file *file, u32 sid);
#endif
-/* Check whether a task can use an open file descriptor to
- access an inode in a given way. Check access to the
- descriptor itself, and then use dentry_has_perm to
- check a particular permission to the file.
- Access to the descriptor is implicitly granted if it
- has the same SID as the process. If av is zero, then
- access to the file is not checked, e.g. for cases
- where only the descriptor is affected like seek. */
-static int file_has_perm(const struct cred *cred,
- struct file *file,
- u32 av)
+static int __file_has_perm(const struct cred *cred, const struct file *file,
+ u32 av, bool bf_user_file)
+
{
- struct file_security_struct *fsec = selinux_file(file);
- struct inode *inode = file_inode(file);
struct common_audit_data ad;
- u32 sid = cred_sid(cred);
+ struct inode *inode;
+ u32 ssid = cred_sid(cred);
+ u32 tsid_fd;
int rc;
- ad.type = LSM_AUDIT_DATA_FILE;
- ad.u.file = file;
+ if (bf_user_file) {
+ struct backing_file_security_struct *bfsec;
+ const struct path *path;
- if (sid != fsec->sid) {
- rc = avc_has_perm(sid, fsec->sid,
- SECCLASS_FD,
- FD__USE,
- &ad);
+ if (WARN_ON(!(file->f_mode & FMODE_BACKING)))
+ return -EIO;
+
+ bfsec = selinux_backing_file(file);
+ path = backing_file_user_path(file);
+ tsid_fd = bfsec->uf_sid;
+ inode = d_inode(path->dentry);
+
+ ad.type = LSM_AUDIT_DATA_PATH;
+ ad.u.path = *path;
+ } else {
+ struct file_security_struct *fsec = selinux_file(file);
+
+ tsid_fd = fsec->sid;
+ inode = file_inode(file);
+
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
+ }
+
+ if (ssid != tsid_fd) {
+ rc = avc_has_perm(ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad);
if (rc)
- goto out;
+ return rc;
}
#ifdef CONFIG_BPF_SYSCALL
- rc = bpf_fd_pass(file, cred_sid(cred));
+ /* regardless of backing vs user file, use the underlying file here */
+ rc = bpf_fd_pass(file, ssid);
if (rc)
return rc;
#endif
/* av is zero if only checking access to the descriptor. */
- rc = 0;
if (av)
- rc = inode_has_perm(cred, inode, av, &ad);
+ return inode_has_perm(cred, inode, av, &ad);
-out:
- return rc;
+ return 0;
+}
+
+/* Check whether a task can use an open file descriptor to
+ access an inode in a given way. Check access to the
+ descriptor itself, and then use dentry_has_perm to
+ check a particular permission to the file.
+ Access to the descriptor is implicitly granted if it
+ has the same SID as the process. If av is zero, then
+ access to the file is not checked, e.g. for cases
+ where only the descriptor is affected like seek. */
+static inline int file_has_perm(const struct cred *cred,
+ const struct file *file, u32 av)
+{
+ return __file_has_perm(cred, file, av, false);
}
/*
@@ -3799,6 +3822,17 @@ static int selinux_file_alloc_security(struct file *file)
return 0;
}
+static int selinux_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ struct backing_file_security_struct *bfsec;
+
+ bfsec = selinux_backing_file(backing_file);
+ bfsec->uf_sid = selinux_file(user_file)->sid;
+
+ return 0;
+}
+
/*
* Check whether a task has the ioctl permission and cmd
* operation to an inode.
@@ -3916,42 +3950,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
static int default_noexec __ro_after_init;
-static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
+static int __file_map_prot_check(const struct cred *cred,
+ const struct file *file, unsigned long prot,
+ bool shared, bool bf_user_file)
{
- const struct cred *cred = current_cred();
- u32 sid = cred_sid(cred);
- int rc = 0;
+ struct inode *inode = NULL;
+ bool prot_exec = prot & PROT_EXEC;
+ bool prot_write = prot & PROT_WRITE;
+
+ if (file) {
+ if (bf_user_file)
+ inode = d_inode(backing_file_user_path(file)->dentry);
+ else
+ inode = file_inode(file);
+ }
+
+ if (default_noexec && prot_exec &&
+ (!file || IS_PRIVATE(inode) || (!shared && prot_write))) {
+ int rc;
+ u32 sid = cred_sid(cred);
- if (default_noexec &&
- (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) ||
- (!shared && (prot & PROT_WRITE)))) {
/*
- * We are making executable an anonymous mapping or a
- * private file mapping that will also be writable.
- * This has an additional check.
+ * We are making executable an anonymous mapping or a private
+ * file mapping that will also be writable.
*/
- rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
- PROCESS__EXECMEM, NULL);
+ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM,
+ NULL);
if (rc)
- goto error;
+ return rc;
}
if (file) {
- /* read access is always possible with a mapping */
+ /* "read" always possible, "write" only if shared */
u32 av = FILE__READ;
-
- /* write access only matters if the mapping is shared */
- if (shared && (prot & PROT_WRITE))
+ if (shared && prot_write)
av |= FILE__WRITE;
-
- if (prot & PROT_EXEC)
+ if (prot_exec)
av |= FILE__EXECUTE;
- return file_has_perm(cred, file, av);
+ return __file_has_perm(cred, file, av, bf_user_file);
}
-error:
- return rc;
+ return 0;
+}
+
+static inline int file_map_prot_check(const struct cred *cred,
+ const struct file *file,
+ unsigned long prot, bool shared)
+{
+ return __file_map_prot_check(cred, file, prot, shared, false);
}
static int selinux_mmap_addr(unsigned long addr)
@@ -3967,36 +4014,80 @@ static int selinux_mmap_addr(unsigned long addr)
return rc;
}
-static int selinux_mmap_file(struct file *file,
- unsigned long reqprot __always_unused,
- unsigned long prot, unsigned long flags)
+static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
+ unsigned long prot, bool shared)
{
- struct common_audit_data ad;
- int rc;
-
if (file) {
+ int rc;
+ struct common_audit_data ad;
+
ad.type = LSM_AUDIT_DATA_FILE;
ad.u.file = file;
- rc = inode_has_perm(current_cred(), file_inode(file),
- FILE__MAP, &ad);
+ rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad);
if (rc)
return rc;
}
- return file_map_prot_check(file, prot,
- (flags & MAP_TYPE) == MAP_SHARED);
+ return file_map_prot_check(cred, file, prot, shared);
+}
+
+static int selinux_mmap_file(struct file *file,
+ unsigned long reqprot __always_unused,
+ unsigned long prot, unsigned long flags)
+{
+ return selinux_mmap_file_common(current_cred(), file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED);
+}
+
+/**
+ * selinux_mmap_backing_file - Check mmap permissions on a backing file
+ * @vma: memory region
+ * @backing_file: stacked filesystem backing file
+ * @user_file: user visible file
+ *
+ * This is called after selinux_mmap_file() on stacked filesystems, and it
+ * is this function's responsibility to verify access to @backing_file and
+ * setup the SELinux state for possible later use in the mprotect() code path.
+ *
+ * By the time this function is called, mmap() access to @user_file has already
+ * been authorized and @vma->vm_file has been set to point to @backing_file.
+ *
+ * Return zero on success, negative values otherwise.
+ */
+static int selinux_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file __always_unused)
+{
+ unsigned long prot = 0;
+
+ /* translate vma->vm_flags perms into PROT perms */
+ if (vma->vm_flags & VM_READ)
+ prot |= PROT_READ;
+ if (vma->vm_flags & VM_WRITE)
+ prot |= PROT_WRITE;
+ if (vma->vm_flags & VM_EXEC)
+ prot |= PROT_EXEC;
+
+ return selinux_mmap_file_common(backing_file->f_cred, backing_file,
+ prot, vma->vm_flags & VM_SHARED);
}
static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long reqprot __always_unused,
unsigned long prot)
{
+ int rc;
const struct cred *cred = current_cred();
u32 sid = cred_sid(cred);
+ const struct file *file = vma->vm_file;
+ bool backing_file;
+ bool shared = vma->vm_flags & VM_SHARED;
+
+ /* check if we need to trigger the "backing files are awful" mode */
+ backing_file = file && (file->f_mode & FMODE_BACKING);
if (default_noexec &&
(prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
- int rc = 0;
/*
* We don't use the vma_is_initial_heap() helper as it has
* a history of problems and is currently broken on systems
@@ -4010,11 +4101,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
vma->vm_end <= vma->vm_mm->brk) {
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECHEAP, NULL);
- } else if (!vma->vm_file && (vma_is_initial_stack(vma) ||
+ if (rc)
+ return rc;
+ } else if (!file && (vma_is_initial_stack(vma) ||
vma_is_stack_for_current(vma))) {
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECSTACK, NULL);
- } else if (vma->vm_file && vma->anon_vma) {
+ if (rc)
+ return rc;
+ } else if (file && vma->anon_vma) {
/*
* We are making executable a file mapping that has
* had some COW done. Since pages might have been
@@ -4022,13 +4117,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
* modified content. This typically should only
* occur for text relocations.
*/
- rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
+ rc = __file_has_perm(cred, file, FILE__EXECMOD,
+ backing_file);
+ if (rc)
+ return rc;
+ if (backing_file) {
+ rc = file_has_perm(file->f_cred, file,
+ FILE__EXECMOD);
+ if (rc)
+ return rc;
+ }
}
+ }
+
+ rc = __file_map_prot_check(cred, file, prot, shared, backing_file);
+ if (rc)
+ return rc;
+ if (backing_file) {
+ rc = file_map_prot_check(file->f_cred, file, prot, shared);
if (rc)
return rc;
}
- return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
+ return 0;
}
static int selinux_file_lock(struct file *file, unsigned int cmd)
@@ -7140,6 +7251,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
.lbs_cred = sizeof(struct cred_security_struct),
.lbs_task = sizeof(struct task_security_struct),
.lbs_file = sizeof(struct file_security_struct),
+ .lbs_backing_file = sizeof(struct backing_file_security_struct),
.lbs_inode = sizeof(struct inode_security_struct),
.lbs_ipc = sizeof(struct ipc_security_struct),
.lbs_key = sizeof(struct key_security_struct),
@@ -7363,9 +7475,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_permission, selinux_file_permission),
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
+ LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
+ LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file),
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
LSM_HOOK_INIT(file_lock, selinux_file_lock),
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 816fde5a5896..fcb46793898f 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -86,6 +86,10 @@ struct file_security_struct {
u32 pseqno; /* Policy seqno at the time of file open */
};
+struct backing_file_security_struct {
+ u32 uf_sid; /* associated user file fsec->sid */
+};
+
struct superblock_security_struct {
u32 sid; /* SID of file system superblock */
u32 def_sid; /* default SID for labeling */
@@ -190,6 +194,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file)
return file->f_security + selinux_blob_sizes.lbs_file;
}
+static inline struct backing_file_security_struct *
+selinux_backing_file(const struct file *backing_file)
+{
+ void *blob = backing_file_security(backing_file);
+ return blob + selinux_blob_sizes.lbs_backing_file;
+}
+
static inline struct inode_security_struct *
selinux_inode(const struct inode *inode)
{
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH v2 stable/linux-6.18.y 1/2] lsm: add backing_file LSM hooks
From: Cai Xinchen @ 2026-06-26 7:50 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, bboscaccy, caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260626075035.143419-1-caixinchen1@huawei.com>
From: Paul Moore <paul@paul-moore.com>
[ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ]
Stacked filesystems such as overlayfs do not currently provide the
necessary mechanisms for LSMs to properly enforce access controls on the
mmap() and mprotect() operations. In order to resolve this gap, a LSM
security blob is being added to the backing_file struct and the following
new LSM hooks are being created:
security_backing_file_alloc()
security_backing_file_free()
security_mmap_backing_file()
The first two hooks are to manage the lifecycle of the LSM security blob
in the backing_file struct, while the third provides a new mmap() access
control point for the underlying backing file. It is also expected that
LSMs will likely want to update their security_file_mprotect() callback
to address issues with their mprotect() controls, but that does not
require a change to the security_file_mprotect() LSM hook.
There are a three other small changes to support these new LSM hooks:
* Pass the user file associated with a backing file down to
alloc_empty_backing_file() so it can be included in the
security_backing_file_alloc() hook.
* Add getter and setter functions for the backing_file struct LSM blob
as the backing_file struct remains private to fs/file_table.c.
* Constify the file struct field in the LSM common_audit_data struct to
better support LSMs that need to pass a const file struct pointer into
the common LSM audit code.
Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL()
and supplying a fixup.
Cc: stable@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-unionfs@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Paul Moore <paul@paul-moore.com>
[Mainline declares lsm_backing_file_cache in security/lsm.h. Linux 6.18.y
does not have security/lsm_init.c or security/lsm.h; the cache variable
is defined locally as static struct kmem_cache *lsm_backing_file_cache in
security/security.c.]
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
fs/backing-file.c | 17 ++++--
fs/file_table.c | 27 +++++++--
fs/fuse/passthrough.c | 2 +-
fs/internal.h | 3 +-
fs/overlayfs/dir.c | 2 +-
fs/overlayfs/file.c | 2 +-
include/linux/backing-file.h | 4 +-
include/linux/fs.h | 13 ++++
include/linux/lsm_audit.h | 2 +-
include/linux/lsm_hook_defs.h | 5 ++
include/linux/lsm_hooks.h | 1 +
include/linux/security.h | 22 +++++++
security/security.c | 109 ++++++++++++++++++++++++++++++++++
13 files changed, 194 insertions(+), 15 deletions(-)
diff --git a/fs/backing-file.c b/fs/backing-file.c
index 15a7f8031084..e049a627d78f 100644
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@ -12,6 +12,7 @@
#include <linux/backing-file.h>
#include <linux/splice.h>
#include <linux/mm.h>
+#include <linux/security.h>
#include "internal.h"
@@ -29,14 +30,15 @@
* returned file into a container structure that also stores the stacked
* file's path, which can be retrieved using backing_file_user_path().
*/
-struct file *backing_file_open(const struct path *user_path, int flags,
+struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred)
{
+ const struct path *user_path = &user_file->f_path;
struct file *f;
int error;
- f = alloc_empty_backing_file(flags, cred);
+ f = alloc_empty_backing_file(flags, cred, user_file);
if (IS_ERR(f))
return f;
@@ -52,15 +54,16 @@ struct file *backing_file_open(const struct path *user_path, int flags,
}
EXPORT_SYMBOL_GPL(backing_file_open);
-struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+struct file *backing_tmpfile_open(const struct file *user_file, int flags,
const struct path *real_parentpath,
umode_t mode, const struct cred *cred)
{
struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt);
+ const struct path *user_path = &user_file->f_path;
struct file *f;
int error;
- f = alloc_empty_backing_file(flags, cred);
+ f = alloc_empty_backing_file(flags, cred, user_file);
if (IS_ERR(f))
return f;
@@ -339,6 +342,12 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
vma_set_file(vma, file);
old_cred = override_creds(ctx->cred);
+ ret = security_mmap_backing_file(vma, file, user_file);
+ if (ret) {
+ revert_creds(old_cred);
+ return ret;
+ }
+
ret = vfs_mmap(vma->vm_file, vma);
revert_creds(old_cred);
diff --git a/fs/file_table.c b/fs/file_table.c
index 762f03dcbcd7..987e01da9938 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -50,6 +50,9 @@ struct backing_file {
struct path user_path;
freeptr_t bf_freeptr;
};
+#ifdef CONFIG_SECURITY
+ void *security;
+#endif
};
#define backing_file(f) container_of(f, struct backing_file, file)
@@ -66,8 +69,21 @@ void backing_file_set_user_path(struct file *f, const struct path *path)
}
EXPORT_SYMBOL_GPL(backing_file_set_user_path);
+#ifdef CONFIG_SECURITY
+void *backing_file_security(const struct file *f)
+{
+ return backing_file(f)->security;
+}
+
+void backing_file_set_security(struct file *f, void *security)
+{
+ backing_file(f)->security = security;
+}
+#endif /* CONFIG_SECURITY */
+
static inline void backing_file_free(struct backing_file *ff)
{
+ security_backing_file_free(&ff->file);
path_put(&ff->user_path);
kmem_cache_free(bfilp_cachep, ff);
}
@@ -288,10 +304,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
return f;
}
-static int init_backing_file(struct backing_file *ff)
+static int init_backing_file(struct backing_file *ff,
+ const struct file *user_file)
{
memset(&ff->user_path, 0, sizeof(ff->user_path));
- return 0;
+ backing_file_set_security(&ff->file, NULL);
+ return security_backing_file_alloc(&ff->file, user_file);
}
/*
@@ -301,7 +319,8 @@ static int init_backing_file(struct backing_file *ff)
* This is only for kernel internal use, and the allocate file must not be
* installed into file tables or such.
*/
-struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
+ const struct file *user_file)
{
struct backing_file *ff;
int error;
@@ -318,7 +337,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
/* The f_mode flags must be set before fput(). */
ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
- error = init_backing_file(ff);
+ error = init_backing_file(ff, user_file);
if (unlikely(error)) {
fput(&ff->file);
return ERR_PTR(error);
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index 72de97c03d0e..f2d08ac2459b 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -167,7 +167,7 @@ struct fuse_backing *fuse_passthrough_open(struct file *file, int backing_id)
goto out;
/* Allocate backing file per fuse file to store fuse path */
- backing_file = backing_file_open(&file->f_path, file->f_flags,
+ backing_file = backing_file_open(file, file->f_flags,
&fb->file->f_path, fb->cred);
err = PTR_ERR(backing_file);
if (IS_ERR(backing_file)) {
diff --git a/fs/internal.h b/fs/internal.h
index 9b2b4d116880..51107fd51514 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -100,7 +100,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
*/
struct file *alloc_empty_file(int flags, const struct cred *cred);
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
-struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
+ const struct file *user_file);
void backing_file_set_user_path(struct file *f, const struct path *path);
static inline void file_put_write_access(struct file *file)
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index a5e9ddf3023b..e924321b6402 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -1355,7 +1355,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
}
ovl_path_upper(dentry->d_parent, &realparentpath);
- realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
+ realfile = backing_tmpfile_open(file, flags, &realparentpath,
mode, current_cred());
err = PTR_ERR_OR_ZERO(realfile);
pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 7ab2c9daffd0..3fedfdddfa75 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -48,7 +48,7 @@ static struct file *ovl_open_realfile(const struct file *file,
if (!inode_owner_or_capable(real_idmap, realinode))
flags &= ~O_NOATIME;
- realfile = backing_file_open(file_user_path(file),
+ realfile = backing_file_open(file,
flags, realpath, current_cred());
}
ovl_revert_creds(old_cred);
diff --git a/include/linux/backing-file.h b/include/linux/backing-file.h
index 1476a6ed1bfd..c939cd222730 100644
--- a/include/linux/backing-file.h
+++ b/include/linux/backing-file.h
@@ -18,10 +18,10 @@ struct backing_file_ctx {
void (*end_write)(struct kiocb *iocb, ssize_t);
};
-struct file *backing_file_open(const struct path *user_path, int flags,
+struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred);
-struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+struct file *backing_tmpfile_open(const struct file *user_file, int flags,
const struct path *real_parentpath,
umode_t mode, const struct cred *cred);
ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 014cb04eefbe..f3e798184a58 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2890,6 +2890,19 @@ struct file *dentry_create(const struct path *path, int flags, umode_t mode,
const struct cred *cred);
const struct path *backing_file_user_path(const struct file *f);
+#ifdef CONFIG_SECURITY
+void *backing_file_security(const struct file *f);
+void backing_file_set_security(struct file *f, void *security);
+#else
+static inline void *backing_file_security(const struct file *f)
+{
+ return NULL;
+}
+static inline void backing_file_set_security(struct file *f, void *security)
+{
+}
+#endif /* CONFIG_SECURITY */
+
/*
* When mmapping a file on a stackable filesystem (e.g., overlayfs), the file
* stored in ->vm_file is a backing file whose f_inode is on the underlying
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 382c56a97bba..584db296e43b 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -94,7 +94,7 @@ struct common_audit_data {
#endif
char *kmod_name;
struct lsm_ioctlop_audit *op;
- struct file *file;
+ const struct file *file;
struct lsm_ibpkey_audit *ibpkey;
struct lsm_ibendport_audit *ibendport;
int reason;
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 8c42b4bde09c..b4958167e381 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -191,6 +191,9 @@ LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
LSM_HOOK(int, 0, file_alloc_security, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
+LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file,
+ const struct file *user_file)
+LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file)
LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
unsigned long arg)
LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
@@ -198,6 +201,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
+LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma,
+ struct file *backing_file, struct file *user_file)
LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot)
LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 79ec5a2bdcca..ea4b0f5ca7f0 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -104,6 +104,7 @@ struct security_hook_list {
struct lsm_blob_sizes {
int lbs_cred;
int lbs_file;
+ int lbs_backing_file;
int lbs_ib;
int lbs_inode;
int lbs_sock;
diff --git a/include/linux/security.h b/include/linux/security.h
index b64598e5d65d..e54025362426 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -473,11 +473,17 @@ int security_file_permission(struct file *file, int mask);
int security_file_alloc(struct file *file);
void security_file_release(struct file *file);
void security_file_free(struct file *file);
+int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file);
+void security_backing_file_free(struct file *backing_file);
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int security_file_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg);
int security_mmap_file(struct file *file, unsigned long prot,
unsigned long flags);
+int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file);
int security_mmap_addr(unsigned long addr);
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot);
@@ -1142,6 +1148,15 @@ static inline void security_file_release(struct file *file)
static inline void security_file_free(struct file *file)
{ }
+static inline int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ return 0;
+}
+
+static inline void security_backing_file_free(struct file *backing_file)
+{ }
+
static inline int security_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -1161,6 +1176,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot,
return 0;
}
+static inline int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file)
+{
+ return 0;
+}
+
static inline int security_mmap_addr(unsigned long addr)
{
return cap_mmap_addr(addr);
diff --git a/security/security.c b/security/security.c
index 603c3c6d5635..9285909908ab 100644
--- a/security/security.c
+++ b/security/security.c
@@ -94,6 +94,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
static struct kmem_cache *lsm_file_cache;
+static struct kmem_cache *lsm_backing_file_cache;
static struct kmem_cache *lsm_inode_cache;
char *lsm_names;
@@ -265,6 +266,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
+ lsm_set_blob_size(&needed->lbs_backing_file, &blob_sizes.lbs_backing_file);
lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
/*
* The inode blob gets an rcu_head in addition to
@@ -470,6 +472,7 @@ static void __init ordered_lsm_init(void)
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
+ init_debug("lsm_backing_file_cache = %d\n", blob_sizes.lbs_backing_file);
init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
@@ -495,6 +498,11 @@ static void __init ordered_lsm_init(void)
lsm_file_cache = kmem_cache_create("lsm_file_cache",
blob_sizes.lbs_file, 0,
SLAB_PANIC, NULL);
+ if (blob_sizes.lbs_backing_file)
+ lsm_backing_file_cache = kmem_cache_create(
+ "lsm_backing_file_cache",
+ blob_sizes.lbs_backing_file,
+ 0, SLAB_PANIC, NULL);
if (blob_sizes.lbs_inode)
lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
blob_sizes.lbs_inode, 0,
@@ -671,6 +679,30 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb)
}
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
+/**
+ * lsm_backing_file_alloc - allocate a composite backing file blob
+ * @backing_file: the backing file
+ *
+ * Allocate the backing file blob for all the modules.
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_backing_file_alloc(struct file *backing_file)
+{
+ void *blob;
+
+ if (!lsm_backing_file_cache) {
+ backing_file_set_security(backing_file, NULL);
+ return 0;
+ }
+
+ blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL);
+ backing_file_set_security(backing_file, blob);
+ if (!blob)
+ return -ENOMEM;
+ return 0;
+}
+
/**
* lsm_blob_alloc - allocate a composite blob
* @dest: the destination for the blob
@@ -2965,6 +2997,57 @@ void security_file_free(struct file *file)
}
}
+/**
+ * security_backing_file_alloc() - Allocate and setup a backing file blob
+ * @backing_file: the backing file
+ * @user_file: the associated user visible file
+ *
+ * Allocate a backing file LSM blob and perform any necessary initialization of
+ * the LSM blob. There will be some operations where the LSM will not have
+ * access to @user_file after this point, so any important state associated
+ * with @user_file that is important to the LSM should be captured in the
+ * backing file's LSM blob.
+ *
+ * LSM's should avoid taking a reference to @user_file in this hook as it will
+ * result in problems later when the system attempts to drop/put the file
+ * references due to a circular dependency.
+ *
+ * Return: Return 0 if the hook is successful, negative values otherwise.
+ */
+int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ int rc;
+
+ rc = lsm_backing_file_alloc(backing_file);
+ if (rc)
+ return rc;
+ rc = call_int_hook(backing_file_alloc, backing_file, user_file);
+ if (unlikely(rc))
+ security_backing_file_free(backing_file);
+
+ return rc;
+}
+
+/**
+ * security_backing_file_free() - Free a backing file blob
+ * @backing_file: the backing file
+ *
+ * Free any LSM state associate with a backing file's LSM blob, including the
+ * blob itself.
+ */
+void security_backing_file_free(struct file *backing_file)
+{
+ void *blob = backing_file_security(backing_file);
+
+ call_void_hook(backing_file_free, backing_file);
+
+ if (blob) {
+ backing_file_set_security(backing_file, NULL);
+ kmem_cache_free(lsm_backing_file_cache, blob);
+ }
+}
+
/**
* security_file_ioctl() - Check if an ioctl is allowed
* @file: associated file
@@ -3053,6 +3136,32 @@ int security_mmap_file(struct file *file, unsigned long prot,
flags);
}
+/**
+ * security_mmap_backing_file - Check if mmap'ing a backing file is allowed
+ * @vma: the vm_area_struct for the mmap'd region
+ * @backing_file: the backing file being mmap'd
+ * @user_file: the user file being mmap'd
+ *
+ * Check permissions for a mmap operation on a stacked filesystem. This hook
+ * is called after the security_mmap_file() and is responsible for authorizing
+ * the mmap on @backing_file. It is important to note that the mmap operation
+ * on @user_file has already been authorized and the @vma->vm_file has been
+ * set to @backing_file.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file)
+{
+ /* recommended by the stackable filesystem devs */
+ if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING)))
+ return -EIO;
+
+ return call_int_hook(mmap_backing_file, vma, backing_file, user_file);
+}
+EXPORT_SYMBOL_GPL(security_mmap_backing_file);
+
/**
* security_mmap_addr() - Check if mmap'ing an address is allowed
* @addr: address
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH v2 stable/linux-6.18.y 0/2] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Cai Xinchen @ 2026-06-26 7:50 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, bboscaccy, caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
v2: Add static to struct kmem_cache *lsm_backing_file_cache; and define
lbs_backing_file as int for keeping the same type as 6.18.
Backport the patch series
"Fix incorrect overlayfs mmap() and mprotect() LSM access controls" [1]
to 6.18 lts
I test selinux-testsuite[2] overlay test, it pass 135 tests.
[1] https://lore.kernel.org/all/20260403030848.731867-5-paul@paul-moore.com/
[2] https://github.com/SELinuxProject/selinux-testsuite
Paul Moore (2):
lsm: add backing_file LSM hooks
selinux: fix overlayfs mmap() and mprotect() access checks
fs/backing-file.c | 17 ++-
fs/file_table.c | 27 +++-
fs/fuse/passthrough.c | 2 +-
fs/internal.h | 3 +-
fs/overlayfs/dir.c | 2 +-
fs/overlayfs/file.c | 2 +-
include/linux/backing-file.h | 4 +-
include/linux/fs.h | 13 ++
include/linux/lsm_audit.h | 2 +-
include/linux/lsm_hook_defs.h | 5 +
include/linux/lsm_hooks.h | 1 +
include/linux/security.h | 22 +++
security/security.c | 109 ++++++++++++++
security/selinux/hooks.c | 242 ++++++++++++++++++++++--------
security/selinux/include/objsec.h | 11 ++
15 files changed, 383 insertions(+), 79 deletions(-)
--
2.18.0.huawei.25
^ permalink raw reply
* Re: [PATCH v2 stable/linux-6.18.y 0/2] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Greg KH @ 2026-06-26 7:07 UTC (permalink / raw)
To: Cai Xinchen
Cc: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, bboscaccy, linux-fsdevel,
linux-kernel, linux-unionfs, linux-security-module, selinux, bpf,
lujialin4
In-Reply-To: <20260626024058.3149217-1-caixinchen1@huawei.com>
On Fri, Jun 26, 2026 at 10:40:56AM +0800, Cai Xinchen wrote:
> v2: Add static to struct kmem_cache *lsm_backing_file_cache; and define
> lbs_backing_file as int for keeping the same type as 6.18.
Why are you not actually cc: stable@vger.kernel.org on backports you
want to see applied there?
confused,
greg k-h
^ permalink raw reply
* [PATCH] selftests/landlock: Fix snprintf truncation checks in test files
From: Wang Yan @ 2026-06-26 7:00 UTC (permalink / raw)
To: mic, gnoack, shuah
Cc: linux-security-module, linux-kselftest, linux-kernel, Wang Yan,
Haofeng Li
Commit b566f7a4f0e4 ("selftests/landlock: Fix snprintf truncation checks
in audit helpers") fixed the truncation detection in audit.h by changing
the comparison from ">" to ">=" to correctly handle the edge case where
snprintf returns a value equal to the buffer size.
However, the same pattern exists in ptrace_test.c, audit_test.c, and
net_test.c and was not fixed. snprintf() returns the number of characters
that would have been written, excluding the terminating NUL byte. When
the output is truncated, this return value equals or exceeds the buffer
size. The existing ">" check therefore fails to detect truncation when
the return value equals the buffer size.
Fix these remaining instances to use ">=" for truncation detection,
matching the fix in audit.h.
Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs")
Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
Signed-off-by: Wang Yan <wangyan01@kylinos.cn>
---
tools/testing/selftests/landlock/audit_test.c | 2 +-
tools/testing/selftests/landlock/net_test.c | 4 ++--
tools/testing/selftests/landlock/ptrace_test.c | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c
index 72b5612375dd..4bea8c880a4d 100644
--- a/tools/testing/selftests/landlock/audit_test.c
+++ b/tools/testing/selftests/landlock/audit_test.c
@@ -31,7 +31,7 @@ static int matches_log_signal(struct __test_metadata *const _metadata,
log_match_len =
snprintf(log_match, sizeof(log_match), log_template, opid);
- if (log_match_len > sizeof(log_match))
+ if (log_match_len >= sizeof(log_match))
return -E2BIG;
return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 2ed1f76b7a8b..aebeafd80466 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -2777,7 +2777,7 @@ static int matches_auditlog(const int audit_fd, const char *const blockers,
log_match_len = snprintf(log_match, sizeof(log_match),
log_with_addrport_tmpl, blockers,
dir_addr, addr, dir_port, port);
- if (log_match_len > sizeof(log_match))
+ if (log_match_len >= sizeof(log_match))
return -E2BIG;
return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
@@ -3072,7 +3072,7 @@ static int matches_log_connect_bound(int audit_fd, const char *const blockers,
log_match_len = snprintf(log_match, sizeof(log_match), log_template,
blockers, addr, lport, addr, dport);
- if (log_match_len > sizeof(log_match))
+ if (log_match_len >= sizeof(log_match))
return -E2BIG;
return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c
index 4f64c90583cd..65cf2d82f721 100644
--- a/tools/testing/selftests/landlock/ptrace_test.c
+++ b/tools/testing/selftests/landlock/ptrace_test.c
@@ -302,7 +302,7 @@ static int matches_log_ptrace(struct __test_metadata *const _metadata,
log_match_len =
snprintf(log_match, sizeof(log_match), log_template, opid);
- if (log_match_len > sizeof(log_match))
+ if (log_match_len >= sizeof(log_match))
return -E2BIG;
return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
--
2.25.1
^ permalink raw reply related
* [PATCH v2 stable/linux-6.18.y 2/2] selinux: fix overlayfs mmap() and mprotect() access checks
From: Cai Xinchen @ 2026-06-26 2:40 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, bboscaccy, caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, lujialin4
In-Reply-To: <20260626024058.3149217-1-caixinchen1@huawei.com>
From: Paul Moore <paul@paul-moore.com>
[ Upstream commit 82544d36b1729153c8aeb179e84750f0c085d3b1 ]
The existing SELinux security model for overlayfs is to allow access if
the current task is able to access the top level file (the "user" file)
and the mounter's credentials are sufficient to access the lower
level file (the "backing" file). Unfortunately, the current code does
not properly enforce these access controls for both mmap() and mprotect()
operations on overlayfs filesystems.
This patch makes use of the newly created security_mmap_backing_file()
LSM hook to provide the missing backing file enforcement for mmap()
operations, and leverages the backing file API and new LSM blob to
provide the necessary information to properly enforce the mprotect()
access controls.
Cc: stable@vger.kernel.org
Acked-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
security/selinux/hooks.c | 242 ++++++++++++++++++++++--------
security/selinux/include/objsec.h | 11 ++
2 files changed, 189 insertions(+), 64 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3da3017ad2ca..f96ee8f372e3 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1739,49 +1739,72 @@ static inline int file_path_has_perm(const struct cred *cred,
static int bpf_fd_pass(const struct file *file, u32 sid);
#endif
-/* Check whether a task can use an open file descriptor to
- access an inode in a given way. Check access to the
- descriptor itself, and then use dentry_has_perm to
- check a particular permission to the file.
- Access to the descriptor is implicitly granted if it
- has the same SID as the process. If av is zero, then
- access to the file is not checked, e.g. for cases
- where only the descriptor is affected like seek. */
-static int file_has_perm(const struct cred *cred,
- struct file *file,
- u32 av)
+static int __file_has_perm(const struct cred *cred, const struct file *file,
+ u32 av, bool bf_user_file)
+
{
- struct file_security_struct *fsec = selinux_file(file);
- struct inode *inode = file_inode(file);
struct common_audit_data ad;
- u32 sid = cred_sid(cred);
+ struct inode *inode;
+ u32 ssid = cred_sid(cred);
+ u32 tsid_fd;
int rc;
- ad.type = LSM_AUDIT_DATA_FILE;
- ad.u.file = file;
+ if (bf_user_file) {
+ struct backing_file_security_struct *bfsec;
+ const struct path *path;
- if (sid != fsec->sid) {
- rc = avc_has_perm(sid, fsec->sid,
- SECCLASS_FD,
- FD__USE,
- &ad);
+ if (WARN_ON(!(file->f_mode & FMODE_BACKING)))
+ return -EIO;
+
+ bfsec = selinux_backing_file(file);
+ path = backing_file_user_path(file);
+ tsid_fd = bfsec->uf_sid;
+ inode = d_inode(path->dentry);
+
+ ad.type = LSM_AUDIT_DATA_PATH;
+ ad.u.path = *path;
+ } else {
+ struct file_security_struct *fsec = selinux_file(file);
+
+ tsid_fd = fsec->sid;
+ inode = file_inode(file);
+
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
+ }
+
+ if (ssid != tsid_fd) {
+ rc = avc_has_perm(ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad);
if (rc)
- goto out;
+ return rc;
}
#ifdef CONFIG_BPF_SYSCALL
- rc = bpf_fd_pass(file, cred_sid(cred));
+ /* regardless of backing vs user file, use the underlying file here */
+ rc = bpf_fd_pass(file, ssid);
if (rc)
return rc;
#endif
/* av is zero if only checking access to the descriptor. */
- rc = 0;
if (av)
- rc = inode_has_perm(cred, inode, av, &ad);
+ return inode_has_perm(cred, inode, av, &ad);
-out:
- return rc;
+ return 0;
+}
+
+/* Check whether a task can use an open file descriptor to
+ access an inode in a given way. Check access to the
+ descriptor itself, and then use dentry_has_perm to
+ check a particular permission to the file.
+ Access to the descriptor is implicitly granted if it
+ has the same SID as the process. If av is zero, then
+ access to the file is not checked, e.g. for cases
+ where only the descriptor is affected like seek. */
+static inline int file_has_perm(const struct cred *cred,
+ const struct file *file, u32 av)
+{
+ return __file_has_perm(cred, file, av, false);
}
/*
@@ -3799,6 +3822,17 @@ static int selinux_file_alloc_security(struct file *file)
return 0;
}
+static int selinux_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ struct backing_file_security_struct *bfsec;
+
+ bfsec = selinux_backing_file(backing_file);
+ bfsec->uf_sid = selinux_file(user_file)->sid;
+
+ return 0;
+}
+
/*
* Check whether a task has the ioctl permission and cmd
* operation to an inode.
@@ -3916,42 +3950,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
static int default_noexec __ro_after_init;
-static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
+static int __file_map_prot_check(const struct cred *cred,
+ const struct file *file, unsigned long prot,
+ bool shared, bool bf_user_file)
{
- const struct cred *cred = current_cred();
- u32 sid = cred_sid(cred);
- int rc = 0;
+ struct inode *inode = NULL;
+ bool prot_exec = prot & PROT_EXEC;
+ bool prot_write = prot & PROT_WRITE;
+
+ if (file) {
+ if (bf_user_file)
+ inode = d_inode(backing_file_user_path(file)->dentry);
+ else
+ inode = file_inode(file);
+ }
+
+ if (default_noexec && prot_exec &&
+ (!file || IS_PRIVATE(inode) || (!shared && prot_write))) {
+ int rc;
+ u32 sid = cred_sid(cred);
- if (default_noexec &&
- (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) ||
- (!shared && (prot & PROT_WRITE)))) {
/*
- * We are making executable an anonymous mapping or a
- * private file mapping that will also be writable.
- * This has an additional check.
+ * We are making executable an anonymous mapping or a private
+ * file mapping that will also be writable.
*/
- rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
- PROCESS__EXECMEM, NULL);
+ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM,
+ NULL);
if (rc)
- goto error;
+ return rc;
}
if (file) {
- /* read access is always possible with a mapping */
+ /* "read" always possible, "write" only if shared */
u32 av = FILE__READ;
-
- /* write access only matters if the mapping is shared */
- if (shared && (prot & PROT_WRITE))
+ if (shared && prot_write)
av |= FILE__WRITE;
-
- if (prot & PROT_EXEC)
+ if (prot_exec)
av |= FILE__EXECUTE;
- return file_has_perm(cred, file, av);
+ return __file_has_perm(cred, file, av, bf_user_file);
}
-error:
- return rc;
+ return 0;
+}
+
+static inline int file_map_prot_check(const struct cred *cred,
+ const struct file *file,
+ unsigned long prot, bool shared)
+{
+ return __file_map_prot_check(cred, file, prot, shared, false);
}
static int selinux_mmap_addr(unsigned long addr)
@@ -3967,36 +4014,80 @@ static int selinux_mmap_addr(unsigned long addr)
return rc;
}
-static int selinux_mmap_file(struct file *file,
- unsigned long reqprot __always_unused,
- unsigned long prot, unsigned long flags)
+static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
+ unsigned long prot, bool shared)
{
- struct common_audit_data ad;
- int rc;
-
if (file) {
+ int rc;
+ struct common_audit_data ad;
+
ad.type = LSM_AUDIT_DATA_FILE;
ad.u.file = file;
- rc = inode_has_perm(current_cred(), file_inode(file),
- FILE__MAP, &ad);
+ rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad);
if (rc)
return rc;
}
- return file_map_prot_check(file, prot,
- (flags & MAP_TYPE) == MAP_SHARED);
+ return file_map_prot_check(cred, file, prot, shared);
+}
+
+static int selinux_mmap_file(struct file *file,
+ unsigned long reqprot __always_unused,
+ unsigned long prot, unsigned long flags)
+{
+ return selinux_mmap_file_common(current_cred(), file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED);
+}
+
+/**
+ * selinux_mmap_backing_file - Check mmap permissions on a backing file
+ * @vma: memory region
+ * @backing_file: stacked filesystem backing file
+ * @user_file: user visible file
+ *
+ * This is called after selinux_mmap_file() on stacked filesystems, and it
+ * is this function's responsibility to verify access to @backing_file and
+ * setup the SELinux state for possible later use in the mprotect() code path.
+ *
+ * By the time this function is called, mmap() access to @user_file has already
+ * been authorized and @vma->vm_file has been set to point to @backing_file.
+ *
+ * Return zero on success, negative values otherwise.
+ */
+static int selinux_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file __always_unused)
+{
+ unsigned long prot = 0;
+
+ /* translate vma->vm_flags perms into PROT perms */
+ if (vma->vm_flags & VM_READ)
+ prot |= PROT_READ;
+ if (vma->vm_flags & VM_WRITE)
+ prot |= PROT_WRITE;
+ if (vma->vm_flags & VM_EXEC)
+ prot |= PROT_EXEC;
+
+ return selinux_mmap_file_common(backing_file->f_cred, backing_file,
+ prot, vma->vm_flags & VM_SHARED);
}
static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long reqprot __always_unused,
unsigned long prot)
{
+ int rc;
const struct cred *cred = current_cred();
u32 sid = cred_sid(cred);
+ const struct file *file = vma->vm_file;
+ bool backing_file;
+ bool shared = vma->vm_flags & VM_SHARED;
+
+ /* check if we need to trigger the "backing files are awful" mode */
+ backing_file = file && (file->f_mode & FMODE_BACKING);
if (default_noexec &&
(prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
- int rc = 0;
/*
* We don't use the vma_is_initial_heap() helper as it has
* a history of problems and is currently broken on systems
@@ -4010,11 +4101,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
vma->vm_end <= vma->vm_mm->brk) {
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECHEAP, NULL);
- } else if (!vma->vm_file && (vma_is_initial_stack(vma) ||
+ if (rc)
+ return rc;
+ } else if (!file && (vma_is_initial_stack(vma) ||
vma_is_stack_for_current(vma))) {
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECSTACK, NULL);
- } else if (vma->vm_file && vma->anon_vma) {
+ if (rc)
+ return rc;
+ } else if (file && vma->anon_vma) {
/*
* We are making executable a file mapping that has
* had some COW done. Since pages might have been
@@ -4022,13 +4117,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
* modified content. This typically should only
* occur for text relocations.
*/
- rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
+ rc = __file_has_perm(cred, file, FILE__EXECMOD,
+ backing_file);
+ if (rc)
+ return rc;
+ if (backing_file) {
+ rc = file_has_perm(file->f_cred, file,
+ FILE__EXECMOD);
+ if (rc)
+ return rc;
+ }
}
+ }
+
+ rc = __file_map_prot_check(cred, file, prot, shared, backing_file);
+ if (rc)
+ return rc;
+ if (backing_file) {
+ rc = file_map_prot_check(file->f_cred, file, prot, shared);
if (rc)
return rc;
}
- return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
+ return 0;
}
static int selinux_file_lock(struct file *file, unsigned int cmd)
@@ -7140,6 +7251,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
.lbs_cred = sizeof(struct cred_security_struct),
.lbs_task = sizeof(struct task_security_struct),
.lbs_file = sizeof(struct file_security_struct),
+ .lbs_backing_file = sizeof(struct backing_file_security_struct),
.lbs_inode = sizeof(struct inode_security_struct),
.lbs_ipc = sizeof(struct ipc_security_struct),
.lbs_key = sizeof(struct key_security_struct),
@@ -7363,9 +7475,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_permission, selinux_file_permission),
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
+ LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
+ LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file),
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
LSM_HOOK_INIT(file_lock, selinux_file_lock),
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 816fde5a5896..fcb46793898f 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -86,6 +86,10 @@ struct file_security_struct {
u32 pseqno; /* Policy seqno at the time of file open */
};
+struct backing_file_security_struct {
+ u32 uf_sid; /* associated user file fsec->sid */
+};
+
struct superblock_security_struct {
u32 sid; /* SID of file system superblock */
u32 def_sid; /* default SID for labeling */
@@ -190,6 +194,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file)
return file->f_security + selinux_blob_sizes.lbs_file;
}
+static inline struct backing_file_security_struct *
+selinux_backing_file(const struct file *backing_file)
+{
+ void *blob = backing_file_security(backing_file);
+ return blob + selinux_blob_sizes.lbs_backing_file;
+}
+
static inline struct inode_security_struct *
selinux_inode(const struct inode *inode)
{
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH v2 stable/linux-6.18.y 1/2] lsm: add backing_file LSM hooks
From: Cai Xinchen @ 2026-06-26 2:40 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, bboscaccy, caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, lujialin4
In-Reply-To: <20260626024058.3149217-1-caixinchen1@huawei.com>
From: Paul Moore <paul@paul-moore.com>
[ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ]
Stacked filesystems such as overlayfs do not currently provide the
necessary mechanisms for LSMs to properly enforce access controls on the
mmap() and mprotect() operations. In order to resolve this gap, a LSM
security blob is being added to the backing_file struct and the following
new LSM hooks are being created:
security_backing_file_alloc()
security_backing_file_free()
security_mmap_backing_file()
The first two hooks are to manage the lifecycle of the LSM security blob
in the backing_file struct, while the third provides a new mmap() access
control point for the underlying backing file. It is also expected that
LSMs will likely want to update their security_file_mprotect() callback
to address issues with their mprotect() controls, but that does not
require a change to the security_file_mprotect() LSM hook.
There are a three other small changes to support these new LSM hooks:
* Pass the user file associated with a backing file down to
alloc_empty_backing_file() so it can be included in the
security_backing_file_alloc() hook.
* Add getter and setter functions for the backing_file struct LSM blob
as the backing_file struct remains private to fs/file_table.c.
* Constify the file struct field in the LSM common_audit_data struct to
better support LSMs that need to pass a const file struct pointer into
the common LSM audit code.
Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL()
and supplying a fixup.
Cc: stable@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-unionfs@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Paul Moore <paul@paul-moore.com>
[Mainline declares lsm_backing_file_cache in security/lsm.h. Linux 6.18.y
does not have security/lsm_init.c or security/lsm.h; the cache variable
is defined locally as static struct kmem_cache *lsm_backing_file_cache in
security/security.c.]
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
fs/backing-file.c | 17 ++++--
fs/file_table.c | 27 +++++++--
fs/fuse/passthrough.c | 2 +-
fs/internal.h | 3 +-
fs/overlayfs/dir.c | 2 +-
fs/overlayfs/file.c | 2 +-
include/linux/backing-file.h | 4 +-
include/linux/fs.h | 13 ++++
include/linux/lsm_audit.h | 2 +-
include/linux/lsm_hook_defs.h | 5 ++
include/linux/lsm_hooks.h | 1 +
include/linux/security.h | 22 +++++++
security/security.c | 109 ++++++++++++++++++++++++++++++++++
13 files changed, 194 insertions(+), 15 deletions(-)
diff --git a/fs/backing-file.c b/fs/backing-file.c
index 15a7f8031084..e049a627d78f 100644
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@ -12,6 +12,7 @@
#include <linux/backing-file.h>
#include <linux/splice.h>
#include <linux/mm.h>
+#include <linux/security.h>
#include "internal.h"
@@ -29,14 +30,15 @@
* returned file into a container structure that also stores the stacked
* file's path, which can be retrieved using backing_file_user_path().
*/
-struct file *backing_file_open(const struct path *user_path, int flags,
+struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred)
{
+ const struct path *user_path = &user_file->f_path;
struct file *f;
int error;
- f = alloc_empty_backing_file(flags, cred);
+ f = alloc_empty_backing_file(flags, cred, user_file);
if (IS_ERR(f))
return f;
@@ -52,15 +54,16 @@ struct file *backing_file_open(const struct path *user_path, int flags,
}
EXPORT_SYMBOL_GPL(backing_file_open);
-struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+struct file *backing_tmpfile_open(const struct file *user_file, int flags,
const struct path *real_parentpath,
umode_t mode, const struct cred *cred)
{
struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt);
+ const struct path *user_path = &user_file->f_path;
struct file *f;
int error;
- f = alloc_empty_backing_file(flags, cred);
+ f = alloc_empty_backing_file(flags, cred, user_file);
if (IS_ERR(f))
return f;
@@ -339,6 +342,12 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
vma_set_file(vma, file);
old_cred = override_creds(ctx->cred);
+ ret = security_mmap_backing_file(vma, file, user_file);
+ if (ret) {
+ revert_creds(old_cred);
+ return ret;
+ }
+
ret = vfs_mmap(vma->vm_file, vma);
revert_creds(old_cred);
diff --git a/fs/file_table.c b/fs/file_table.c
index 762f03dcbcd7..987e01da9938 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -50,6 +50,9 @@ struct backing_file {
struct path user_path;
freeptr_t bf_freeptr;
};
+#ifdef CONFIG_SECURITY
+ void *security;
+#endif
};
#define backing_file(f) container_of(f, struct backing_file, file)
@@ -66,8 +69,21 @@ void backing_file_set_user_path(struct file *f, const struct path *path)
}
EXPORT_SYMBOL_GPL(backing_file_set_user_path);
+#ifdef CONFIG_SECURITY
+void *backing_file_security(const struct file *f)
+{
+ return backing_file(f)->security;
+}
+
+void backing_file_set_security(struct file *f, void *security)
+{
+ backing_file(f)->security = security;
+}
+#endif /* CONFIG_SECURITY */
+
static inline void backing_file_free(struct backing_file *ff)
{
+ security_backing_file_free(&ff->file);
path_put(&ff->user_path);
kmem_cache_free(bfilp_cachep, ff);
}
@@ -288,10 +304,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
return f;
}
-static int init_backing_file(struct backing_file *ff)
+static int init_backing_file(struct backing_file *ff,
+ const struct file *user_file)
{
memset(&ff->user_path, 0, sizeof(ff->user_path));
- return 0;
+ backing_file_set_security(&ff->file, NULL);
+ return security_backing_file_alloc(&ff->file, user_file);
}
/*
@@ -301,7 +319,8 @@ static int init_backing_file(struct backing_file *ff)
* This is only for kernel internal use, and the allocate file must not be
* installed into file tables or such.
*/
-struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
+ const struct file *user_file)
{
struct backing_file *ff;
int error;
@@ -318,7 +337,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
/* The f_mode flags must be set before fput(). */
ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
- error = init_backing_file(ff);
+ error = init_backing_file(ff, user_file);
if (unlikely(error)) {
fput(&ff->file);
return ERR_PTR(error);
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index 72de97c03d0e..f2d08ac2459b 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -167,7 +167,7 @@ struct fuse_backing *fuse_passthrough_open(struct file *file, int backing_id)
goto out;
/* Allocate backing file per fuse file to store fuse path */
- backing_file = backing_file_open(&file->f_path, file->f_flags,
+ backing_file = backing_file_open(file, file->f_flags,
&fb->file->f_path, fb->cred);
err = PTR_ERR(backing_file);
if (IS_ERR(backing_file)) {
diff --git a/fs/internal.h b/fs/internal.h
index 9b2b4d116880..51107fd51514 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -100,7 +100,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
*/
struct file *alloc_empty_file(int flags, const struct cred *cred);
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
-struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
+ const struct file *user_file);
void backing_file_set_user_path(struct file *f, const struct path *path);
static inline void file_put_write_access(struct file *file)
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index a5e9ddf3023b..e924321b6402 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -1355,7 +1355,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
}
ovl_path_upper(dentry->d_parent, &realparentpath);
- realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
+ realfile = backing_tmpfile_open(file, flags, &realparentpath,
mode, current_cred());
err = PTR_ERR_OR_ZERO(realfile);
pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 7ab2c9daffd0..3fedfdddfa75 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -48,7 +48,7 @@ static struct file *ovl_open_realfile(const struct file *file,
if (!inode_owner_or_capable(real_idmap, realinode))
flags &= ~O_NOATIME;
- realfile = backing_file_open(file_user_path(file),
+ realfile = backing_file_open(file,
flags, realpath, current_cred());
}
ovl_revert_creds(old_cred);
diff --git a/include/linux/backing-file.h b/include/linux/backing-file.h
index 1476a6ed1bfd..c939cd222730 100644
--- a/include/linux/backing-file.h
+++ b/include/linux/backing-file.h
@@ -18,10 +18,10 @@ struct backing_file_ctx {
void (*end_write)(struct kiocb *iocb, ssize_t);
};
-struct file *backing_file_open(const struct path *user_path, int flags,
+struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred);
-struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+struct file *backing_tmpfile_open(const struct file *user_file, int flags,
const struct path *real_parentpath,
umode_t mode, const struct cred *cred);
ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 014cb04eefbe..f3e798184a58 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2890,6 +2890,19 @@ struct file *dentry_create(const struct path *path, int flags, umode_t mode,
const struct cred *cred);
const struct path *backing_file_user_path(const struct file *f);
+#ifdef CONFIG_SECURITY
+void *backing_file_security(const struct file *f);
+void backing_file_set_security(struct file *f, void *security);
+#else
+static inline void *backing_file_security(const struct file *f)
+{
+ return NULL;
+}
+static inline void backing_file_set_security(struct file *f, void *security)
+{
+}
+#endif /* CONFIG_SECURITY */
+
/*
* When mmapping a file on a stackable filesystem (e.g., overlayfs), the file
* stored in ->vm_file is a backing file whose f_inode is on the underlying
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 382c56a97bba..584db296e43b 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -94,7 +94,7 @@ struct common_audit_data {
#endif
char *kmod_name;
struct lsm_ioctlop_audit *op;
- struct file *file;
+ const struct file *file;
struct lsm_ibpkey_audit *ibpkey;
struct lsm_ibendport_audit *ibendport;
int reason;
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 8c42b4bde09c..b4958167e381 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -191,6 +191,9 @@ LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
LSM_HOOK(int, 0, file_alloc_security, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
+LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file,
+ const struct file *user_file)
+LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file)
LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
unsigned long arg)
LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
@@ -198,6 +201,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
+LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma,
+ struct file *backing_file, struct file *user_file)
LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot)
LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 79ec5a2bdcca..ea4b0f5ca7f0 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -104,6 +104,7 @@ struct security_hook_list {
struct lsm_blob_sizes {
int lbs_cred;
int lbs_file;
+ int lbs_backing_file;
int lbs_ib;
int lbs_inode;
int lbs_sock;
diff --git a/include/linux/security.h b/include/linux/security.h
index b64598e5d65d..e54025362426 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -473,11 +473,17 @@ int security_file_permission(struct file *file, int mask);
int security_file_alloc(struct file *file);
void security_file_release(struct file *file);
void security_file_free(struct file *file);
+int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file);
+void security_backing_file_free(struct file *backing_file);
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int security_file_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg);
int security_mmap_file(struct file *file, unsigned long prot,
unsigned long flags);
+int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file);
int security_mmap_addr(unsigned long addr);
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot);
@@ -1142,6 +1148,15 @@ static inline void security_file_release(struct file *file)
static inline void security_file_free(struct file *file)
{ }
+static inline int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ return 0;
+}
+
+static inline void security_backing_file_free(struct file *backing_file)
+{ }
+
static inline int security_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -1161,6 +1176,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot,
return 0;
}
+static inline int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file)
+{
+ return 0;
+}
+
static inline int security_mmap_addr(unsigned long addr)
{
return cap_mmap_addr(addr);
diff --git a/security/security.c b/security/security.c
index 603c3c6d5635..9285909908ab 100644
--- a/security/security.c
+++ b/security/security.c
@@ -94,6 +94,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
static struct kmem_cache *lsm_file_cache;
+static struct kmem_cache *lsm_backing_file_cache;
static struct kmem_cache *lsm_inode_cache;
char *lsm_names;
@@ -265,6 +266,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
+ lsm_set_blob_size(&needed->lbs_backing_file, &blob_sizes.lbs_backing_file);
lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
/*
* The inode blob gets an rcu_head in addition to
@@ -470,6 +472,7 @@ static void __init ordered_lsm_init(void)
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
+ init_debug("lsm_backing_file_cache = %d\n", blob_sizes.lbs_backing_file);
init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
@@ -495,6 +498,11 @@ static void __init ordered_lsm_init(void)
lsm_file_cache = kmem_cache_create("lsm_file_cache",
blob_sizes.lbs_file, 0,
SLAB_PANIC, NULL);
+ if (blob_sizes.lbs_backing_file)
+ lsm_backing_file_cache = kmem_cache_create(
+ "lsm_backing_file_cache",
+ blob_sizes.lbs_backing_file,
+ 0, SLAB_PANIC, NULL);
if (blob_sizes.lbs_inode)
lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
blob_sizes.lbs_inode, 0,
@@ -671,6 +679,30 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb)
}
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
+/**
+ * lsm_backing_file_alloc - allocate a composite backing file blob
+ * @backing_file: the backing file
+ *
+ * Allocate the backing file blob for all the modules.
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_backing_file_alloc(struct file *backing_file)
+{
+ void *blob;
+
+ if (!lsm_backing_file_cache) {
+ backing_file_set_security(backing_file, NULL);
+ return 0;
+ }
+
+ blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL);
+ backing_file_set_security(backing_file, blob);
+ if (!blob)
+ return -ENOMEM;
+ return 0;
+}
+
/**
* lsm_blob_alloc - allocate a composite blob
* @dest: the destination for the blob
@@ -2965,6 +2997,57 @@ void security_file_free(struct file *file)
}
}
+/**
+ * security_backing_file_alloc() - Allocate and setup a backing file blob
+ * @backing_file: the backing file
+ * @user_file: the associated user visible file
+ *
+ * Allocate a backing file LSM blob and perform any necessary initialization of
+ * the LSM blob. There will be some operations where the LSM will not have
+ * access to @user_file after this point, so any important state associated
+ * with @user_file that is important to the LSM should be captured in the
+ * backing file's LSM blob.
+ *
+ * LSM's should avoid taking a reference to @user_file in this hook as it will
+ * result in problems later when the system attempts to drop/put the file
+ * references due to a circular dependency.
+ *
+ * Return: Return 0 if the hook is successful, negative values otherwise.
+ */
+int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ int rc;
+
+ rc = lsm_backing_file_alloc(backing_file);
+ if (rc)
+ return rc;
+ rc = call_int_hook(backing_file_alloc, backing_file, user_file);
+ if (unlikely(rc))
+ security_backing_file_free(backing_file);
+
+ return rc;
+}
+
+/**
+ * security_backing_file_free() - Free a backing file blob
+ * @backing_file: the backing file
+ *
+ * Free any LSM state associate with a backing file's LSM blob, including the
+ * blob itself.
+ */
+void security_backing_file_free(struct file *backing_file)
+{
+ void *blob = backing_file_security(backing_file);
+
+ call_void_hook(backing_file_free, backing_file);
+
+ if (blob) {
+ backing_file_set_security(backing_file, NULL);
+ kmem_cache_free(lsm_backing_file_cache, blob);
+ }
+}
+
/**
* security_file_ioctl() - Check if an ioctl is allowed
* @file: associated file
@@ -3053,6 +3136,32 @@ int security_mmap_file(struct file *file, unsigned long prot,
flags);
}
+/**
+ * security_mmap_backing_file - Check if mmap'ing a backing file is allowed
+ * @vma: the vm_area_struct for the mmap'd region
+ * @backing_file: the backing file being mmap'd
+ * @user_file: the user file being mmap'd
+ *
+ * Check permissions for a mmap operation on a stacked filesystem. This hook
+ * is called after the security_mmap_file() and is responsible for authorizing
+ * the mmap on @backing_file. It is important to note that the mmap operation
+ * on @user_file has already been authorized and the @vma->vm_file has been
+ * set to @backing_file.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file)
+{
+ /* recommended by the stackable filesystem devs */
+ if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING)))
+ return -EIO;
+
+ return call_int_hook(mmap_backing_file, vma, backing_file, user_file);
+}
+EXPORT_SYMBOL_GPL(security_mmap_backing_file);
+
/**
* security_mmap_addr() - Check if mmap'ing an address is allowed
* @addr: address
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH v2 stable/linux-6.18.y 0/2] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Cai Xinchen @ 2026-06-26 2:40 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, bboscaccy, caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, lujialin4
v2: Add static to struct kmem_cache *lsm_backing_file_cache; and define
lbs_backing_file as int for keeping the same type as 6.18.
Backport the patch series
"Fix incorrect overlayfs mmap() and mprotect() LSM access controls" [1]
to 6.18 lts
I test selinux-testsuite[2] overlay test, it pass 135 tests.
[1] https://lore.kernel.org/all/20260403030848.731867-5-paul@paul-moore.com/
[2] https://github.com/SELinuxProject/selinux-testsuite
Paul Moore (2):
lsm: add backing_file LSM hooks
selinux: fix overlayfs mmap() and mprotect() access checks
fs/backing-file.c | 17 ++-
fs/file_table.c | 27 +++-
fs/fuse/passthrough.c | 2 +-
fs/internal.h | 3 +-
fs/overlayfs/dir.c | 2 +-
fs/overlayfs/file.c | 2 +-
include/linux/backing-file.h | 4 +-
include/linux/fs.h | 13 ++
include/linux/lsm_audit.h | 2 +-
include/linux/lsm_hook_defs.h | 5 +
include/linux/lsm_hooks.h | 1 +
include/linux/security.h | 22 +++
security/security.c | 109 ++++++++++++++
security/selinux/hooks.c | 242 ++++++++++++++++++++++--------
security/selinux/include/objsec.h | 11 ++
15 files changed, 383 insertions(+), 79 deletions(-)
--
2.18.0.huawei.25
^ permalink raw reply
* Re: [PATCH bpf-next v2 1/5] bpf: Verify signed loader metadata at load time
From: Paul Moore @ 2026-06-26 2:01 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Daniel Borkmann, Alexei Starovoitov, KP Singh, James Bottomley,
Blaise Boscaccy, Kumar Kartikeya Dwivedi, Linus Torvalds, bpf,
LSM List
In-Reply-To: <CAADnVQLdDZR+q66XgCgprPnikhE_Zf_LH3XTFh43u_ks62cWRg@mail.gmail.com>
On June 25, 2026 9:44:54 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Thu, Jun 25, 2026 at 6:38 PM Paul Moore <paul@paul-moore.com> wrote:
>>
>> On Thu, Jun 25, 2026 at 9:16 PM Alexei Starovoitov
>> <alexei.starovoitov@gmail.com> wrote:
>>> On Thu Jun 25, 2026 at 5:59 PM PDT, Paul Moore wrote:
>>>>
>>>> For all the reasons I gave previously, I can't support moving the
>>>> existing security_bpf_prog_load() hook at this point in time.
>>>
>>> Paul,
>>> it's not up to you to approve or deny where security_bpf_prog_load()
>>> is called within bpf subsystem as long as it doesn't affect behavior.
>>> Daniel's patch doesn't change observable state from LSMs pov.
>>> It merely moves the call from syscall.c to verifier.c.
>>
>> Alexei,
>> It is my responsibility to speak up and voice my opinion about LSM
>> hook placement; arguably that is one of the LSM maintainer's larger
>> responsibilities. Non-trivial work, including several allocations
>> (which can be quite large in some cases), occurs between the current
>> placement of security_bpf_prog_load() and Daniel's proposed location.
>> We must preserve the existing security_bpf_prog_load() call site.
>
> I don't think you read the patch because you're saying nonsense.
I've read the patch, as well as the code between the existing and proposed
call sites that is outside the patch's context, that is the basis of my
comment.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH bpf-next v2 1/5] bpf: Verify signed loader metadata at load time
From: Alexei Starovoitov @ 2026-06-26 1:44 UTC (permalink / raw)
To: Paul Moore
Cc: Daniel Borkmann, Alexei Starovoitov, KP Singh, James Bottomley,
Blaise Boscaccy, Kumar Kartikeya Dwivedi, Linus Torvalds, bpf,
LSM List
In-Reply-To: <CAHC9VhQsjW0OJhKYUNEdkt_31oo56aMusDTANDOAQFNQwCpo4A@mail.gmail.com>
On Thu, Jun 25, 2026 at 6:38 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Thu, Jun 25, 2026 at 9:16 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> > On Thu Jun 25, 2026 at 5:59 PM PDT, Paul Moore wrote:
> > >
> > > For all the reasons I gave previously, I can't support moving the
> > > existing security_bpf_prog_load() hook at this point in time.
> >
> > Paul,
> > it's not up to you to approve or deny where security_bpf_prog_load()
> > is called within bpf subsystem as long as it doesn't affect behavior.
> > Daniel's patch doesn't change observable state from LSMs pov.
> > It merely moves the call from syscall.c to verifier.c.
>
> Alexei,
> It is my responsibility to speak up and voice my opinion about LSM
> hook placement; arguably that is one of the LSM maintainer's larger
> responsibilities. Non-trivial work, including several allocations
> (which can be quite large in some cases), occurs between the current
> placement of security_bpf_prog_load() and Daniel's proposed location.
> We must preserve the existing security_bpf_prog_load() call site.
I don't think you read the patch because you're saying nonsense.
^ permalink raw reply
* Re: [PATCH bpf-next v2 1/5] bpf: Verify signed loader metadata at load time
From: Paul Moore @ 2026-06-26 1:38 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Daniel Borkmann, ast, kpsingh, James.Bottomley, bboscaccy, memxor,
torvalds, bpf, linux-security-module
In-Reply-To: <DJIL18C2F40B.39U9WHD43SDBR@gmail.com>
On Thu, Jun 25, 2026 at 9:16 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Thu Jun 25, 2026 at 5:59 PM PDT, Paul Moore wrote:
> >
> > For all the reasons I gave previously, I can't support moving the
> > existing security_bpf_prog_load() hook at this point in time.
>
> Paul,
> it's not up to you to approve or deny where security_bpf_prog_load()
> is called within bpf subsystem as long as it doesn't affect behavior.
> Daniel's patch doesn't change observable state from LSMs pov.
> It merely moves the call from syscall.c to verifier.c.
Alexei,
It is my responsibility to speak up and voice my opinion about LSM
hook placement; arguably that is one of the LSM maintainer's larger
responsibilities. Non-trivial work, including several allocations
(which can be quite large in some cases), occurs between the current
placement of security_bpf_prog_load() and Daniel's proposed location.
We must preserve the existing security_bpf_prog_load() call site.
> So we're going to proceed.
Oh goodie, will the fun ever stop?
--
paul-moore.com
^ permalink raw reply
* [PATCH -next 2/2] security: Fix call security_backing_file_free second time
From: Cai Xinchen @ 2026-06-26 1:17 UTC (permalink / raw)
To: paul, jmorris, serge, stephen.smalley.work, omosnace, amir73il,
brauner
Cc: linux-security-module, linux-kernel, selinux, caixinchen1,
lujialin4
In-Reply-To: <20260626011720.1144213-1-caixinchen1@huawei.com>
I found the following path:
alloc_empty_backing-file
init_file(&ff->file, xxx)
-> file_ref_init(&f->f_ref, 1); // only 1
error = init_backing_file
-> security_backing_file_alloc
-> rc = call_int_hook(backing_file_alloc, ...)
if (unlikely(rc))
security_backing_file_free(backing_file); // first call
if (unlikely(error)) {
fput(&ff->file);
-> if (unlikely(file_ref_put(&file->f_ref))) // zero
__fput_deferred(file);
-> ____fput -> __fput -> file_free(file);
-> backing_file_free(backing_file(f));
-> security_backing_file_free(&ff->file); // second call
Currently, only SELinux has the lsm backing_file_alloc hook, and the
backing_file_free hook is not set. When security_backing_file_free is
called for the first time, the blobs pointer is set to NULL. Therefore,
double free will not occur in the code.
Fixes: 6af36aeb147a ("lsm: add backing_file LSM hooks")
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
security/security.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/security/security.c b/security/security.c
index 71aea8fdf014..595d3c73253e 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2468,11 +2468,8 @@ int security_backing_file_alloc(struct file *backing_file,
rc = lsm_backing_file_alloc(backing_file);
if (rc)
return rc;
- rc = call_int_hook(backing_file_alloc, backing_file, user_file);
- if (unlikely(rc))
- security_backing_file_free(backing_file);
- return rc;
+ return call_int_hook(backing_file_alloc, backing_file, user_file);
}
/**
--
2.34.1
^ permalink raw reply related
* [PATCH -next 1/2] security: Some cleanup code
From: Cai Xinchen @ 2026-06-26 1:17 UTC (permalink / raw)
To: paul, jmorris, serge, stephen.smalley.work, omosnace, amir73il,
brauner
Cc: linux-security-module, linux-kernel, selinux, caixinchen1,
lujialin4
In-Reply-To: <20260626011720.1144213-1-caixinchen1@huawei.com>
Delete an unnecessary blank line and a blobs variable with
duplicate assignment.
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
security/lsm_init.c | 1 -
security/selinux/hooks.c | 1 -
2 files changed, 2 deletions(-)
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 7c0fd17f1601..d7384866e3a5 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -290,7 +290,6 @@ static void __init lsm_prepare(struct lsm_info *lsm)
return;
/* Register the LSM blob sizes. */
- blobs = lsm->blobs;
lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred);
lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file);
lsm_blob_size_update(&blobs->lbs_backing_file,
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1a713d96206f..e5930ebc9e37 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1748,7 +1748,6 @@ static int bpf_fd_pass(const struct file *file, u32 sid);
static int __file_has_perm(const struct cred *cred, const struct file *file,
u32 av, bool bf_user_file)
-
{
struct common_audit_data ad;
struct inode *inode;
--
2.34.1
^ permalink raw reply related
* [PATCH -next 0/2] Fix call security_backing_file_free second time
From: Cai Xinchen @ 2026-06-26 1:17 UTC (permalink / raw)
To: paul, jmorris, serge, stephen.smalley.work, omosnace, amir73il,
brauner
Cc: linux-security-module, linux-kernel, selinux, caixinchen1,
lujialin4
I found the following path:
alloc_empty_backing-file
init_file(&ff->file, xxx)
-> file_ref_init(&f->f_ref, 1); // only 1
error = init_backing_file
-> security_backing_file_alloc
-> rc = call_int_hook(backing_file_alloc, ...)
if (unlikely(rc))
security_backing_file_free(backing_file); // first call
if (unlikely(error)) {
fput(&ff->file);
-> if (unlikely(file_ref_put(&file->f_ref))) // zero
__fput_deferred(file);
-> ____fput -> __fput -> file_free(file);
-> backing_file_free(backing_file(f));
-> security_backing_file_free(&ff->file); // second call
Cai Xinchen (2):
security: Some cleanup code
security: Fix call security_backing_file_free second time
security/lsm_init.c | 1 -
security/security.c | 5 +----
security/selinux/hooks.c | 1 -
3 files changed, 1 insertion(+), 6 deletions(-)
--
2.34.1
^ permalink raw reply
* Re: [PATCH bpf-next v2 1/5] bpf: Verify signed loader metadata at load time
From: Alexei Starovoitov @ 2026-06-26 1:16 UTC (permalink / raw)
To: Paul Moore, Daniel Borkmann
Cc: ast, kpsingh, James.Bottomley, bboscaccy, memxor, torvalds, bpf,
linux-security-module
In-Reply-To: <CAHC9VhRV9jW+dwNf7mW+1zTCsZ1xstBAugWL-TJ-DVNARdzC=Q@mail.gmail.com>
On Thu Jun 25, 2026 at 5:59 PM PDT, Paul Moore wrote:
>
> For all the reasons I gave previously, I can't support moving the
> existing security_bpf_prog_load() hook at this point in time.
Paul,
it's not up to you to approve or deny where security_bpf_prog_load()
is called within bpf subsystem as long as it doesn't affect behavior.
Daniel's patch doesn't change observable state from LSMs pov.
It merely moves the call from syscall.c to verifier.c.
So we're going to proceed.
> I'm guessing you still haven't looked at Blaise's patchset from last
> September.
Blaise approach was Nacked because you guys ignored TOCTOU issue.
I pointed it a year ago before AI was a thing. Then sashiko
pointed it again and the bot explained it in detail. It was again
ignored.
Daniel's v1 sadly had the same issue and sashiko spotted it too.
Hence v2 is moving the location of security_bpf_prog_load().
> on-list. As you can see from the lore archives, he has vehemently
> opposed the approach you are proposing for quite a while.
Exactly, because you kept ignoring TOCTOU issue.
Claiming support for signed bpf that can be easily defeated
is a shameless security scam.
^ permalink raw reply
* Re: [PATCH bpf-next v2 1/5] bpf: Verify signed loader metadata at load time
From: Paul Moore @ 2026-06-26 0:59 UTC (permalink / raw)
To: Daniel Borkmann
Cc: ast, kpsingh, James.Bottomley, bboscaccy, memxor, torvalds, bpf,
linux-security-module
In-Reply-To: <c4dc3117-ad84-4c94-952a-cf0642cb42e7@iogearbox.net>
On Thu, Jun 25, 2026 at 4:37 PM Daniel Borkmann <daniel@iogearbox.net> wrote:
> On 6/24/26 8:42 PM, Paul Moore wrote:
> > On Wed, Jun 24, 2026 at 11:37 AM Daniel Borkmann <daniel@iogearbox.net> wrote:
> >> On 6/24/26 5:12 PM, Paul Moore wrote:
> >>> On Wed, Jun 24, 2026 at 10:03 AM Daniel Borkmann <daniel@iogearbox.net> wrote:
> >> [...]
> >>>> include/linux/bpf_verifier.h | 1 +
> >>>> kernel/bpf/syscall.c | 76 +---------------
> >>>> kernel/bpf/verifier.c | 163 ++++++++++++++++++++++++++++++++++-
> >>>> 3 files changed, 165 insertions(+), 75 deletions(-)
> >>>
> >>> ...
> >>>
> >>>> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> >>>> index b44106c8ea75..026b61d78bdb 100644
> >>>> --- a/kernel/bpf/syscall.c
> >>>> +++ b/kernel/bpf/syscall.c
> >>>> @@ -3189,10 +3121,6 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
> >>>> if (err < 0)
> >>>> goto free_prog;
> >>>>
> >>>> - err = security_bpf_prog_load(prog, attr, token, uattr.is_kernel);
> >>>> - if (err)
> >>>> - goto free_prog;
> >>>> -
> >>>> /* run eBPF verifier */
> >>>> err = bpf_check(&prog, attr, uattr, attr_log);
> >>>> if (err < 0)
> >>>
> >>> We must preserve the existing location of the call into the
> >>> security_bpf_prog_load() hook as some users rely on this hook being
> >>> called *before* the verifier runs.
> >>
> >> Keep in mind that the verifier /at this point/ of the new location did
> >> _not_ verify anything. So there is no heavy-duty work happening yet at
> >> security_bpf_prog_load. The work that is done before security_bpf_prog_load
> >> is basically setting up the env, initializing the verifier log, and doing
> >> the process_fd_array which is resolving the map/BTF objects. But it did
> >> not walk any instructions etc, so semantics of the security_bpf_prog_load
> >> hook did not change from a user PoV.
> >
> > There is still a reasonable amount of work between the existing and
> > new call sites, and the existing location outside of bpf_check()
> > offers an additional robustness benefit that future verifier changes
> > are less likely to impact the hook. If I'm completely honest, I also
> > need to consider the events of the past year and a half; I'm now much
> > less inclined to support LSM hook changes in the BPF subsystem because
> > I'm very concerned about our ability to revert/modify those changes in
> > the future if needed. That doesn't mean I won't support LSM hook
> > changes in BPF, but such changes are going to need to have a *very*
> > strong advantage from a LSM perspective to offset the risk associated
> > with the current BPF subsystem.
>
> From where you sit with regards to LSMs that is a natural stance towards
> all kernel code, but coming back to the LSM hook, to me this is way too
> excessive that we should add *yet another* LSM hook ...
For all the reasons I gave previously, I can't support moving the
existing security_bpf_prog_load() hook at this point in time.
However, unlike Alexei, I am willing to work with you to develop a new
LSM hook to meet your new needs for enforcing signed BPF program
policies via an LSM (BPF or otherwise). If, as you say, you are not
willing to add a new hook, you will need to find a way to make it work
with the existing hooks/placements.
> > I also have to bring up the same question I asked back in your v1
> > posting: have you discussed this signature approach with Alexei? Your
> > patches abandon and remove KP's signature scheme in favor of what is
> > effectively Blaise's signature scheme from last fall; Alexei argued
> > very strongly against these changes in the past. I'd hate to spend a
> > lot more time reviewing and discussing patches that Alexei is simply
> > going to NACK once again.
>
> I think last time I already stated that this is not "effectively Blaise's
> signature scheme" for couple of reasons ...
I already responded to these three points in your last patchset. It
was sent to you directly, as well as to all of the relevant lists (it
was a reply-all), but here is a lore link if you haven't read it:
https://lore.kernel.org/linux-security-module/CAHC9VhQQKNvP1Who0DdUc0EsVYd_JoSneyzOHZ=Q0MP2qQndCw@mail.gmail.com/
> From where you started out back then, it was the stance that while the
> original KP approach generically addresses all the use cases for loading
> BPF related to relocations via the lskel loader, Blaise proposed a parallel
> scheme which would only allow static programs (only insns, no maps) ...
I'm guessing you still haven't looked at Blaise's patchset from last
September. You were CC'd on the original posting, and I sent you a
lore link in our discussion of your v1 patchset, but I guess it's easy
to get busy/distracted and lose track of things. Regardless, here is
another link to Blaise's patchset:
https://lore.kernel.org/linux-security-module/20250929213520.1821223-1-bboscaccy@linux.microsoft.com/
Blaise's patchset proposed a scheme which ran the PKCS7 signature over
the lskel loader and maps in a way *very* similar to what you are
proposing. Blaise's patchset also supported the same key selection as
KP's scheme so user and session signatures were supported without
issue.
> ... So I cannot
> directly speak for Alexei/KP, but I think this proposal should satisfy all
> parties under one roof.
Considering the striking similarities between what you are proposing
and what Blaise proposed last September I *strongly* suggest getting a
basic thumbs-up or thumbs-down from Alexei on this new/old approach
on-list. As you can see from the lore archives, he has vehemently
opposed the approach you are proposing for quite a while. If he has
changed his mind to understand the value in Blaise's approach of
running the PKCS7 signature over both the lskel loader and maps that's
great, but I worry that will not be the case.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH v3 1/2] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: Paul Moore @ 2026-06-25 20:47 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: David Windsor, Christian Brauner, Alexander Viro, Jan Kara,
Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, KP Singh, Matt Bobrowski, James Morris,
Serge E . Hallyn, Mimi Zohar, Roberto Sassu, dmitry.kasatkin,
eric.snowberg, Stephen Smalley, Ondrej Mosnacek, Casey Schaufler,
Shuah Khan, LKML, Linux-Fsdevel, bpf, LSM List, linux-integrity,
selinux, open list:KERNEL SELFTEST FRAMEWORK
In-Reply-To: <CAADnVQJM15E0PwomdTiz8zvVMGkqs9mTSjYRdPBF6fgE=tsPCQ@mail.gmail.com>
On Thu, Jun 25, 2026 at 4:44 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Thu, Jun 25, 2026 at 1:40 PM Paul Moore <paul@paul-moore.com> wrote:
> > On Thu, Jun 25, 2026 at 3:58 PM Alexei Starovoitov
> > <alexei.starovoitov@gmail.com> wrote:
> > > On Thu, Jun 25, 2026 at 7:23 AM Christian Brauner <brauner@kernel.org> wrote:
> > > > On 2026-06-23 20:12:32-04:00, Paul Moore wrote:
> > > > > On Jun 18, 2026 David Windsor <dwindsor@gmail.com> wrote:
> > > > >
> > > > > > Add bpf_init_inode_xattr() kfunc for BPF LSM programs to atomically set
> > > > > > xattrs via the inode_init_security hook using lsm_get_xattr_slot().
> > > > > >
> > > > > > The inode_init_security hook previously took the xattr array and count
> > > > > > as two separate output parameters (struct xattr *xattrs, int
> > > > > > *xattr_count), which BPF programs cannot write to. Pass the xattr state
> > > > > > as a single context object (struct xattr_ctx) instead, and have
> > > > > > bpf_init_inode_xattr() take that context directly. Update the existing
> > > > > > in-tree callers of inode_init_security to take and forward the new
> > > > > > xattr_ctx.
> > > > > >
> > > > > > A previous attempt [1] required a kmalloc string output protocol for
> > > > > > the xattr name. Since commit 6bcdfd2cac55 ("security: Allow all LSMs to
> > > > > > provide xattrs for inode_init_security hook") [2], the xattr name is no
> > > > > > longer allocated; it is a static constant.
> > > > > >
> > > > > > Because we rely on the hook-specific ctx layout, the kfunc is
> > > > > > restricted to lsm/inode_init_security. Restrict the xattr names that
> > > > > > may be set via this kfunc to the bpf.* namespace.
> > > > > >
> > > > > > Link: https://kernsec.org/pipermail/linux-security-module-archive/2022-October/034878.html [1]
> > > > > > Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6bcdfd2cac55 [2]
> > > > > > Suggested-by: Song Liu <song@kernel.org>
> > > > > > Signed-off-by: David Windsor <dwindsor@gmail.com>
> > > > > > ---
> > > > > > fs/bpf_fs_kfuncs.c | 106 +++++++++++++++++++++++++++++-
> > > > > > include/linux/bpf.h | 1 +
> > > > > > include/linux/bpf_lsm.h | 3 +
> > > > > > include/linux/evm.h | 9 +--
> > > > > > include/linux/lsm_hook_defs.h | 4 +-
> > > > > > include/linux/lsm_hooks.h | 16 ++---
> > > > > > include/linux/security.h | 5 ++
> > > > > > kernel/bpf/bpf_lsm.c | 10 +++
> > > > > > kernel/bpf/trampoline.c | 3 +
> > > > > > security/bpf/hooks.c | 1 +
> > > > > > security/integrity/evm/evm_main.c | 8 ++-
> > > > > > security/security.c | 7 +-
> > > > > > security/selinux/hooks.c | 4 +-
> > > > > > security/smack/smack_lsm.c | 27 ++++----
> > > > > > 14 files changed, 166 insertions(+), 38 deletions(-)
> > > > >
> > > > > I have a few specific comments below, inline with the patch, but I wanted
> > > > > to make some general comments too.
> > > > >
> > > > > The kfunc additions really don't belong in the VFS kfunc file, please
> > > > > create a LSM kfunc file (call it security/bpf_lsm_kfuncs.c) and add the
> > > > > kfunc code to this new file.
> > > >
> > > > We expose a bunch of VFS heavy operations for various security modules
> > > > and this is really not different. For xattrs we have it all centralized
> > > > in the VFS and in general all VFS related bpf kfuncs should continue
> > > > living there and be registered there. Anything that's just bpf infra
> > > > specific can go to security/bpf/kfuncs.c instead. But anyway, it's a bpf
> > > > specific helper so it's the bpf maintainer's call.
> > >
> > > Completely agree. This is vfs related kfunc and has to be
> > > in fs/bpf_fs_kfuncs.c to make sure vfs maintainers review it now
> > > and all future changes to it.
> >
> > *laughs*
> >
> > Okay, then split out the LSM specific stuff into
> > security/bpf_lsm_kfuncs.c; all the LSM macros/defines/calls should be
> > in the LSM kfuncs file.
>
> Paul,
>
> I'm sorry, but you didn't demonstrate the level of understanding
> of bpf to be trusted to maintain any piece of it.
Alexei,
You haven't demonstrated the understanding or decorum necessary to be
entrusted with any part of the LSM framework.
--
paul-moore.com
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox