rust: add SRCU abstraction
Subsystems (4)
Onur Özkan
maintainerv10 cover rust: add SRCU abstraction
The immediate motivation is the Tyr reset infrastructure [1] which needs to serialize reset sensitive hardware access against reset and teardown paths. That reset series started to require many independent dependencies so this SRCU support is split out as a standalone Rust API to keep the reset series focused on the reset logic and easier to review, rebase and land.
Changes since v9:
- SRCU initialization restructuring is now a separate patch (the first one in this series).
- Comment fix on C initialization functions.
Changes since v8:
- Refactor srcu init handling to provide better init API (motivated by the "mutex.h" initializers.
- Use warn_on! instead of pr_warn! on leaked guard detection.
Changes since v7:
- Moved synchronize_srcu() call inside "if srcu_readers_active()" condition in "impl PinnedDrop for Srcu".
- Improved comments on the synchronize_srcu() call in "impl PinnedDrop for Srcu".
Changes since v6:
- Removed "CONFIG_DEBUG_LOCK_ALLOC" condition from "rust/helpers/srcu.c" and created a simple wrapper inside "include/linux/srcu.h" for it.
Changes since v5:
- Created separate srcu_readers_active() variants for "srcutiny.h" and "srcutree.h".
Changes since v4:
- Exposed srcu_readers_active from C side and wired it to the Rust helpers.
- Used srcu_readers_active() in SRCU drop and logged with pr_warn if there are leaked guards during the drop.
Changes since v3 (which are for Sashiko notes [2]):
- Added rust helpers for srcu_barrier() and synchronize_srcu_expedited() so the abstraction builds with CONFIG_TINY_SRCU, where these are static inline functions.
- Added missing INVARIANT comment in Srcu::new() about why the type invariants hold after successful initialization.
Changes since v2:
- Removed closure-based API.
- Added #[doc(hidden)] on new_srcu macro.
- Added #[must_use..] on srcu::Guard.
- Improved the clean-up path (PinnedDrop implementation) which eventually made read_lock safe with leaked guards.
Changes since v1:
- Made the owned SRCU read-side guard API unsafe and added a safe closure based helper for callers that do not need to keep the guard. This is to avoid UB on the C side cleanup_srcu_struct where the SRCU struct is freed while there are still active guards, which can happen if the caller leaks the guard e.g., with mem::forget().
- Improved doc comments.
v1: https://lore.kernel.org/all/[email protected]
v2: https://lore.kernel.org/all/[email protected]
v3: https://lore.kernel.org/all/[email protected]
v4: https://lore.kernel.org/all/[email protected]
v5: https://lore.kernel.org/all/[email protected]
v6: https://lore.kernel.org/all/[email protected]
v7: https://lore.kernel.org/all/[email protected]
v8: https://lore.kernel.org/all/[email protected]
v9: https://lore.kernel.org/all/[email protected]
[1]: https://lore.kernel.org/all/[email protected]
[2]: https://sashiko.dev/#/patchset/[email protected]?part=2
Onur Özkan (5):
srcu: make init_srcu_struct() consistently wrap __init_srcu_struct()
rust: helpers: add SRCU helpers
srcu: expose srcu_readers_active()
rust: sync: add SRCU abstraction
MAINTAINERS: add Rust SRCU files to SRCU entry
MAINTAINERS | 3 + include/linux/srcu.h | 29 ++++--- include/linux/srcutiny.h | 13 +++ include/linux/srcutree.h | 24 ++++++ kernel/rcu/srcutiny.c | 14 ++-- kernel/rcu/srcutree.c | 36 ++------- rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 35 ++++++++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++ 10 files changed, 282 insertions(+), 46 deletions(-) create mode 100644 rust/helpers/srcu.c create mode 100644 rust/kernel/sync/srcu.rs
Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v10 1/5 srcu: make init_srcu_struct() consistently wrap __init_srcu_struct()
Restructure the SRCU initialization functions so it always follows one direction:
init_srcu_struct() -> __init_srcu_struct() -> lockdep or generic
This uses the same wrapper style as mutex. It avoids the old confusing style where init_srcu_struct() and __init_srcu_struct() called each other in different configs. It also helps Rust side to have simpler helper for SRCU initialization.
--- include/linux/srcu.h | 29 ++++++++++++++++++++--------- kernel/rcu/srcutiny.c | 12 ++++++------ kernel/rcu/srcutree.c | 11 ++++++----- 3 files changed, 32 insertions(+), 20 deletions(-)
include/linux/srcu.h +20 -9
@@ -25,20 +25,19 @@ context_lock_struct(srcu_struct, __reentrant_ctx_lock);#ifdef CONFIG_DEBUG_LOCK_ALLOC -int __init_srcu_struct (struct srcu_struct *ssp,const char *name,struct lock_class_key *key); +int init_srcu_struct_lockdep (struct srcu_struct *ssp,const char *name, +struct lock_class_key *key); +static inline int __init_srcu_struct (struct srcu_struct *ssp,const char *name, +struct lock_class_key *key) +{ +return init_srcu_struct_lockdep (ssp, name, key); +}#ifndef CONFIG_TINY_SRCU int __init_srcu_struct_fast (struct srcu_struct *ssp,const char *name,struct lock_class_key *key);int __init_srcu_struct_fast_updown (struct srcu_struct *ssp,const char *name,struct lock_class_key *key);#endif // #ifndef CONFIG_TINY_SRCU -#define init_srcu_struct (ssp) \ -({ \ - static struct lock_class_key __srcu_key; \ - \ - __init_srcu_struct((ssp), #ssp, &__srcu_key); \ -}) -#define init_srcu_struct_fast (ssp) \ ({ \ static struct lock_class_key __srcu_key; \
@@ -56,7 +55,12 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,#define __SRCU_DEP_MAP_INIT (srcu_name) .dep_map = { .name = #srcu_name },#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ -int init_srcu_struct (struct srcu_struct *ssp); +int init_srcu_struct_generic (struct srcu_struct *ssp); +static inline int __init_srcu_struct (struct srcu_struct *ssp,const char *name, +struct lock_class_key *key) +{ +return init_srcu_struct_generic (ssp); +}#ifndef CONFIG_TINY_SRCU int init_srcu_struct_fast (struct srcu_struct *ssp);int init_srcu_struct_fast_updown (struct srcu_struct *ssp);
@@ -65,6 +69,13 @@ int init_srcu_struct_fast_updown(struct srcu_struct *ssp);#define __SRCU_DEP_MAP_INIT (srcu_name)#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ +#define init_srcu_struct (ssp) \ +({ \ + static struct lock_class_key __srcu_key; \ + \ + __init_srcu_struct((ssp), #ssp, &__srcu_key); \ +}) +/* Values for SRCU Tree srcu_data ->srcu_reader_flavor, but also used by rcutorture. */ #define SRCU_READ_FLAVOR_NORMAL 0x1 // srcu_read_lock().#define SRCU_READ_FLAVOR_NMI 0x2 // srcu_read_lock_nmisafe().
kernel/rcu/srcutiny.c +6 -6
@@ -48,31 +48,31 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp)#ifdef CONFIG_DEBUG_LOCK_ALLOC -int __init_srcu_struct (struct srcu_struct *ssp,const char *name, -struct lock_class_key *key) +int init_srcu_struct_lockdep (struct srcu_struct *ssp,const char *name, +struct lock_class_key *key) {/* Don't re-initialize a lock while it is held. */ debug_check_no_locks_freed ((void *)ssp,sizeof (*ssp));lockdep_init_map (&ssp->dep_map, name, key,0 );return init_srcu_struct_fields (ssp); } -EXPORT_SYMBOL_GPL (__init_srcu_struct); +EXPORT_SYMBOL_GPL (init_srcu_struct_lockdep);#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ /* -* init_srcu_struct - initialize a sleep-RCU structure +* init_srcu_struct_generic - initialize a sleep-RCU structure * @ssp: structure to initialize. * * Must invoke this on a given srcu_struct before passing that srcu_struct * to any other function. Each srcu_struct represents a separate domain * of SRCU protection. */ -int init_srcu_struct (struct srcu_struct *ssp) +int init_srcu_struct_generic (struct srcu_struct *ssp) {return init_srcu_struct_fields (ssp); } -EXPORT_SYMBOL_GPL (init_srcu_struct); +EXPORT_SYMBOL_GPL (init_srcu_struct_generic);#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
kernel/rcu/srcutree.c +6 -5
@@ -266,12 +266,13 @@ __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lockreturn init_srcu_struct_fields (ssp, false); } -int __init_srcu_struct (struct srcu_struct *ssp,const char *name,struct lock_class_key *key) +int init_srcu_struct_lockdep (struct srcu_struct *ssp,const char *name, +struct lock_class_key *key) { ssp->srcu_reader_flavor =0 ;return __init_srcu_struct_common (ssp, name, key); } -EXPORT_SYMBOL_GPL (__init_srcu_struct); +EXPORT_SYMBOL_GPL (init_srcu_struct_lockdep);int __init_srcu_struct_fast (struct srcu_struct *ssp,const char *name,struct lock_class_key *key) {
@@ -291,7 +292,7 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ /** -* init_srcu_struct - initialize a sleep-RCU structure +* init_srcu_struct_generic - initialize a sleep-RCU structure * @ssp: structure to initialize. * * Use this in place of DEFINE_SRCU() and DEFINE_STATIC_SRCU()
@@ -301,12 +302,12 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);* to any other function. Each srcu_struct represents a separate domain * of SRCU protection. */ -int init_srcu_struct (struct srcu_struct *ssp) +int init_srcu_struct_generic (struct srcu_struct *ssp) { ssp->srcu_reader_flavor =0 ;return init_srcu_struct_fields (ssp, false); } -EXPORT_SYMBOL_GPL (init_srcu_struct); +EXPORT_SYMBOL_GPL (init_srcu_struct_generic);/** * init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure
Signature · 1 line
2.51.2
Re: [PATCH v10 1/5] srcu: make init_srcu_struct() consistently wrap __init_srcu_struct()
Quoted 11 lines · Onur Özkan
Restructure the SRCU initialization functions so it always follows one direction:
init_srcu_struct() -> __init_srcu_struct() -> lockdep or genericThis uses the same wrapper style as mutex. It avoids the old confusing style where
init_srcu_struct()and__init_srcu_struct()called each other in different configs. It also helps Rust side to have simpler helper for SRCU initialization.Signed-off-by Onur Özkan <[email protected]>
Quoted 5 lines
--- include/linux/srcu.h | 29 ++++++++++++++++++++--------- kernel/rcu/srcutiny.c | 12 ++++++------ kernel/rcu/srcutree.c | 11 ++++++----- 3 files changed, 32 insertions(+), 20 deletions(-)
Onur Özkan
maintainerPATCH v10 2/5 rust: helpers: add SRCU helpers
In reply to Onur ÖzkanAdd helper wrappers for SRCU functions that are exposed to Rust through generated bindings.
--- rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 rust/helpers/srcu.c
rust/helpers/helpers.c +1 -0
@@ -88,6 +88,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "srcu.c" #include "sync.c" #include "task.c" #include "time.c"
rust/helpers/srcu.c new file +30 -0
@@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/srcu.h> + +__rust_helper int rust_helper_init_srcu_struct_with_key(struct srcu_struct *ssp, +const char *name, +struct lock_class_key *key) +{ +return __init_srcu_struct (ssp, name, key); +} + +__rust_helper int rust_helper_srcu_read_lock(struct srcu_struct *ssp) +{ +return srcu_read_lock (ssp); +} + +__rust_helper void rust_helper_srcu_read_unlock(struct srcu_struct *ssp, int idx) +{ +srcu_read_unlock (ssp, idx); +} + +__rust_helper void rust_helper_srcu_barrier(struct srcu_struct *ssp) +{ +srcu_barrier (ssp); +} + +__rust_helper void rust_helper_synchronize_srcu_expedited(struct srcu_struct *ssp) +{ +synchronize_srcu_expedited (ssp); +}
Signature · 1 line
2.51.2
Re: [PATCH v10 2/5] rust: helpers: add SRCU helpers
Quoted 4 lines · Onur Özkan
Add helper wrappers for SRCU functions that are exposed to Rust through generated bindings.
Signed-off-by Onur Özkan <[email protected]>
Quoted 5 lines
--- rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 rust/helpers/srcu.c
Onur Özkan
maintainerPATCH v10 3/5 srcu: expose srcu_readers_active()
In reply to Onur ÖzkanThis is needed by rust/helpers/srcu.c which now adds rust_helper_srcu_readers_active() as a wrapper around the SRCU helper for Rust callers.
To achive this:
1- Move the srcu_readers_active() implementation from
"kernel/rcu/srcutree.c" to "include/linux/srcutree.h".
2- Implement a matching srcu_readers_active() in
"include/linux/srcutiny.h" and use it on the existing open-coded WARN_ON() check in cleanup_srcu_struct().
--- include/linux/srcutiny.h | 13 +++++++++++++ include/linux/srcutree.h | 24 ++++++++++++++++++++++++ kernel/rcu/srcutiny.c | 2 +- kernel/rcu/srcutree.c | 25 ------------------------- rust/helpers/srcu.c | 5 +++++ 5 files changed, 43 insertions(+), 26 deletions(-)
include/linux/srcutiny.h +13 -0
@@ -154,4 +154,17 @@ static inline void srcu_torture_stats_print(struct srcu_struct *ssp, data_race(READ_ONCE(ssp->srcu_idx_max))); } +/** +* srcu_readers_active - returns true if there are readers. and false otherwise. +* @ssp: which srcu_struct to count active readers (holding srcu_read_lock). +* +* Note that this is not an atomic primitive, and can therefore suffer +* severe errors when invoked on an active srcu_struct. That said, it +* can be useful as an error check at cleanup time. +*/ +static inline bool srcu_readers_active (struct srcu_struct *ssp) +{ +return READ_ONCE (ssp->srcu_lock_nesting[0 ]) ||READ_ONCE (ssp->srcu_lock_nesting[1 ]); +} +#endif
include/linux/srcutree.h +24 -0
@@ -374,4 +374,28 @@ static inline void srcu_check_read_flavor(struct srcu_struct *ssp, int read_flav __srcu_check_read_flavor(ssp, read_flavor); } +/** +* srcu_readers_active - returns true if there are readers. and false otherwise. +* @ssp: which srcu_struct to count active readers (holding srcu_read_lock). +* +* Note that this is not an atomic primitive, and can therefore suffer +* severe errors when invoked on an active srcu_struct. That said, it +* can be useful as an error check at cleanup time. +*/ +static inline bool srcu_readers_active (struct srcu_struct *ssp) +{ +int cpu; +unsigned long sum =0 ; + +for_each_possible_cpu (cpu) { +struct srcu_data *sdp =per_cpu_ptr (ssp->sda, cpu); + + sum +=atomic_long_read (&sdp->srcu_ctrs[0 ].srcu_locks); + sum +=atomic_long_read (&sdp->srcu_ctrs[1 ].srcu_locks); + sum -=atomic_long_read (&sdp->srcu_ctrs[0 ].srcu_unlocks); + sum -=atomic_long_read (&sdp->srcu_ctrs[1 ].srcu_unlocks); + } +return sum; +} +#endif
kernel/rcu/srcutiny.c +1 -1
@@ -85,7 +85,7 @@ EXPORT_SYMBOL_GPL(init_srcu_struct_generic);*/ void cleanup_srcu_struct (struct srcu_struct *ssp) { -WARN_ON (ssp->srcu_lock_nesting[0 ] || ssp->srcu_lock_nesting[1 ]); +WARN_ON (srcu_readers_active (ssp));irq_work_sync (&ssp->srcu_irq_work);flush_work (&ssp->srcu_work);WARN_ON (ssp->srcu_gp_running);
kernel/rcu/srcutree.c +0 -25
@@ -599,31 +599,6 @@ static bool srcu_readers_active_idx_check(struct srcu_struct *ssp, int idx)return srcu_readers_lock_idx (ssp, idx, did_gp, unlocks); } -/** -* srcu_readers_active - returns true if there are readers. and false -* otherwise -* @ssp: which srcu_struct to count active readers (holding srcu_read_lock). -* -* Note that this is not an atomic primitive, and can therefore suffer -* severe errors when invoked on an active srcu_struct. That said, it -* can be useful as an error check at cleanup time. -*/ -static bool srcu_readers_active (struct srcu_struct *ssp) -{ -int cpu; -unsigned long sum =0 ; - -for_each_possible_cpu (cpu) { -struct srcu_data *sdp =per_cpu_ptr (ssp->sda, cpu); - - sum +=atomic_long_read (&sdp->srcu_ctrs[0 ].srcu_locks); - sum +=atomic_long_read (&sdp->srcu_ctrs[1 ].srcu_locks); - sum -=atomic_long_read (&sdp->srcu_ctrs[0 ].srcu_unlocks); - sum -=atomic_long_read (&sdp->srcu_ctrs[1 ].srcu_unlocks); - } -return sum; -} -/* * We use an adaptive strategy for synchronize_srcu() and especially for * synchronize_srcu_expedited(). We spin for a fixed time period
rust/helpers/srcu.c +5 -0
@@ -9,6 +9,11 @@ __rust_helper int rust_helper_init_srcu_struct_with_key(struct srcu_struct *ssp,return __init_srcu_struct (ssp, name, key); } +__rust_helper bool rust_helper_srcu_readers_active(struct srcu_struct *ssp) +{ +return srcu_readers_active (ssp); +} + __rust_helper int rust_helper_srcu_read_lock(struct srcu_struct *ssp) {return srcu_read_lock (ssp);
Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v10 4/5 rust: sync: add SRCU abstraction
In reply to Onur ÖzkanAdd a Rust abstraction for sleepable RCU (SRCU), backed by C srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.
Cleanup is handled via PinnedDrop. It first checks for active read-side sections and emits a warning if any guards were leaked. In that case, it waits in synchronize_srcu() rather than risking a UAF by freeing the srcu_struct that is still reachable from the C side. It then uses srcu_barrier() to drain pending callbacks before finally calling cleanup_srcu_struct().
--- rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 rust/kernel/sync/srcu.rs
rust/kernel/sync.rs +2 -0
@@ -21,6 +21,7 @@pub mod rcu;mod refcount;mod set_once; +pub mod srcu;pub use arc::{Arc ,ArcBorrow ,UniqueArc };pub use completion::Completion ;
@@ -31,6 +32,7 @@pub use locked_by::LockedBy ;pub use refcount::Refcount ;pub use set_once::SetOnce ; +pub use srcu::Srcu ;/// Represents a lockdep class. ///
rust/kernel/sync/srcu.rs new file +171 -0
171 lines not on this page · raw message
Signature · 1 line
2.51.2
Alice Ryhl
maintainerRe: [PATCH v10 4/5] rust: sync: add SRCU abstraction
Quoted 21 lines · Onur Özkan
rust/kernel/sync/srcu.rs new file +21
-0+#[pinned_drop] +impl PinnedDrop for Srcu { +fn drop (self:Pin <&mut Self >) { +let ptr = self.inner.get (); + +if crate ::warn_on!( +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct` +// and `srcu_readers_active()` only checks the active reader count. +unsafe { bindings::srcu_readers_active (ptr) } + ) { +// `cleanup_srcu_struct()` may return early if there are still active readers. +// This should only happen if a guard was leaked with `mem::forget`, which is +// "WRONG" code and may cause a UAF because Rust will free the `srcu_struct` +// while it is still referenced from the C side (e.g. by `call_srcu()` callbacks). +// +// Another consequence of leaking guards is that `call_srcu()` callbacks will +// never run because the grace period can never complete due to permanently +// active readers (i.e. leaked guards). +// +// If this ever happens, that means the guard was leaked by mistake and the +// caller must fix the bug. Sleeping here is intentional and less harmful
rust/kernel/sync/srcu.rs line 131
Quoted 10 lines
rust/kernel/sync/srcu.rs new file +10
-0+// than risking a UAF. +// +// SAFETY: By the type invariants, `self` contains a valid and pinned +// `struct srcu_struct`. +unsafe { bindings::synchronize_srcu (ptr) }; + } + +// Ensure all SRCU callbacks have been finished before freeing. +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::srcu_barrier (ptr) };
Hmm. It's not entirely clear to me that synchronize_srcu() is needed here. If there are calls to srcu_read_lock() that do not have a matching unlock due to use of mem::forget(), then either there is a pending call_srcu() callback in the queue, in which case srcu_barrier() already sleeps forever, or there are no such callbacks in which case I don't think this actually leads to UAF.
Thoughts?
Alice
Gary Guo
maintainerRe: [PATCH v10 4/5] rust: sync: add SRCU abstraction
Quoted 40 lines · Alice Ryhl
Quoted 21 lines · Onur Özkan
rust/kernel/sync/srcu.rs new file +21
-0+#[pinned_drop] +impl PinnedDrop for Srcu { +fn drop (self:Pin <&mut Self >) { +let ptr = self.inner.get (); + +if crate ::warn_on!( +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct` +// and `srcu_readers_active()` only checks the active reader count. +unsafe { bindings::srcu_readers_active (ptr) } + ) { +// `cleanup_srcu_struct()` may return early if there are still active readers. +// This should only happen if a guard was leaked with `mem::forget`, which is +// "WRONG" code and may cause a UAF because Rust will free the `srcu_struct` +// while it is still referenced from the C side (e.g. by `call_srcu()` callbacks). +// +// Another consequence of leaking guards is that `call_srcu()` callbacks will +// never run because the grace period can never complete due to permanently +// active readers (i.e. leaked guards). +// +// If this ever happens, that means the guard was leaked by mistake and the +// caller must fix the bug. Sleeping here is intentional and less harmful rust/kernel/sync/srcu.rs line 131
Quoted 10 lines
rust/kernel/sync/srcu.rs new file +10
-0+// than risking a UAF. +// +// SAFETY: By the type invariants, `self` contains a valid and pinned +// `struct srcu_struct`. +unsafe { bindings::synchronize_srcu (ptr) }; + } + +// Ensure all SRCU callbacks have been finished before freeing. +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::srcu_barrier (ptr) };Hmm. It's not entirely clear to me that
synchronize_srcu()is needed here. If there are calls tosrcu_read_lock()that do not have a matching unlock due to use ofmem::forget(), then either there is a pendingcall_srcu()callback in the queue, in which casesrcu_barrier()already sleeps forever, or there are no such callbacks in which case I don't think this actually leads to UAF.
Quoted 1 line
Thoughts?
If srcu_readers_active returns true, then cleanup_srcu_struct will hit one of the "leak it" code path, and the question is whether that is okay.
My analysis in https://lore.kernel.org/all/[email protected]/:
Quoted 11 lines
But after taking another look, I am not even sure if this is needed. A quick glance of the code it appears that
__srcu_read_unlockdoesn't do anything apart from adjusting the counter, and the SRCU grace period and thus the timers won't actually start unless there's a pending grace period, which won't start unless there's acall_srcuorsychronize_srcu. And we *know* that none of them would happen, as the lifetime guarantees that nothing accesses theSrcustruct whendropstarts, and inside drop we have already invokedsrcu_barrier().So I think, even if we hit the "Just leak it" scenario, we can still safely deallocate the backing storage of
srcu_structand nothing should break?
So I _think_ it is not needed, but this is quite heavily related to internals of SRCU implementation.
Best,
Gary
Onur Özkan
maintainerRe: [PATCH v10 4/5] rust: sync: add SRCU abstraction
Quoted 56 lines · On Mon, 06 Jul 2026 13:39:47 +0100 Gary Guo <[email protected]> wrote:
Quoted 40 lines · Alice Ryhl
Quoted 21 lines · Onur Özkan
rust/kernel/sync/srcu.rs new file +21
-0+#[pinned_drop] +impl PinnedDrop for Srcu { +fn drop (self:Pin <&mut Self >) { +let ptr = self.inner.get (); + +if crate ::warn_on!( +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct` +// and `srcu_readers_active()` only checks the active reader count. +unsafe { bindings::srcu_readers_active (ptr) } + ) { +// `cleanup_srcu_struct()` may return early if there are still active readers. +// This should only happen if a guard was leaked with `mem::forget`, which is +// "WRONG" code and may cause a UAF because Rust will free the `srcu_struct` +// while it is still referenced from the C side (e.g. by `call_srcu()` callbacks). +// +// Another consequence of leaking guards is that `call_srcu()` callbacks will +// never run because the grace period can never complete due to permanently +// active readers (i.e. leaked guards). +// +// If this ever happens, that means the guard was leaked by mistake and the +// caller must fix the bug. Sleeping here is intentional and less harmful rust/kernel/sync/srcu.rs line 131
Quoted 10 lines
rust/kernel/sync/srcu.rs new file +10
-0+// than risking a UAF. +// +// SAFETY: By the type invariants, `self` contains a valid and pinned +// `struct srcu_struct`. +unsafe { bindings::synchronize_srcu (ptr) }; + } + +// Ensure all SRCU callbacks have been finished before freeing. +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::srcu_barrier (ptr) };Hmm. It's not entirely clear to me that
synchronize_srcu()is needed here. If there are calls tosrcu_read_lock()that do not have a matching unlock due to use ofmem::forget(), then either there is a pendingcall_srcu()callback in the queue, in which casesrcu_barrier()already sleeps forever, or there are no such callbacks in which case I don't think this actually leads to UAF.Quoted 1 line
Thoughts?
If
srcu_readers_activereturns true, thencleanup_srcu_structwill hit one of the "leak it" code path, and the question is whether that is okay.My analysis in https://lore.kernel.org/all/[email protected]/:
Quoted 9 lines
But after taking another look, I am not even sure if this is needed. A quick glance of the code it appears that
__srcu_read_unlockdoesn't do anything apart from adjusting the counter, and the SRCU grace period and thus the timers won't actually start unless there's a pending grace period, which won't start unless there's acall_srcuorsychronize_srcu. And we *know* that none of them would happen, as the lifetime guarantees that nothing accesses theSrcustruct whendropstarts, and inside drop we have already invokedsrcu_barrier().
Quoted 5 lines
Quoted 2 lines
So I think, even if we hit the "Just leak it" scenario, we can still safely deallocate the backing storage of
srcu_structand nothing should break?So I _think_ it is not needed, but this is quite heavily related to internals of SRCU implementation.
So when cleanup_srcu_struct() hits "leak it" path, I wanted to make sure nothing can access to that freed srcu_struct and the easiest and safest approach for it is calling synchronize_srcu().
Callbacks are just a detail that can become problem after this leak, so relying on srcu_barrier() to handle the "leak it" case doesn't sound like a good solution to me. It does not make the active reader case safe by itself and honestly we don't really lose anything with calling synchronize_srcu() as an additional safety guard, so I just kept it that way..
Thanks,
Onur
Quoted 3 lines
Best,
Gary
Re: [PATCH v10 4/5] rust: sync: add SRCU abstraction
In reply to Onur ÖzkanQuoted 4 lines · Onur Özkan
Add a Rust abstraction for sleepable RCU (SRCU), backed by C
srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.
Quoted 8 lines
Cleanup is handled via
PinnedDrop. It first checks for active read-side sections and emits a warning if any guards were leaked. In that case, it waits insynchronize_srcu()rather than risking a UAF by freeing thesrcu_structthat is still reachable from the C side. It then usessrcu_barrier()to drain pending callbacks before finally callingcleanup_srcu_struct().Signed-off-by Onur Özkan <[email protected]>
Quoted 5 lines
--- rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 rust/kernel/sync/srcu.rs
Onur Özkan
maintainerPATCH v10 5/5 MAINTAINERS: add Rust SRCU files to SRCU entry
In reply to Onur ÖzkanInclude Rust side implementation files to the SRCU maintainer entry.
--- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+)
MAINTAINERS +3 -0
@@ -24624,6 +24624,7 @@ SLEEPABLE READ-COPY UPDATE (SRCU) M: Lai Jiangshan <[email protected]> M: "Paul E. McKenney" <[email protected]> M: Josh Triplett <[email protected]> +M: Onur Özkan <[email protected]> (RUST) R: Steven Rostedt <[email protected]> R: Mathieu Desnoyers <[email protected]> L: [email protected]
@@ -24632,6 +24633,8 @@ W: http://www.rdrop.com/users/paulmck/RCU/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev F: include/linux/srcu*.h F: kernel/rcu/srcu*.c +F: rust/helpers/srcu.c +F: rust/kernel/sync/srcu.rs SMACK SECURITY MODULE M: Casey Schaufler <[email protected]>
Signature · 1 line
2.51.2
Onur Özkan
maintainerQuoted 98 lines · On Sat, 13 Jun 2026 09:40:06 +0300 Onur Özkan <[email protected]> wrote:
The immediate motivation is the Tyr reset infrastructure [1] which needs to serialize reset sensitive hardware access against reset and teardown paths. That reset series started to require many independent dependencies so this SRCU support is split out as a standalone Rust API to keep the reset series focused on the reset logic and easier to review, rebase and land.
Changes since v9:
- SRCU initialization restructuring is now a separate patch (the first one in this series).
- Comment fix on C initialization functions.Changes since v8:
- Refactor srcu init handling to provide better init API (motivated by the "
mutex.h" initializers.
- Usewarn_on! instead ofpr_warn! on leaked guard detection.Changes since v7:
- Moved
synchronize_srcu()call inside "ifsrcu_readers_active()" condition in "impl PinnedDrop for Srcu".
- Improved comments on thesynchronize_srcu()call in "impl PinnedDrop for Srcu".Changes since v6:
- Removed "
CONFIG_DEBUG_LOCK_ALLOC" condition from "rust/helpers/srcu.c" and created a simple wrapper inside "include/linux/srcu.h" for it.Changes since v5:
- Created separate
srcu_readers_active()variants for "srcutiny.h" and "srcutree.h".Changes since v4:
- Exposed
srcu_readers_activefrom C side and wired it to the Rust helpers.
- Usedsrcu_readers_active()in SRCU drop and logged withpr_warnif there are leaked guards during the drop.Changes since v3 (which are for Sashiko notes [2]):
- Added rust helpers for
srcu_barrier()andsynchronize_srcu_expedited()so the abstraction builds withCONFIG_TINY_SRCU, where these are static inline functions.
- Added missing INVARIANT comment inSrcu::new()about why the type invariants hold after successful initialization.Changes since v2:
- Removed closure-based API.
- Added #[doc(hidden)] onnew_srcumacro.
- Added #[must_use..] onsrcu::Guard.
- Improved the clean-up path (PinnedDrop implementation) which eventually maderead_locksafe with leaked guards.Changes since v1:
- Made the owned SRCU read-side guard API unsafe and added a safe closure based helper for callers that do not need to keep the guard. This is to avoid UB on the C side
cleanup_srcu_structwhere the SRCU struct is freed while there are still active guards, which can happen if the caller leaks the guard e.g., withmem::forget().
- Improved doc comments.v1: https://lore.kernel.org/all/[email protected]
v2: https://lore.kernel.org/all/[email protected]
v3: https://lore.kernel.org/all/[email protected]
v4: https://lore.kernel.org/all/[email protected]
v5: https://lore.kernel.org/all/[email protected]
v6: https://lore.kernel.org/all/[email protected]
v7: https://lore.kernel.org/all/[email protected]
v8: https://lore.kernel.org/all/[email protected]
v9: https://lore.kernel.org/all/[email protected][1]: https://lore.kernel.org/all/[email protected]
[2]: https://sashiko.dev/#/patchset/[email protected]?part=2Onur Özkan (5):
srcu: makeinit_srcu_struct()consistently wrap__init_srcu_struct()
rust: helpers: add SRCU helpers
srcu: exposesrcu_readers_active()
rust: sync: add SRCU abstraction
MAINTAINERS: add Rust SRCU files to SRCU entry
Quoted 17 lines
MAINTAINERS | 3 + include/linux/srcu.h | 29 ++++--- include/linux/srcutiny.h | 13 +++ include/linux/srcutree.h | 24 ++++++ kernel/rcu/srcutiny.c | 14 ++-- kernel/rcu/srcutree.c | 36 ++------- rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 35 ++++++++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++ 10 files changed, 282 insertions(+), 46 deletions(-) create mode 100644 rust/helpers/srcu.c create mode 100644 rust/kernel/sync/srcu.rsSignature · 1 line
2.51.2
A gentle ping on this series. Are there any remaining concerns with v10?
Regards,
Onur
Quoted 6 lines · Onur Özkan
The immediate motivation is the Tyr reset infrastructure [1] which needs to serialize reset sensitive hardware access against reset and teardown paths. That reset series started to require many independent dependencies so this SRCU support is split out as a standalone Rust API to keep the reset series focused on the reset logic and easier to review, rebase and land.
Quoted 7 lines · Onur Özkan
The immediate motivation is the Tyr reset infrastructure [1] which needs to serialize reset sensitive hardware access against reset and teardown paths. That reset series started to require many independent dependencies so this SRCU support is split out as a standalone Rust API to keep the reset series focused on the reset logic and easier to review, rebase and land.
[...]
Thank you all!
Paul, per our previous discussion, I think this series should into rcu tree, since it changes a significant part of SRCU C code. Thanks!
Regards,
Boqun
Paul E. McKenney
maintainerQuoted 8 lines · Boqun Feng
Quoted 7 lines · Onur Özkan
The immediate motivation is the Tyr reset infrastructure [1] which needs to serialize reset sensitive hardware access against reset and teardown paths. That reset series started to require many independent dependencies so this SRCU support is split out as a standalone Rust API to keep the reset series focused on the reset logic and easier to review, rebase and land.
Quoted 8 lines
[...]
Thank you all!
Reviewed-by Boqun Feng <[email protected]>Paul, per our previous discussion, I think this series should into rcu tree, since it changes a significant part of SRCU C code. Thanks!
As Boqun said, thank you all!
I have queued these, with a little luck for the upcoming merge window.
Thanx, paul