lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-Id: <CQ3RBI3EBELP.206VKZ8X6P301@vincent> Date: Sat, 28 Jan 2023 11:38:36 +0100 From: "Vincenzo Palazzo" <vincenzopalazzodev@...il.com> To: "Wedson Almeida Filho" <wedsonaf@...il.com>, <rust-for-linux@...r.kernel.org> Cc: "Miguel Ojeda" <ojeda@...nel.org>, "Alex Gaynor" <alex.gaynor@...il.com>, "Boqun Feng" <boqun.feng@...il.com>, "Gary Guo" <gary@...yguo.net>, Björn Roy Baron <bjorn3_gh@...tonmail.com>, <linux-kernel@...r.kernel.org> Subject: Re: [PATCH 1/5] rust: types: introduce `ScopeGuard` On Thu Jan 19, 2023 at 6:40 PM CET, Wedson Almeida Filho wrote: > This allows us to run some code when the guard is dropped (e.g., > implicitly when it goes out of scope). We can also prevent the > guard from running by calling its `dismiss()` method. > > Signed-off-by: Wedson Almeida Filho <wedsonaf@...il.com> > --- Reviewed-by: Vincenzo Palazzo <vincenzopalazzodev@...il.com> > rust/kernel/types.rs | 127 ++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 126 insertions(+), 1 deletion(-) > > diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs > index e84e51ec9716..f0ad4472292d 100644 > --- a/rust/kernel/types.rs > +++ b/rust/kernel/types.rs > @@ -2,7 +2,132 @@ > > //! Kernel types. > > -use core::{cell::UnsafeCell, mem::MaybeUninit}; > +use alloc::boxed::Box; > +use core::{ > + cell::UnsafeCell, > + mem::MaybeUninit, > + ops::{Deref, DerefMut}, > +}; > + > +/// Runs a cleanup function/closure when dropped. > +/// > +/// The [`ScopeGuard::dismiss`] function prevents the cleanup function from running. > +/// > +/// # Examples > +/// > +/// In the example below, we have multiple exit paths and we want to log regardless of which one is > +/// taken: > +/// ``` > +/// # use kernel::ScopeGuard; > +/// fn example1(arg: bool) { > +/// let _log = ScopeGuard::new(|| pr_info!("example1 completed\n")); > +/// > +/// if arg { > +/// return; > +/// } > +/// > +/// pr_info!("Do something...\n"); > +/// } > +/// > +/// # example1(false); > +/// # example1(true); > +/// ``` > +/// > +/// In the example below, we want to log the same message on all early exits but a different one on > +/// the main exit path: > +/// ``` > +/// # use kernel::ScopeGuard; > +/// fn example2(arg: bool) { > +/// let log = ScopeGuard::new(|| pr_info!("example2 returned early\n")); > +/// > +/// if arg { > +/// return; > +/// } > +/// > +/// // (Other early returns...) > +/// > +/// log.dismiss(); > +/// pr_info!("example2 no early return\n"); > +/// } > +/// > +/// # example2(false); > +/// # example2(true); > +/// ``` > +/// > +/// In the example below, we need a mutable object (the vector) to be accessible within the log > +/// function, so we wrap it in the [`ScopeGuard`]: > +/// ``` > +/// # use kernel::ScopeGuard; > +/// fn example3(arg: bool) -> Result { > +/// let mut vec = > +/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len())); > +/// > +/// vec.try_push(10u8)?; > +/// if arg { > +/// return Ok(()); > +/// } > +/// vec.try_push(20u8)?; > +/// Ok(()) > +/// } > +/// > +/// # assert_eq!(example3(false), Ok(())); > +/// # assert_eq!(example3(true), Ok(())); > +/// ``` > +/// > +/// # Invariants > +/// > +/// The value stored in the struct is nearly always `Some(_)`, except between > +/// [`ScopeGuard::dismiss`] and [`ScopeGuard::drop`]: in this case, it will be `None` as the value > +/// will have been returned to the caller. Since [`ScopeGuard::dismiss`] consumes the guard, > +/// callers won't be able to use it anymore. > +pub struct ScopeGuard<T, F: FnOnce(T)>(Option<(T, F)>); > + > +impl<T, F: FnOnce(T)> ScopeGuard<T, F> { > + /// Creates a new guarded object wrapping the given data and with the given cleanup function. > + pub fn new_with_data(data: T, cleanup_func: F) -> Self { > + // INVARIANT: The struct is being initialised with `Some(_)`. > + Self(Some((data, cleanup_func))) > + } > + > + /// Prevents the cleanup function from running and returns the guarded data. > + pub fn dismiss(mut self) -> T { > + // INVARIANT: This is the exception case in the invariant; it is not visible to callers > + // because this function consumes `self`. > + self.0.take().unwrap().0 > + } > +} > + > +impl ScopeGuard<(), Box<dyn FnOnce(())>> { > + /// Creates a new guarded object with the given cleanup function. > + pub fn new(cleanup: impl FnOnce()) -> ScopeGuard<(), impl FnOnce(())> { > + ScopeGuard::new_with_data((), move |_| cleanup()) > + } > +} > + > +impl<T, F: FnOnce(T)> Deref for ScopeGuard<T, F> { > + type Target = T; > + > + fn deref(&self) -> &T { > + // The type invariants guarantee that `unwrap` will succeed. > + &self.0.as_ref().unwrap().0 > + } > +} > + > +impl<T, F: FnOnce(T)> DerefMut for ScopeGuard<T, F> { > + fn deref_mut(&mut self) -> &mut T { > + // The type invariants guarantee that `unwrap` will succeed. > + &mut self.0.as_mut().unwrap().0 > + } > +} > + > +impl<T, F: FnOnce(T)> Drop for ScopeGuard<T, F> { > + fn drop(&mut self) { > + // Run the cleanup function if one is still present. > + if let Some((data, cleanup)) = self.0.take() { > + cleanup(data) > + } > + } > +} > > /// Stores an opaque value. > /// > -- > 2.34.1
Powered by blists - more mailing lists