public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
From: Miguel Ojeda <ojeda@kernel.org>
To: "Miguel Ojeda" <ojeda@kernel.org>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Arve Hjønnevåg" <arve@android.com>,
	"Todd Kjos" <tkjos@android.com>,
	"Christian Brauner" <christian@brauner.io>,
	"Carlos Llamas" <cmllamas@google.com>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Nathan Chancellor" <nathan@kernel.org>,
	"Nicolas Schier" <nsc@kernel.org>
Cc: "Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Nick Desaulniers" <nick.desaulniers+lkml@gmail.com>,
	"Bill Wendling" <morbo@google.com>,
	"Justin Stitt" <justinstitt@google.com>,
	llvm@lists.linux.dev, linux-kbuild@vger.kernel.org,
	"Aaron Ballman" <aaron@aaronballman.com>,
	"Bill Wendling" <isanbard@gmail.com>,
	"Cole Nixon" <nixontcole@gmail.com>,
	"Connor Kuehl" <cipkuehl@gmail.com>,
	"Fangrui Song" <i@maskray.me>,
	"James Foster" <jafosterja@gmail.com>,
	"Jeff Takahashi" <jeffrey.takahashi@gmail.com>,
	"Jordan Cantrell" <jordan.cantrell@mail.com>,
	"Matthew Maurer" <mmaurer@google.com>,
	"Nikk Forbus" <nicholas.forbus@gmail.com>,
	"Qing Zhao" <qing.zhao@oracle.com>,
	"Sami Tolvanen" <samitolvanen@google.com>,
	"Tim Pugh" <nwtpugh@gmail.com>, "Kees Cook" <kees@kernel.org>
Subject: [PATCH v2] rust: allow Clang-native `RANDSTRUCT` configs
Date: Mon, 23 Mar 2026 14:02:24 +0100	[thread overview]
Message-ID: <20260323130224.165738-1-ojeda@kernel.org> (raw)

The kernel supports `RANDSTRUCT_FULL` with Clang 16+, and `bindgen`
(which uses `libclang` under the hood) inherits the information, so the
generated bindings look correct.

For instance, running:

    bindgen x.h -- -frandomize-layout-seed=100

with:

    struct S1 {
        int a;
        int b;
    };

    struct S2 {
        int a;
        int b;
    } __attribute__((randomize_layout));

    struct S3 {
        void (*a)(void);
        void (*b)(void);
    };

    struct S4 {
        void (*a)(void);
        void (*b)(void);
    } __attribute__((no_randomize_layout));

may swap `S2`'s and `S3`'s members, but not `S1`'s nor `S4`'s:

    pub struct S1 {
        pub a: ::std::os::raw::c_int,
        pub b: ::std::os::raw::c_int,
    }

    pub struct S2 {
        pub b: ::std::os::raw::c_int,
        pub a: ::std::os::raw::c_int,
    }

    pub struct S3 {
        pub b: ::std::option::Option<unsafe extern "C" fn()>,
        pub a: ::std::option::Option<unsafe extern "C" fn()>,
    }

    pub struct S4 {
        pub a: ::std::option::Option<unsafe extern "C" fn()>,
        pub b: ::std::option::Option<unsafe extern "C" fn()>,
    }

Thus allow those configurations by requiring a Clang compiler to use
`RANDSTRUCT`. In addition, remove the `!GCC_PLUGIN_RANDSTRUCT` check
since it is not needed.

