From: Gary Guo <gary@garyguo.net>
To: gregkh@linuxfoundation.org, ojeda@kernel.org
Cc: stable@vger.kernel.org, Gary Guo <gary@garyguo.net>
Subject: [PATCH 6.18.y] rust: pin-init: fix incorrect accessor reference lifetime
Date: Tue, 12 May 2026 16:02:57 +0100 [thread overview]
Message-ID: <20260512150257.3240635-1-gary@garyguo.net> (raw)
In-Reply-To: <2026051228-lurch-yogurt-28a2@gregkh>
commit 68bf102226cf2199dc609b67c1e847cad4de4b57 upstream
When a field has been initialized, `init!`/`pin_init!` create a reference
or pinned reference to the field so it can be accessed later during the
initialization of other fields. However, the reference it created is
incorrectly `&'static` rather than just the scope of the initializer.
This means that you can do
init!(Foo {
a: 1,
_: {
let b: &'static u32 = a;
}
})
which is unsound.
This is caused by `&mut (*$slot).$ident`, which actually allows arbitrary
lifetime, so this is effectively `'static`.
Fix it by adding `let_binding` method on `DropGuard` to shorten lifetime.
This results in exactly what we want for these accessors. The safety and
invariant comments of `DropGuard` have been reworked; instead of reasoning
about what caller can do with the guard, express it in a way that the
ownership is transferred to the guard and `forget` takes it back, so the
unsafe operations within the `DropGuard` can be more easily justified.
Assisted-by: Claude:claude-3-opus
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/pin-init/src/__internal.rs | 28 ++++++----
rust/pin-init/src/macros.rs | 91 +++++++++++++++++++--------------
2 files changed, 73 insertions(+), 46 deletions(-)
diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs
index 90f18e9a2912..83cc7ca2ead3 100644
--- a/rust/pin-init/src/__internal.rs
+++ b/rust/pin-init/src/__internal.rs
@@ -218,32 +218,42 @@ struct Foo {
/// When a value of this type is dropped, it drops a `T`.
///
/// Can be forgotten to prevent the drop.
+///
+/// # Invariants
+///
+/// - `ptr` is valid and properly aligned.
+/// - `*ptr` is initialized and owned by this guard.
pub struct DropGuard<T: ?Sized> {
ptr: *mut T,
}
impl<T: ?Sized> DropGuard<T> {
- /// Creates a new [`DropGuard<T>`]. It will [`ptr::drop_in_place`] `ptr` when it gets dropped.
+ /// Creates a drop guard and transfer the ownership of the pointer content.
///
- /// # Safety
+ /// The ownership is only relinquished if the guard is forgotten via [`core::mem::forget`].
///
- /// `ptr` must be a valid pointer.
+ /// # Safety
///
- /// It is the callers responsibility that `self` will only get dropped if the pointee of `ptr`:
- /// - has not been dropped,
- /// - is not accessible by any other means,
- /// - will not be dropped by any other means.
+ /// - `ptr` is valid and properly aligned.
+ /// - `*ptr` is initialized, and the ownership is transferred to this guard.
#[inline]
pub unsafe fn new(ptr: *mut T) -> Self {
+ // INVARIANT: By safety requirement.
Self { ptr }
}
+
+ /// Create a let binding for accessor use.
+ #[inline]
+ pub fn let_binding(&mut self) -> &mut T {
+ // SAFETY: Per type invariant.
+ unsafe { &mut *self.ptr }
+ }
}
impl<T: ?Sized> Drop for DropGuard<T> {
#[inline]
fn drop(&mut self) {
- // SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function
- // ensuring that this operation is safe.
+ // SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard.
unsafe { ptr::drop_in_place(self.ptr) }
}
}
diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs
index fdf38b4fdbdc..a65ddf6cc873 100644
--- a/rust/pin-init/src/macros.rs
+++ b/rust/pin-init/src/macros.rs
@@ -1310,27 +1310,33 @@ fn assert_zeroable<T: $crate::Zeroable>(_: *mut T) {}
// return when an error/panic occurs.
// We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`.
unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? };
- // NOTE: the field accessor ensures that the initialized field is properly aligned.
+ // NOTE: this ensures that the initialized field is properly aligned.
// Unaligned fields will cause the compiler to emit E0793. We do not support
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
// `ptr::write` below has the same requirement.
- // SAFETY:
- // - the project function does the correct field projection,
- // - the field has been initialized,
- // - the reference is only valid until the end of the initializer.
- #[allow(unused_variables)]
- let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
+ // SAFETY: the field has been initialized.
+ let _ = unsafe { &mut (*$slot).$field };
// Create the drop guard:
//
// We rely on macro hygiene to make it impossible for users to access this local variable.
// We use `paste!` to create new hygiene for `$field`.
$crate::macros::paste! {
- // SAFETY: We forget the guard later when initialization has succeeded.
- let [< __ $field _guard >] = unsafe {
+ // SAFETY:
+ // - `addr_of_mut!((*$slot).$field)` is valid.
+ // - `(*$slot).$field` has been initialized above.
+ // - We only need the ownership to the pointee back when initialization has
+ // succeeded, where we `forget` the guard.
+ let mut [< __ $field _guard >] = unsafe {
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
};
+ // NOTE: The reference is derived from the guard so that it only lives as long as
+ // the guard does and cannot escape the scope.
+ #[allow(unused_variables)]
+ // SAFETY: the project function does the correct field projection.
+ let $field = unsafe { $data.[< __project_ $field >]([< __ $field _guard >].let_binding()) };
+
$crate::__init_internal!(init_slot($use_data):
@data($data),
@slot($slot),
@@ -1353,27 +1359,30 @@ fn assert_zeroable<T: $crate::Zeroable>(_: *mut T) {}
// return when an error/panic occurs.
unsafe { $crate::Init::__init(init, ::core::ptr::addr_of_mut!((*$slot).$field))? };
- // NOTE: the field accessor ensures that the initialized field is properly aligned.
+ // NOTE: this ensures that the initialized field is properly aligned.
// Unaligned fields will cause the compiler to emit E0793. We do not support
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
// `ptr::write` below has the same requirement.
- // SAFETY:
- // - the field is not structurally pinned, since the line above must compile,
- // - the field has been initialized,
- // - the reference is only valid until the end of the initializer.
- #[allow(unused_variables)]
- let $field = unsafe { &mut (*$slot).$field };
+ // SAFETY: the field has been initialized.
+ let _ = unsafe { &mut (*$slot).$field };
// Create the drop guard:
//
// We rely on macro hygiene to make it impossible for users to access this local variable.
// We use `paste!` to create new hygiene for `$field`.
$crate::macros::paste! {
- // SAFETY: We forget the guard later when initialization has succeeded.
- let [< __ $field _guard >] = unsafe {
+ // SAFETY:
+ // - `addr_of_mut!((*$slot).$field)` is valid.
+ // - `(*$slot).$field` has been initialized above.
+ // - We only need the ownership to the pointee back when initialization has
+ // succeeded, where we `forget` the guard.
+ let mut [< __ $field _guard >] = unsafe {
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
};
+ #[allow(unused_variables)]
+ let $field = [< __ $field _guard >].let_binding();
+
$crate::__init_internal!(init_slot():
@data($data),
@slot($slot),
@@ -1397,28 +1406,30 @@ fn assert_zeroable<T: $crate::Zeroable>(_: *mut T) {}
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
}
- // NOTE: the field accessor ensures that the initialized field is properly aligned.
+ // NOTE: this ensures that the initialized field is properly aligned.
// Unaligned fields will cause the compiler to emit E0793. We do not support
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
// `ptr::write` below has the same requirement.
- #[allow(unused_variables)]
- // SAFETY:
- // - the field is not structurally pinned, since no `use_data` was required to create this
- // initializer,
- // - the field has been initialized,
- // - the reference is only valid until the end of the initializer.
- let $field = unsafe { &mut (*$slot).$field };
+ // SAFETY: the field has been initialized.
+ let _ = unsafe { &mut (*$slot).$field };
// Create the drop guard:
//
// We rely on macro hygiene to make it impossible for users to access this local variable.
// We use `paste!` to create new hygiene for `$field`.
$crate::macros::paste! {
- // SAFETY: We forget the guard later when initialization has succeeded.
- let [< __ $field _guard >] = unsafe {
+ // SAFETY:
+ // - `addr_of_mut!((*$slot).$field)` is valid.
+ // - `(*$slot).$field` has been initialized above.
+ // - We only need the ownership to the pointee back when initialization has
+ // succeeded, where we `forget` the guard.
+ let mut [< __ $field _guard >] = unsafe {
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
};
+ #[allow(unused_variables)]
+ let $field = [< __ $field _guard >].let_binding();
+
$crate::__init_internal!(init_slot():
@data($data),
@slot($slot),
@@ -1441,27 +1452,33 @@ fn assert_zeroable<T: $crate::Zeroable>(_: *mut T) {}
// SAFETY: The memory at `slot` is uninitialized.
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
}
- // NOTE: the field accessor ensures that the initialized field is properly aligned.
+ // NOTE: this ensures that the initialized field is properly aligned.
// Unaligned fields will cause the compiler to emit E0793. We do not support
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
// `ptr::write` below has the same requirement.
- // SAFETY:
- // - the project function does the correct field projection,
- // - the field has been initialized,
- // - the reference is only valid until the end of the initializer.
- #[allow(unused_variables)]
- let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
+ // SAFETY: the field has been initialized.
+ let _ = unsafe { &mut (*$slot).$field };
// Create the drop guard:
//
// We rely on macro hygiene to make it impossible for users to access this local variable.
// We use `paste!` to create new hygiene for `$field`.
$crate::macros::paste! {
- // SAFETY: We forget the guard later when initialization has succeeded.
- let [< __ $field _guard >] = unsafe {
+ // SAFETY:
+ // - `addr_of_mut!((*$slot).$field)` is valid.
+ // - `(*$slot).$field` has been initialized above.
+ // - We only need the ownership to the pointee back when initialization has
+ // succeeded, where we `forget` the guard.
+ let mut [< __ $field _guard >] = unsafe {
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
};
+ // NOTE: The reference is derived from the guard so that it only lives as long as
+ // the guard does and cannot escape the scope.
+ #[allow(unused_variables)]
+ // SAFETY: the project function does the correct field projection.
+ let $field = unsafe { $data.[< __project_ $field >]([< __ $field _guard >].let_binding()) };
+
$crate::__init_internal!(init_slot($use_data):
@data($data),
@slot($slot),
base-commit: d31a849ff5011dad5c271b53819a0b279e367d68
--
2.51.2
prev parent reply other threads:[~2026-05-12 15:03 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-12 12:44 FAILED: patch "[PATCH] rust: pin-init: fix incorrect accessor reference lifetime" failed to apply to 6.18-stable tree gregkh
2026-05-12 15:02 ` Gary Guo [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260512150257.3240635-1-gary@garyguo.net \
--to=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=ojeda@kernel.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox