From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EAF5945948; Mon, 23 Mar 2026 13:02:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774270964; cv=none; b=IyE65ldSRugVMxV6vCuR+p4UFJIWXUexdk7FkjmgjLgKQZoEix9EB/zlwPEBKf49Hnc2Ed4zyPtS5sKcCU0C7Y3J4+evWhJNd4kI7Ch7YWc8Pf7zCszKNwqx/I4cOqDWj91qzljkOEnxwVDHSRnM7ygnolRjq57SyZHqD+LlZg4= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774270964; c=relaxed/simple; bh=dGugYsSSm0m+CsRJX9rAmm9p56BnzRQocyVfqD5UzNA=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=XbXIaI2Bqr6XDDgLdYNcZmV2pEYX83L8ascmzdXX6TNXFY7cyV3Q7pIXHnnC9FOuBltKz7SMHHtFc45HXQgj7VZHZMUywOpfttx5LfeEcBlkKNdD0njw1qoPE8GDvUVVd5p5DjGwSJzm+HUC11O0CMlMFFZf0R3wG86iNCWeQ88= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XZRvS2W0; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XZRvS2W0" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 933D6C2BCB9; Mon, 23 Mar 2026 13:02:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774270963; bh=dGugYsSSm0m+CsRJX9rAmm9p56BnzRQocyVfqD5UzNA=; h=From:To:Cc:Subject:Date:From; b=XZRvS2W0yDwJGkYRoGtIMBrCwJq8z7PVHuP4SrwWTsSj7js/eOHCF0+0VTnxbRk6u jbnqa3DX7i884VhLZ+Mx2FU+fLpvNjKr64y8f3BN+zucXHT1qVSseWLcyFsoMmnMd4 Ly41hAmEGdIMJH0Lv0SRXZvDLGxV0Cq2lusVcgA7f5SpxkTtisX5X9DZuadXumJVPK 0FcfdE0UYfQ4f8esGYigOANzfFQVrvhcBodrQAjwVdEKOUk5a8fQJ52wdYFKBFcUkH tziWh6WNVUJ4k8RUwH8hCtfhjHCN5sAzxeQ/tWuV+0umzz3Dr5G9x90eBrGLwg/PWX Xoa9n7tq/KBXQ== From: Miguel Ojeda To: Miguel Ojeda , Greg Kroah-Hartman , =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= , Todd Kjos , Christian Brauner , Carlos Llamas , Alice Ryhl , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Nick Desaulniers , Bill Wendling , Justin Stitt , llvm@lists.linux.dev, linux-kbuild@vger.kernel.org, Aaron Ballman , Bill Wendling , Cole Nixon , Connor Kuehl , Fangrui Song , James Foster , Jeff Takahashi , Jordan Cantrell , Matthew Maurer , Nikk Forbus , Qing Zhao , Sami Tolvanen , Tim Pugh , Kees Cook Subject: [PATCH v2] rust: allow Clang-native `RANDSTRUCT` configs Date: Mon, 23 Mar 2026 14:02:24 +0100 Message-ID: <20260323130224.165738-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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, pub a: ::std::option::Option, } pub struct S4 { pub a: ::std::option::Option, pub b: ::std::option::Option, } 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 Cc: Bill Wendling Cc: Cole Nixon Cc: Connor Kuehl Cc: Fangrui Song Cc: James Foster Cc: Jeff Takahashi Cc: Jordan Cantrell Cc: Justin Stitt Cc: Matthew Maurer Cc: Nathan Chancellor Cc: Nicolas Schier Cc: Nikk Forbus Cc: Qing Zhao Cc: Sami Tolvanen Cc: Tim Pugh 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 Tested-by: Andreas Hindborg Link: https://patch.msgid.link/20241119185747.862544-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- 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