A simpler alternative would be to remove the `!RANDSTRUCT` check (keeping
the `!GCC_PLUGIN_RANDSTRUCT` one). However, if in the future GCC starts
supporting `RANDSTRUCT` natively, it would be likely that it would not
work unless GCC and Clang agree on the exact semantics of the flag. And,
as far as I can see, so far the randomization in Clang does not seem to be
guaranteed to remain stable across versions or platforms, i.e. only for a
given compiler Clang binary, given it is not documented and that LLVM's
`HEAD` has the revert in place for the expected field names in the test
(LLVM commit 8dbc6b560055 ("Revert "[randstruct] Check final randomized
layout ordering"")) [1][2]. And the GCC plugin definitely does not match,
e.g. its RNG is different (`std::mt19937` vs Bob Jenkins').

And given it is not supposed to be guaranteed to remain stable across
versions, it is a good idea to match the Clang and `bindgen`'s
`libclang` versions -- we already have a warning for that in
`scripts/rust_is_available.sh`. In the future, it would also be good to
have a build test to double-check layouts do actually match (but that
is true regardless of this particular feature).

Finally, make a few required small changes to take into account the
anonymous struct used in `randomized_struct_fields_*` in `struct
task_struct`.

[ Andreas writes:

    Tested with the rust null block driver patch series [1]. I did a
    few functional verification tests and a set of performance tests.

    For the rnull driver, enabling randstruct with this patch gives no
    statistically significant change to the average performance of the set
    of 120 workloads that we publish on [2]. Individual configurations see
    around 10% change in throughput, both directions.

    Link: https://lore.kernel.org/rust-for-linux/20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org/ [1]
    Link: https://rust-for-linux.com/null-block-driver [2]

        - Miguel ]

Cc: Aaron Ballman <aaron@aaronballman.com>
Cc: Bill Wendling <isanbard@gmail.com>
Cc: Cole Nixon <nixontcole@gmail.com>
Cc: Connor Kuehl <cipkuehl@gmail.com>
Cc: Fangrui Song <i@maskray.me>
Cc: James Foster <jafosterja@gmail.com>
Cc: Jeff Takahashi <jeffrey.takahashi@gmail.com>
Cc: Jordan Cantrell <jordan.cantrell@mail.com>
Cc: Justin Stitt <justinstitt@google.com>
Cc: Matthew Maurer <mmaurer@google.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Nicolas Schier <nsc@kernel.org>
Cc: Nikk Forbus <nicholas.forbus@gmail.com>
Cc: Qing Zhao <qing.zhao@oracle.com>
Cc: Sami Tolvanen <samitolvanen@google.com>
Cc: Tim Pugh <nwtpugh@gmail.com>
Link: https://reviews.llvm.org/D121556
Link: https://github.com/llvm/llvm-project/commit/8dbc6b560055ff5068ff45b550482ba62c36b5a5 [1]
Link: https://reviews.llvm.org/D124199 [2]
Reviewed-by: Kees Cook <kees@kernel.org>
Tested-by: Andreas Hindborg <a.hindborg@kernel.org>
Link: https://patch.msgid.link/20241119185747.862544-1-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
Ok, here is a rebased one in case it helps. Nowadays we have a couple
more instances in Binder.

I ran all the KUnit tests (nowadays we have way more compared to the
days of v1) with a bunch of debug options and for a few architectures.
Also did a run of all released Rust versions with x86_64. It would still
be nice to get more actual testing like Andreas' did with his driver,
but we will probably only get that if we actually go ahead...

Kees: do you want to do the other change directly?

 drivers/android/binder/deferred_close.rs | 29 ++++++++++++++++++---
 init/Kconfig                             |  3 +--
 rust/kernel/task.rs                      | 33 +++++++++++++++++++++---
 3 files changed, 57 insertions(+), 8 deletions(-)

diff --git a/drivers/android/binder/deferred_close.rs b/drivers/android/binder/deferred_close.rs
index ac895c04d0cb..edda56b7c4c0 100644
--- a/drivers/android/binder/deferred_close.rs
+++ b/drivers/android/binder/deferred_close.rs
@@ -69,9 +69,20 @@ pub(crate) fn close_fd(self, fd: u32) -> Result<(), DeferredFdCloseError> {
         // Task works are not available on kthreads.
         let current = kernel::current!();

-        // Check if this is a kthread.
         // SAFETY: Reading `flags` from a task is always okay.
-        if unsafe { ((*current.as_ptr()).flags & bindings::PF_KTHREAD) != 0 } {
+        let flags = unsafe {
+            #[cfg(not(CONFIG_RANDSTRUCT))]
+            {
+                (*current.as_ptr()).flags
+            }
+            #[cfg(CONFIG_RANDSTRUCT)]
+            {
+                (*current.as_ptr()).__bindgen_anon_1.flags
+            }
+        };
+
+        // Check if this is a kthread.
+        if (flags & bindings::PF_KTHREAD) != 0 {
             return Err(DeferredFdCloseError::TaskWorkUnavailable);
         }

@@ -135,6 +146,18 @@ pub(crate) fn close_fd(self, fd: u32) -> Result<(), DeferredFdCloseError> {
         // SAFETY: The `file` pointer points at a file with a non-zero refcount.
         unsafe { bindings::get_file(file) };

+        // SAFETY: Reading `files` from a task is always okay.
+        let files = unsafe {
+            #[cfg(not(CONFIG_RANDSTRUCT))]
+            {
+                (*current).files
+            }
+            #[cfg(CONFIG_RANDSTRUCT)]
+            {
+                (*current).__bindgen_anon_1.files
+            }
+        };
+
         // This method closes the fd, consuming one of our two refcounts. There could be active
         // light refcounts created from that fd, so we must ensure that the file has a positive
         // refcount for the duration of those active light refcounts. We do that by holding on to
@@ -145,7 +168,7 @@ pub(crate) fn close_fd(self, fd: u32) -> Result<(), DeferredFdCloseError> {
         // `current->files`.
         //
         // Note: fl_owner_t is currently a void pointer.
-        unsafe { bindings::filp_close(file, (*current).files as bindings::fl_owner_t) };
+        unsafe { bindings::filp_close(file, files as bindings::fl_owner_t) };

         // We update the file pointer that the task work is supposed to fput. This transfers
         // ownership of our last refcount.
diff --git a/init/Kconfig b/init/Kconfig
index 444ce811ea67..d3d0e8c95fb3 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2173,8 +2173,7 @@ config RUST
 	depends on RUST_IS_AVAILABLE
 	select EXTENDED_MODVERSIONS if MODVERSIONS
 	depends on !MODVERSIONS || GENDWARFKSYMS
-	depends on !GCC_PLUGIN_RANDSTRUCT
-	depends on !RANDSTRUCT
+	depends on !RANDSTRUCT || CC_IS_CLANG
 	depends on !DEBUG_INFO_BTF || (PAHOLE_HAS_LANG_EXCLUDE && !LTO)
 	depends on !CFI || HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC
 	select CFI_ICALL_NORMALIZE_INTEGERS if CFI
diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
index 049c8a4d45d8..a533f5be2469 100644
--- a/rust/kernel/task.rs
+++ b/rust/kernel/task.rs
@@ -207,7 +207,16 @@ pub fn as_ptr(&self) -> *mut bindings::task_struct {
     pub fn pid(&self) -> Pid {
         // SAFETY: The pid of a task never changes after initialization, so reading this field is
         // not a data race.
-        unsafe { *ptr::addr_of!((*self.as_ptr()).pid) }
+        unsafe {
+            #[cfg(not(CONFIG_RANDSTRUCT))]
+            {
+                *ptr::addr_of!((*self.as_ptr()).pid)
+            }
+            #[cfg(CONFIG_RANDSTRUCT)]
+            {
+                *ptr::addr_of!((*self.as_ptr()).__bindgen_anon_1.pid)
+            }
+        }
     }

     /// Returns the UID of the given task.
@@ -278,7 +287,16 @@ impl CurrentTask {
     pub fn mm(&self) -> Option<&MmWithUser> {
         // SAFETY: The `mm` field of `current` is not modified from other threads, so reading it is
         // not a data race.
-        let mm = unsafe { (*self.as_ptr()).mm };
+        let mm = unsafe {
+            #[cfg(not(CONFIG_RANDSTRUCT))]
+            {
+                (*self.as_ptr()).mm
+            }
+            #[cfg(CONFIG_RANDSTRUCT)]
+            {
+                (*self.as_ptr()).__bindgen_anon_1.mm
+            }
+        };

         if mm.is_null() {
             return None;
@@ -337,7 +355,16 @@ pub fn active_pid_ns(&self) -> Option<&PidNamespace> {
     pub fn group_leader(&self) -> &Task {
         // SAFETY: The group leader of a task never changes while the task is running, and `self`
         // is the current task, which is guaranteed running.
-        let ptr = unsafe { (*self.as_ptr()).group_leader };
+        let ptr = unsafe {
+            #[cfg(not(CONFIG_RANDSTRUCT))]
+            {
+                (*self.as_ptr()).group_leader
+            }
+            #[cfg(CONFIG_RANDSTRUCT)]
+            {
+                (*self.as_ptr()).__bindgen_anon_1.group_leader
+            }
+        };

         // SAFETY: `current->group_leader` stays valid for at least the duration in which `current`
         // is running, and the signature of this function ensures that the returned `&Task` can

base-commit: bf074eb6891be799174ff42e0051492681fdc045
--
2.53.0

             reply	other threads:[~2026-03-23 13:02 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-23 13:02 Miguel Ojeda [this message]
2026-03-23 13:46 ` [PATCH v2] rust: allow Clang-native `RANDSTRUCT` configs Gary Guo
2026-03-23 14:24   ` Miguel Ojeda
2026-03-26  3:18 ` kernel test robot

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=20260323130224.165738-1-ojeda@kernel.org \
    --to=ojeda@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=aaron@aaronballman.com \
    --cc=aliceryhl@google.com \
    --cc=arve@android.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=christian@brauner.io \
    --cc=cipkuehl@gmail.com \
    --cc=cmllamas@google.com \
    --cc=dakr@kernel.org \
    --cc=gary@garyguo.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=i@maskray.me \
    --cc=isanbard@gmail.com \
    --cc=jafosterja@gmail.com \
    --cc=jeffrey.takahashi@gmail.com \
    --cc=jordan.cantrell@mail.com \
    --cc=justinstitt@google.com \
    --cc=kees@kernel.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=llvm@lists.linux.dev \
    --cc=lossin@kernel.org \
    --cc=mmaurer@google.com \
    --cc=morbo@google.com \
    --cc=nathan@kernel.org \
    --cc=nicholas.forbus@gmail.com \
    --cc=nick.desaulniers+lkml@gmail.com \
    --cc=nixontcole@gmail.com \
    --cc=nsc@kernel.org \
    --cc=nwtpugh@gmail.com \
    --cc=qing.zhao@oracle.com \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=samitolvanen@google.com \
    --cc=tkjos@android.com \
    --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