From: Benno Lossin <lossin@kernel.org>
To: "Benno Lossin" <lossin@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>
Cc: "Janne Grunau" <j@jannau.net>,
asahi@lists.linux.dev, rust-for-linux@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 1/2] rust: pin-init: internal: init: remove `#[disable_initialized_field_access]`
Date: Sat, 28 Feb 2026 12:37:04 +0100 [thread overview]
Message-ID: <20260228113713.1402110-1-lossin@kernel.org> (raw)
Gary noticed [1] that the initializer macros as well as the `[Pin]Init`
traits cannot support packed struct, since they use operations that
require aligned pointers. This means that any code using packed structs
and pin-init is unsound.
Thus remove the `#[disable_initialized_field_access]` attribute from
`init!`, which is the only safe way to create an initializer of a packed
struct.
In the future, we can add support for packed structs by changing the
trait infrastructure to include `UnalignedInit` or hopefully another
mechanism.
Reported-by: Gary Guo <gary@garyguo.net>
Link: https://rust-for-linux.zulipchat.com/#narrow/channel/561532-pin-init/topic/initialized.20field.20accessor.20detection/with/576210658 [1]
Fixes: ceca298c53f9 ("rust: pin-init: internal: init: add escape hatch for referencing initialized fields")
Signed-off-by: Benno Lossin <lossin@kernel.org>
---
This commit does not need backporting, as ceca298c53f9 is not yet in any
stable tree.
However, the unsoundness still affects several stable trees, because it
was unknowingly fixed in commit 42415d163e5d ("rust: pin-init: add
references to previously initialized fields"). Before then, packed
structs compiled without any issues with pin-init and thus all prior
kernel versions with pin-init that do not contain that commit are
affected.
We introduced pin-init in 90e53c5e70a6 ("rust: add pin-init API core"),
which was included in 6.4. The affected stable trees that are still
maintained are: 6.17, 6.16, 6.12, and 6.6. Note that 6.18 and 6.19
already contain 42415d163e5d, so they are unaffected.
I will prepare a separate patch series to backport 42415d163e5d to each
of the affected trees, including the second patch of this series that
documents the fact that field accessors are load-bearing for soundness.
@asahi folks, let me know if I should prioritize a solution for packed
structs. Otherwise I'd like not support it at the moment, as that
requires some deeper changes to the internals of pin-init. I'm tracking
the status of packed structs in:
https://github.com/Rust-for-Linux/pin-init/issues/112
---
rust/pin-init/internal/src/init.rs | 39 ++++++------------------------
1 file changed, 8 insertions(+), 31 deletions(-)
diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
index 42936f915a07..da53adc44ecf 100644
--- a/rust/pin-init/internal/src/init.rs
+++ b/rust/pin-init/internal/src/init.rs
@@ -62,7 +62,6 @@ fn ident(&self) -> Option<&Ident> {
enum InitializerAttribute {
DefaultError(DefaultErrorAttribute),
- DisableInitializedFieldAccess,
}
struct DefaultErrorAttribute {
@@ -86,6 +85,7 @@ pub(crate) fn expand(
let error = error.map_or_else(
|| {
if let Some(default_error) = attrs.iter().fold(None, |acc, attr| {
+ #[expect(irrefutable_let_patterns)]
if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr {
Some(ty.clone())
} else {
@@ -145,15 +145,7 @@ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
};
// `mixed_site` ensures that the data is not accessible to the user-controlled code.
let data = Ident::new("__data", Span::mixed_site());
- let init_fields = init_fields(
- &fields,
- pinned,
- !attrs
- .iter()
- .any(|attr| matches!(attr, InitializerAttribute::DisableInitializedFieldAccess)),
- &data,
- &slot,
- );
+ let init_fields = init_fields(&fields, pinned, &data, &slot);
let field_check = make_field_check(&fields, init_kind, &path);
Ok(quote! {{
// We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
@@ -236,7 +228,6 @@ fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKi
fn init_fields(
fields: &Punctuated<InitializerField, Token![,]>,
pinned: bool,
- generate_initialized_accessors: bool,
data: &Ident,
slot: &Ident,
) -> TokenStream {
@@ -272,13 +263,6 @@ fn init_fields(
unsafe { &mut (*#slot).#ident }
}
};
- let accessor = generate_initialized_accessors.then(|| {
- quote! {
- #(#cfgs)*
- #[allow(unused_variables)]
- let #ident = #accessor;
- }
- });
quote! {
#(#attrs)*
{
@@ -286,7 +270,9 @@ fn init_fields(
// SAFETY: TODO
unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) };
}
- #accessor
+ #(#cfgs)*
+ #[allow(unused_variables)]
+ let #ident = #accessor;
}
}
InitializerKind::Init { ident, value, .. } => {
@@ -326,20 +312,15 @@ fn init_fields(
},
)
};
- let accessor = generate_initialized_accessors.then(|| {
- quote! {
- #(#cfgs)*
- #[allow(unused_variables)]
- let #ident = #accessor;
- }
- });
quote! {
#(#attrs)*
{
let #init = #value;
#value_init
}
- #accessor
+ #(#cfgs)*
+ #[allow(unused_variables)]
+ let #ident = #accessor;
}
}
InitializerKind::Code { block: value, .. } => quote! {
@@ -466,10 +447,6 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
if a.path().is_ident("default_error") {
a.parse_args::<DefaultErrorAttribute>()
.map(InitializerAttribute::DefaultError)
- } else if a.path().is_ident("disable_initialized_field_access") {
- a.meta
- .require_path_only()
- .map(|_| InitializerAttribute::DisableInitializedFieldAccess)
} else {
Err(syn::Error::new_spanned(a, "unknown initializer attribute"))
}
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
--
2.53.0
next reply other threads:[~2026-02-28 11:37 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-28 11:37 Benno Lossin [this message]
2026-02-28 11:37 ` [PATCH 2/2] rust: pin-init: internal: init: document load-bearing fact of field accessors Benno Lossin
2026-02-28 11:55 ` Gary Guo
2026-02-28 14:56 ` Benno Lossin
2026-02-28 14:11 ` Miguel Ojeda
2026-02-28 14:49 ` Benno Lossin
2026-02-28 12:03 ` [PATCH 1/2] rust: pin-init: internal: init: remove `#[disable_initialized_field_access]` Gary Guo
2026-02-28 14:09 ` Miguel Ojeda
2026-03-01 17:12 ` Janne Grunau
2026-03-02 14:11 ` Benno Lossin
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=20260228113713.1402110-1-lossin@kernel.org \
--to=lossin@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=aliceryhl@google.com \
--cc=asahi@lists.linux.dev \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=j@jannau.net \
--cc=linux-kernel@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/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