* [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token
@ 2026-03-11 10:50 Benno Lossin
2026-03-11 12:51 ` Gary Guo
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Benno Lossin @ 2026-03-11 10:50 UTC (permalink / raw)
To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Fiona Behrens
Cc: Tim Chirananthavat, stable, rust-for-linux, linux-kernel
We use a unit struct `__InitOk` in the closure generated by the
initializer macros as the return value. We shadow it by creating a
struct with the same name again inside of the closure, preventing early
returns of `Ok` in the initializer (before all fields have been
initialized).
In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
this solution no longer works [1]. The shadowed struct can be named
through type inference. In addition, there is an RFC proposing to add
the feature of path inference to Rust, which would similarly allow [2]
Thus remove the shadowed token and replace it with an `unsafe` to create
token.
The reason we initially used the shadowing solution was because an
alternative solution used a builder pattern. Gary writes [3]:
In the early builder-pattern based InitOk, having a single InitOk
type for token is unsound because one can launder an InitOk token
used for one place to another initializer. I used a branded lifetime
solution, and then you figured out that using a shadowed type would
work better because nobody could construct it at all.
The laundering issue does not apply to the approach we ended up with
today.
With this change, the example by Tim Chirananthavat in [1] no longer
compiles and results in this error:
error: cannot construct `pin_init::__internal::InitOk` with struct literal syntax due to private fields
--> src/main.rs:26:17
|
26 | InferredType {}
| ^^^^^^^^^^^^
|
= note: private field `0` that was not provided
help: you might have meant to use the `new` associated function
|
26 - InferredType {}
26 + InferredType::new()
|
Applying the suggestion of using the `::new()` function, results in
another expected error:
error[E0133]: call to unsafe function `pin_init::__internal::InitOk::new` is unsafe and requires unsafe block
--> src/main.rs:26:17
|
26 | InferredType::new()
| ^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
Reported-by: Tim Chirananthavat <theemathas@gmail.com>
Link: https://github.com/rust-lang/rust/issues/153535 [1]
Link: https://github.com/rust-lang/rfcs/pull/3444#issuecomment-4016145373 [2]
Link: https://github.com/rust-lang/rust/issues/153535#issuecomment-4017620804 [3]
Fixes: fc6c6baa1f40 ("rust: init: add initialization macros")
Cc: stable@vger.kernel.org
Signed-off-by: Benno Lossin <lossin@kernel.org>
---
This is not yet a soundness issue, but could become one in the future
when TAIT gets stabilized in a form that allows the problem described.
---
rust/pin-init/internal/src/init.rs | 22 +++++++---------------
rust/pin-init/src/__internal.rs | 28 ++++++++++++++++++++++++----
2 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
index 42936f915a07..cfae789d55ba 100644
--- a/rust/pin-init/internal/src/init.rs
+++ b/rust/pin-init/internal/src/init.rs
@@ -156,11 +156,6 @@ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
);
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
- // type and shadow it later when we insert the arbitrary user code. That way there will be
- // no possibility of returning without `unsafe`.
- struct __InitOk;
-
// Get the data about fields from the supplied type.
// SAFETY: TODO
let #data = unsafe {
@@ -170,18 +165,15 @@ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
#path::#get_data()
};
// Ensure that `#data` really is of type `#data` and help with type inference:
- let init = ::pin_init::__internal::#data_trait::make_closure::<_, __InitOk, #error>(
+ let init = ::pin_init::__internal::#data_trait::make_closure::<_, #error>(
#data,
move |slot| {
- {
- // Shadow the structure so it cannot be used to return early.
- struct __InitOk;
- #zeroable_check
- #this
- #init_fields
- #field_check
- }
- Ok(__InitOk)
+ #zeroable_check
+ #this
+ #init_fields
+ #field_check
+ // SAFETY: we are the `init!` macro that is allowed to call this.
+ Ok(unsafe { ::pin_init::__internal::InitOk::new() })
}
);
let init = move |slot| -> ::core::result::Result<(), #error> {
diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs
index 90f18e9a2912..90adbdc1893b 100644
--- a/rust/pin-init/src/__internal.rs
+++ b/rust/pin-init/src/__internal.rs
@@ -46,6 +46,24 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
}
}
+/// Token type to signify successful initialization.
+///
+/// Can only be constructed via the unsafe [`Self::new`] function. The initializer macros use this
+/// token type to prevent returning `Ok` from an initializer without initializing all fields.
+pub struct InitOk(());
+
+impl InitOk {
+ /// Creates a new token.
+ ///
+ /// # Safety
+ ///
+ /// This function may only be called from the `init!` macro in `../internal/src/init.rs`.
+ #[inline(always)]
+ pub unsafe fn new() -> Self {
+ Self(())
+ }
+}
+
/// This trait is only implemented via the `#[pin_data]` proc-macro. It is used to facilitate
/// the pin projections within the initializers.
///
@@ -68,9 +86,10 @@ pub unsafe trait PinData: Copy {
type Datee: ?Sized + HasPinData;
/// Type inference helper function.
- fn make_closure<F, O, E>(self, f: F) -> F
+ #[inline(always)]
+ fn make_closure<F, E>(self, f: F) -> F
where
- F: FnOnce(*mut Self::Datee) -> Result<O, E>,
+ F: FnOnce(*mut Self::Datee) -> Result<InitOk, E>,
{
f
}
@@ -98,9 +117,10 @@ pub unsafe trait InitData: Copy {
type Datee: ?Sized + HasInitData;
/// Type inference helper function.
- fn make_closure<F, O, E>(self, f: F) -> F
+ #[inline(always)]
+ fn make_closure<F, E>(self, f: F) -> F
where
- F: FnOnce(*mut Self::Datee) -> Result<O, E>,
+ F: FnOnce(*mut Self::Datee) -> Result<InitOk, E>,
{
f
}
base-commit: 1f318b96cc84d7c2ab792fcc0bfd42a7ca890681
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token
2026-03-11 10:50 [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token Benno Lossin
@ 2026-03-11 12:51 ` Gary Guo
2026-03-11 13:01 ` Danilo Krummrich
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Gary Guo @ 2026-03-11 12:51 UTC (permalink / raw)
To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Fiona Behrens
Cc: Tim Chirananthavat, stable, rust-for-linux, linux-kernel
On Wed Mar 11, 2026 at 10:50 AM GMT, Benno Lossin wrote:
> We use a unit struct `__InitOk` in the closure generated by the
> initializer macros as the return value. We shadow it by creating a
> struct with the same name again inside of the closure, preventing early
> returns of `Ok` in the initializer (before all fields have been
> initialized).
>
> In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
> this solution no longer works [1]. The shadowed struct can be named
> through type inference. In addition, there is an RFC proposing to add
> the feature of path inference to Rust, which would similarly allow [2]
>
> Thus remove the shadowed token and replace it with an `unsafe` to create
> token.
>
> The reason we initially used the shadowing solution was because an
> alternative solution used a builder pattern. Gary writes [3]:
>
> In the early builder-pattern based InitOk, having a single InitOk
> type for token is unsound because one can launder an InitOk token
> used for one place to another initializer. I used a branded lifetime
> solution, and then you figured out that using a shadowed type would
> work better because nobody could construct it at all.
>
> The laundering issue does not apply to the approach we ended up with
> today.
>
> With this change, the example by Tim Chirananthavat in [1] no longer
> compiles and results in this error:
>
> error: cannot construct `pin_init::__internal::InitOk` with struct literal syntax due to private fields
> --> src/main.rs:26:17
> |
> 26 | InferredType {}
> | ^^^^^^^^^^^^
> |
> = note: private field `0` that was not provided
> help: you might have meant to use the `new` associated function
> |
> 26 - InferredType {}
> 26 + InferredType::new()
> |
>
> Applying the suggestion of using the `::new()` function, results in
> another expected error:
>
> error[E0133]: call to unsafe function `pin_init::__internal::InitOk::new` is unsafe and requires unsafe block
> --> src/main.rs:26:17
> |
> 26 | InferredType::new()
> | ^^^^^^^^^^^^^^^^^^^ call to unsafe function
> |
> = note: consult the function's documentation for information on how to avoid undefined behavior
>
> Reported-by: Tim Chirananthavat <theemathas@gmail.com>
> Link: https://github.com/rust-lang/rust/issues/153535 [1]
> Link: https://github.com/rust-lang/rfcs/pull/3444#issuecomment-4016145373 [2]
> Link: https://github.com/rust-lang/rust/issues/153535#issuecomment-4017620804 [3]
> Fixes: fc6c6baa1f40 ("rust: init: add initialization macros")
> Cc: stable@vger.kernel.org
> Signed-off-by: Benno Lossin <lossin@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> This is not yet a soundness issue, but could become one in the future
> when TAIT gets stabilized in a form that allows the problem described.
> ---
> rust/pin-init/internal/src/init.rs | 22 +++++++---------------
> rust/pin-init/src/__internal.rs | 28 ++++++++++++++++++++++++----
> 2 files changed, 31 insertions(+), 19 deletions(-)
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token
2026-03-11 10:50 [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token Benno Lossin
2026-03-11 12:51 ` Gary Guo
@ 2026-03-11 13:01 ` Danilo Krummrich
2026-03-11 16:04 ` Alice Ryhl
2026-03-11 18:14 ` Benno Lossin
2026-03-11 16:02 ` Alice Ryhl
2026-03-12 7:57 ` Miguel Ojeda
3 siblings, 2 replies; 8+ messages in thread
From: Danilo Krummrich @ 2026-03-11 13:01 UTC (permalink / raw)
To: Benno Lossin
Cc: Gary Guo, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Fiona Behrens,
Tim Chirananthavat, stable, rust-for-linux, linux-kernel
On Wed Mar 11, 2026 at 11:50 AM CET, Benno Lossin wrote:
> In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
> this solution no longer works [1]. The shadowed struct can be named
> through type inference. In addition, there is an RFC proposing to add
> the feature of path inference to Rust, which would similarly allow [2]
NIT: I'm not sure if the sentence is supposed to end here, at least it misses a
period.
Besides that, is my understanding correct that the changes mentioned above are
targeting a subsequent Rust edition?
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token
2026-03-11 13:01 ` Danilo Krummrich
@ 2026-03-11 16:04 ` Alice Ryhl
2026-03-11 16:11 ` Gary Guo
2026-03-11 18:14 ` Benno Lossin
1 sibling, 1 reply; 8+ messages in thread
From: Alice Ryhl @ 2026-03-11 16:04 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Andreas Hindborg, Trevor Gross,
Fiona Behrens, Tim Chirananthavat, stable, rust-for-linux,
linux-kernel
On Wed, Mar 11, 2026 at 2:01 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed Mar 11, 2026 at 11:50 AM CET, Benno Lossin wrote:
> > In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
> > this solution no longer works [1]. The shadowed struct can be named
> > through type inference. In addition, there is an RFC proposing to add
> > the feature of path inference to Rust, which would similarly allow [2]
>
> NIT: I'm not sure if the sentence is supposed to end here, at least it misses a
> period.
>
> Besides that, is my understanding correct that the changes mentioned above are
> targeting a subsequent Rust edition?
I don't think it's currently clear when/if the changes mentioned will
land. But on the topic of editions, it's worth keeping in mind that
macros don't know the edition they are expanding code into, so the
macro can't have different logic per edition.
Alice
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token
2026-03-11 16:04 ` Alice Ryhl
@ 2026-03-11 16:11 ` Gary Guo
0 siblings, 0 replies; 8+ messages in thread
From: Gary Guo @ 2026-03-11 16:11 UTC (permalink / raw)
To: Alice Ryhl, Danilo Krummrich
Cc: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Andreas Hindborg, Trevor Gross,
Fiona Behrens, Tim Chirananthavat, stable, rust-for-linux,
linux-kernel
On Wed Mar 11, 2026 at 4:04 PM GMT, Alice Ryhl wrote:
> On Wed, Mar 11, 2026 at 2:01 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> On Wed Mar 11, 2026 at 11:50 AM CET, Benno Lossin wrote:
>> > In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
>> > this solution no longer works [1]. The shadowed struct can be named
>> > through type inference. In addition, there is an RFC proposing to add
>> > the feature of path inference to Rust, which would similarly allow [2]
>>
>> NIT: I'm not sure if the sentence is supposed to end here, at least it misses a
>> period.
>>
>> Besides that, is my understanding correct that the changes mentioned above are
>> targeting a subsequent Rust edition?
>
> I don't think it's currently clear when/if the changes mentioned will
> land. But on the topic of editions, it's worth keeping in mind that
> macros don't know the edition they are expanding code into, so the
> macro can't have different logic per edition.
Macro expansion carries information on the def-site edition, so expanded code
that originates from macro itself is parsed using the edition of the macro.
You can test this in action by defining a macro in Rust 2024 using if-let-chain
(which is only available to Rust 2024) and use it from a Rust 2021 crate, and it
will still work.
That said, this specific example is related to syntax, where type inference is
a more global thing so I am unsure how it will interact with cross-edition
macros.
Best,
Gary
>
> Alice
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token
2026-03-11 13:01 ` Danilo Krummrich
2026-03-11 16:04 ` Alice Ryhl
@ 2026-03-11 18:14 ` Benno Lossin
1 sibling, 0 replies; 8+ messages in thread
From: Benno Lossin @ 2026-03-11 18:14 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Gary Guo, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Fiona Behrens,
Tim Chirananthavat, stable, rust-for-linux, linux-kernel
On Wed Mar 11, 2026 at 2:01 PM CET, Danilo Krummrich wrote:
> On Wed Mar 11, 2026 at 11:50 AM CET, Benno Lossin wrote:
>> In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
>> this solution no longer works [1]. The shadowed struct can be named
>> through type inference. In addition, there is an RFC proposing to add
>> the feature of path inference to Rust, which would similarly allow [2]
>
> NIT: I'm not sure if the sentence is supposed to end here, at least it misses a
> period.
Oh yeah, missed the period.
Cheers,
Benno
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token
2026-03-11 10:50 [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token Benno Lossin
2026-03-11 12:51 ` Gary Guo
2026-03-11 13:01 ` Danilo Krummrich
@ 2026-03-11 16:02 ` Alice Ryhl
2026-03-12 7:57 ` Miguel Ojeda
3 siblings, 0 replies; 8+ messages in thread
From: Alice Ryhl @ 2026-03-11 16:02 UTC (permalink / raw)
To: Benno Lossin
Cc: Gary Guo, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
Andreas Hindborg, Trevor Gross, Danilo Krummrich, Fiona Behrens,
Tim Chirananthavat, stable, rust-for-linux, linux-kernel
On Wed, Mar 11, 2026 at 11:50:49AM +0100, Benno Lossin wrote:
> The reason we initially used the shadowing solution was because an
> alternative solution used a builder pattern. Gary writes [3]:
>
> In the early builder-pattern based InitOk, having a single InitOk
> type for token is unsound because one can launder an InitOk token
> used for one place to another initializer. I used a branded lifetime
> solution, and then you figured out that using a shadowed type would
> work better because nobody could construct it at all.
>
> The laundering issue does not apply to the approach we ended up with
> today.
You could always make the unsafe-to-construct token generic over a
locally-defined type to avoid issues with laundering.
> Reported-by: Tim Chirananthavat <theemathas@gmail.com>
> Link: https://github.com/rust-lang/rust/issues/153535 [1]
> Link: https://github.com/rust-lang/rfcs/pull/3444#issuecomment-4016145373 [2]
> Link: https://github.com/rust-lang/rust/issues/153535#issuecomment-4017620804 [3]
> Fixes: fc6c6baa1f40 ("rust: init: add initialization macros")
> Cc: stable@vger.kernel.org
> Signed-off-by: Benno Lossin <lossin@kernel.org>
> ---
> This is not yet a soundness issue, but could become one in the future
> when TAIT gets stabilized in a form that allows the problem described.
Let's just land it now regardless.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Alice
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token
2026-03-11 10:50 [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token Benno Lossin
` (2 preceding siblings ...)
2026-03-11 16:02 ` Alice Ryhl
@ 2026-03-12 7:57 ` Miguel Ojeda
3 siblings, 0 replies; 8+ messages in thread
From: Miguel Ojeda @ 2026-03-12 7:57 UTC (permalink / raw)
To: Benno Lossin
Cc: Gary Guo, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Fiona Behrens, Tim Chirananthavat, stable, rust-for-linux,
linux-kernel
On Wed, Mar 11, 2026 at 11:51 AM Benno Lossin <lossin@kernel.org> wrote:
>
> We use a unit struct `__InitOk` in the closure generated by the
> initializer macros as the return value. We shadow it by creating a
> struct with the same name again inside of the closure, preventing early
> returns of `Ok` in the initializer (before all fields have been
> initialized).
>
> In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
> this solution no longer works [1]. The shadowed struct can be named
> through type inference. In addition, there is an RFC proposing to add
> the feature of path inference to Rust, which would similarly allow [2]
>
> Thus remove the shadowed token and replace it with an `unsafe` to create
> token.
>
> The reason we initially used the shadowing solution was because an
> alternative solution used a builder pattern. Gary writes [3]:
>
> In the early builder-pattern based InitOk, having a single InitOk
> type for token is unsound because one can launder an InitOk token
> used for one place to another initializer. I used a branded lifetime
> solution, and then you figured out that using a shadowed type would
> work better because nobody could construct it at all.
>
> The laundering issue does not apply to the approach we ended up with
> today.
>
> With this change, the example by Tim Chirananthavat in [1] no longer
> compiles and results in this error:
>
> error: cannot construct `pin_init::__internal::InitOk` with struct literal syntax due to private fields
> --> src/main.rs:26:17
> |
> 26 | InferredType {}
> | ^^^^^^^^^^^^
> |
> = note: private field `0` that was not provided
> help: you might have meant to use the `new` associated function
> |
> 26 - InferredType {}
> 26 + InferredType::new()
> |
>
> Applying the suggestion of using the `::new()` function, results in
> another expected error:
>
> error[E0133]: call to unsafe function `pin_init::__internal::InitOk::new` is unsafe and requires unsafe block
> --> src/main.rs:26:17
> |
> 26 | InferredType::new()
> | ^^^^^^^^^^^^^^^^^^^ call to unsafe function
> |
> = note: consult the function's documentation for information on how to avoid undefined behavior
>
> Reported-by: Tim Chirananthavat <theemathas@gmail.com>
> Link: https://github.com/rust-lang/rust/issues/153535 [1]
> Link: https://github.com/rust-lang/rfcs/pull/3444#issuecomment-4016145373 [2]
> Link: https://github.com/rust-lang/rust/issues/153535#issuecomment-4017620804 [3]
> Fixes: fc6c6baa1f40 ("rust: init: add initialization macros")
> Cc: stable@vger.kernel.org
> Signed-off-by: Benno Lossin <lossin@kernel.org>
Applied to `rust-fixes` -- thanks everyone!
[ Added period as mentioned. - Miguel ]
(We will need to resolve the backport for a bunch of stable releases.)
Cheers,
Miguel
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-03-12 7:57 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-11 10:50 [PATCH] rust: pin-init: replace shadowed return token by `unsafe`-to-create token Benno Lossin
2026-03-11 12:51 ` Gary Guo
2026-03-11 13:01 ` Danilo Krummrich
2026-03-11 16:04 ` Alice Ryhl
2026-03-11 16:11 ` Gary Guo
2026-03-11 18:14 ` Benno Lossin
2026-03-11 16:02 ` Alice Ryhl
2026-03-12 7:57 ` Miguel Ojeda
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox