From ad7f801c49683a15deee7c3d9dfac32b895cc7bb Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Sun, 28 Nov 2021 04:47:17 +0000 Subject: [PATCH] rust: add abstraction for credentials. We need this in binder for the new security callbacks. Signed-off-by: Wedson Almeida Filho --- rust/helpers.c | 11 +++++++ rust/kernel/cred.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/file.rs | 12 +++++++- rust/kernel/lib.rs | 1 + 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/cred.rs diff --git a/rust/helpers.c b/rust/helpers.c index c9c920b997534c..f3e3fb34d3761e 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -402,6 +402,17 @@ void rust_helper_chained_irq_exit(struct irq_chip *chip, } EXPORT_SYMBOL_GPL(rust_helper_chained_irq_exit); +const struct cred *rust_helper_get_cred(const struct cred *cred) +{ + return get_cred(cred); +} +EXPORT_SYMBOL_GPL(rust_helper_get_cred); + +void rust_helper_put_cred(const struct cred *cred) { + put_cred(cred); +} +EXPORT_SYMBOL_GPL(rust_helper_put_cred); + /* We use bindgen's --size_t-is-usize option to bind the C size_t type * as the Rust usize type, so we can use it in contexts where Rust * expects a usize like slice (array) indices. usize is defined to be diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs new file mode 100644 index 00000000000000..1602aa6935ca7c --- /dev/null +++ b/rust/kernel/cred.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Credentials management. +//! +//! C header: [`include/linux/cred.h`](../../../../include/linux/cred.h) +//! +//! Reference: + +use crate::bindings; +use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref}; + +/// Wraps the kernel's `struct cred`. +/// +/// # Invariants +/// +/// The pointer `Credential::ptr` is non-null and valid. Its reference count is also non-zero. +pub struct Credential { + pub(crate) ptr: *const bindings::cred, +} + +impl Clone for Credential { + fn clone(&self) -> Self { + // SAFETY: The type invariants guarantee that `self.ptr` has a non-zero reference count. + let ptr = unsafe { bindings::get_cred(self.ptr) }; + + // INVARIANT: We incremented the reference count to account for the new `Credential` being + // created. + Self { ptr } + } +} + +impl Drop for Credential { + fn drop(&mut self) { + // SAFETY: The type invariants guarantee that `ptr` has a non-zero reference count. + unsafe { bindings::put_cred(self.ptr) }; + } +} + +/// A wrapper for [`Credential`] that doesn't automatically decrement the refcount when dropped. +/// +/// We need the wrapper because [`ManuallyDrop`] alone would allow callers to call +/// [`ManuallyDrop::into_inner`]. This would allow an unsafe sequence to be triggered without +/// `unsafe` blocks because it would trigger an unbalanced call to `put_cred`. +/// +/// # Invariants +/// +/// The wrapped [`Credential`] remains valid for the lifetime of the object. +pub struct CredentialRef<'a> { + cred: ManuallyDrop, + _p: PhantomData<&'a ()>, +} + +impl CredentialRef<'_> { + /// Constructs a new [`struct cred`] wrapper that doesn't change its reference count. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the object. + pub(crate) unsafe fn from_ptr(ptr: *const bindings::cred) -> Self { + Self { + cred: ManuallyDrop::new(Credential { ptr }), + _p: PhantomData, + } + } +} + +impl Deref for CredentialRef<'_> { + type Target = Credential; + + fn deref(&self) -> &Self::Target { + self.cred.deref() + } +} diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs index 9e205ad34d7f6c..d2c0eefc9f68cb 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -5,7 +5,7 @@ //! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and //! [`include/linux/file.h`](../../../../include/linux/file.h) -use crate::{bindings, error::Error, Result}; +use crate::{bindings, cred::CredentialRef, error::Error, Result}; use core::{mem::ManuallyDrop, ops::Deref}; /// Wraps the kernel's `struct file`. @@ -44,6 +44,16 @@ impl File { // SAFETY: `File::ptr` is guaranteed to be valid by the type invariants. unsafe { (*self.ptr).f_flags & bindings::O_NONBLOCK == 0 } } + + /// Returns the credentials of the task that originally opened the file. + pub fn cred(&self) -> CredentialRef<'_> { + // SAFETY: `File::ptr` is guaranteed to be valid by the type invariants. + let ptr = unsafe { (*self.ptr).f_cred }; + // SAFETY: The lifetimes of `self` and `CredentialRef` are tied, so it is guaranteed that + // the credential pointer remains valid (because the file is still alive, and it doesn't + // change over the lifetime of a file). + unsafe { CredentialRef::from_ptr(ptr) } + } } impl Drop for File { diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 0936bc3fb53f8a..1ed05c49a94495 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -47,6 +47,7 @@ pub mod amba; pub mod buffer; pub mod c_types; pub mod chrdev; +pub mod cred; pub mod device; pub mod driver; mod error;