From: Ye Liu <liuye@kylinos.cn>
Introduce for_each_process_rculock(), for_each_thread_rculock() and
for_each_process_thread_rculock() macros that combine the existing
iteration macros with scoped_guard(rcu), so that the RCU read lock
is automatically acquired before iteration and released when the
loop exits — including via break, goto, or return.
The rest of the series converts manual rcu_read_lock()/
rcu_read_unlock() and guard(rcu)() pairs across mm/, kernel/, fs/,
lib/ and security/ to use the new macros.
Suggested by Michal Hocko for the oom_kill path [2].
Signed-off-by: Ye Liu <liuye@kylinos.cn>
---
Changes since v2 [3]:
- Split the kernel/ patch into per-subsystem patches, as the
combined patch could not be applied (Peter Zijlstra, Steven
Rostedt).
- Move changelog below the --- separator so it is excluded from
the commit message (Lorenzo Stoakes).
- Indent loop body in macro definitions to show scoped_guard
scope (Lorenzo Stoakes).
Changes since v1 [1]:
- Rename macros from *_rcu to *_rculock, as suggested by Steven
Rostedt and acked by Thomas Gleixner, to avoid confusion with
existing *_rcu() list iterators that expect the caller to hold
the RCU read lock.
- Improve the comment on for_each_process_thread_rculock() to
document that 'break' only exits the inner loop and 'goto' is
needed to exit both loops (Thomas Gleixner).
- Rename the stale 'unlock:' label to 'out:' in hung_task.c, as
noted by Günther Noack.
- Clarify in patch 4 that page_pgoff() is safe outside the RCU
read-side critical section (SJ Park).
- CC all relevant maintainers on every patch (Lorenzo Stoakes).
- Drop the mm: prefix from patch 1, as the macros are in
include/linux/sched/signal.h (Michal Hocko).
[1] https://lore.kernel.org/all/20260904083001.553587-1-ye.liu@linux.dev/
[2] https://lore.kernel.org/all/20260813092933.562028-1-ye.liu@linux.dev/
[3] https://lore.kernel.org/all/20260907081334.1152889-1-ye.liu@linux.dev/
Ye Liu (15):
sched: introduce for_each_process_rculock and for_each_thread_rculock
mm/oom_kill: convert process/thread iterators to for_each_*_rculock
mm/ksm: convert process iterator to for_each_process_rculock
mm/memory-failure: convert process iterator to for_each_process_rculock
cpu/hotplug: convert thread iterator to for_each_thread_rculock
freezer: convert thread iterator to for_each_thread_rculock
hung_task: convert process/thread iterators to for_each_*_rculock
locking/lockdep: convert process/thread iterators to for_each_*_rculock
rcu: convert process/thread iterator to for_each_process_thread_rculock
sched: convert process/thread iterators to for_each_*_rculock
tracing/fgraph: convert process/thread iterator to for_each_process_thread_rculock
unwind: convert process/thread iterator to for_each_process_thread_rculock
fs: convert process/thread iterators to for_each_*_rculock
lib: convert process iterator to for_each_process_rculock
security/landlock: convert thread iterator to for_each_thread_rculock
fs/proc/base.c | 4 +---
fs/resctrl/rdtgroup.c | 8 ++------
include/linux/sched/signal.h | 26 ++++++++++++++++++++++++++
kernel/cpu.c | 4 +---
kernel/freezer.c | 4 +---
kernel/hung_task.c | 11 ++++-------
kernel/locking/lockdep.c | 4 +---
kernel/rcu/update.c | 4 +---
kernel/sched/core.c | 3 +--
kernel/sched/debug.c | 4 +---
kernel/trace/fgraph.c | 8 ++------
kernel/unwind/deferred.c | 3 +--
lib/is_single_threaded.c | 5 +----
mm/ksm.c | 4 +---
mm/memory-failure.c | 16 ++++------------
mm/oom_kill.c | 20 +++++---------------
security/landlock/tsync.c | 8 ++------
17 files changed, 55 insertions(+), 81 deletions(-)
--
2.25.1
From: Ye Liu <liuye@kylinos.cn>
Introduce for_each_process_rculock(), for_each_thread_rculock() and
for_each_process_thread_rculock() macros that acquire the RCU read
lock before the iteration starts and release it when the loop is left,
so that the RCU read-side critical section is scoped to the loop body
instead of an externally managed rcu_read_lock()/rcu_read_unlock()
pair.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Changes in v3:
- Indent loop body to show scoped_guard scope (Lorenzo Stoakes)
Changes in v2:
- Rename macros from *_rcu to *_rculock (Steven Rostedt, Thomas Gleixner)
- Improve comment on for_each_process_thread_rculock() double loop
- Change prefix from mm: to sched: (Michal Hocko)
include/linux/sched/signal.h | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pairs wrapping
for_each_process()/for_each_thread() loops with for_each_process_rculock()
and for_each_thread_rculock(), which scope the RCU read lock to the
loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Changes in v2:
- Rename *_rcu to *_rculock
mm/oom_kill.c | 20 +++++---------------
1 file changed, 5 insertions(+), 15 deletions(-)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_process() loop in mm/ksm.c with for_each_process_rculock(),
which scopes the RCU read lock to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Changes in v2:
- Rename *_rcu to *_rculock
mm/ksm.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pairs combined
with for_each_process() loop in mm/memory-failure.c with
for_each_process_rculock(), which scopes the RCU read lock to the
loop body via scoped_guard(rcu).
In collect_procs_file(), the page_pgoff() call now falls outside the
RCU read-side critical section. This is safe because page_pgoff()
only reads folio->index and does not traverse any RCU-protected
structures.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Acked-by: Miaohe Lin <linmiaohe@huawei.com>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
---
Changes in v2:
- Rename *_rcu to *_rculock
- Clarify page_pgoff() safety outside RCU lock (SJ Park)
mm/memory-failure.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_thread() loop in kernel/cpu.c with for_each_thread_rculock(),
which scopes the RCU read lock to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
---
Changes in v3:
- Split from kernel/ patch in v2 (Peter Zijlstra, Steven Rostedt)
Changes in v2:
- Rename *_rcu to *_rculock
kernel/cpu.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
@@ -1268,7 +1267,6 @@ void clear_tasks_mm_cpumask(int cpu)arch_clear_mm_cpumask_cpu(cpu,t->mm);task_unlock(t);}-rcu_read_unlock();}/* Take this CPU down. */
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_thread() loop in kernel/freezer.c with
for_each_thread_rculock(), which scopes the RCU read lock to the
loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
---
Changes in v3:
- Split from kernel/ patch in v2 (Peter Zijlstra, Steven Rostedt)
Changes in v2:
- Rename *_rcu to *_rculock
kernel/freezer.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_process_thread() loop in kernel/hung_task.c with
for_each_process_thread_rculock(), which scopes the RCU read lock
to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
---
Changes in v3:
- Split from kernel/ patch in v2 (Peter Zijlstra, Steven Rostedt)
Changes in v2:
- Rename *_rcu to *_rculock
- Rename stale 'unlock:' label to 'out:' in hung_task.c (Günther Noack)
kernel/hung_task.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_process_thread() loop in kernel/locking/lockdep.c with
for_each_process_thread_rculock(), which scopes the RCU read lock
to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
---
Changes in v3:
- Split from kernel/ patch in v2 (Peter Zijlstra, Steven Rostedt)
Changes in v2:
- Rename *_rcu to *_rculock
kernel/locking/lockdep.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
@@ -6834,15 +6834,13 @@ void debug_show_all_locks(void)}pr_warn("\nShowing all locks held in the system:\n");-rcu_read_lock();-for_each_process_thread(g,p){+for_each_process_thread_rculock(g,p){if(!p->lockdep_depth)continue;lockdep_print_held_locks(p);touch_nmi_watchdog();touch_all_softlockup_watchdogs();}-rcu_read_unlock();pr_warn("\n");pr_warn("=============================================\n\n");
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_process_thread() loop in kernel/rcu/update.c with
for_each_process_thread_rculock(), which scopes the RCU read lock
to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
---
Changes in v3:
- Split from kernel/ patch in v2 (Peter Zijlstra, Steven Rostedt)
Changes in v2:
- Rename *_rcu to *_rculock
kernel/rcu/update.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
@@ -548,15 +548,13 @@ void synchronize_rcu_trivial_preempt(void)structtask_struct*t;smp_mb();// Order prior accesses before grace-period start.-rcu_read_lock();// Protect task list.-for_each_process_thread(g,t){+for_each_process_thread_rculock(g,t){if(t==current)continue;// Don't deadlock on ourselves!// Order later rcu_read_lock() on other tasks after QS.while(smp_load_acquire(&t->rcu_trivial_preempt_nesting))continue;}-rcu_read_unlock();}EXPORT_SYMBOL_GPL(synchronize_rcu_trivial_preempt);#endif // #if IS_ENABLED(CONFIG_TRIVIAL_PREEMPT_RCU)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() and guard(rcu)
pairs combined with for_each_process_thread() loops in kernel/sched/
with for_each_process_thread_rculock(), which scopes the RCU read
lock to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
---
Changes in v3:
- Split from kernel/ patch in v2 (Peter Zijlstra, Steven Rostedt)
Changes in v2:
- Rename *_rcu to *_rculock
kernel/sched/core.c | 3 +--
kernel/sched/debug.c | 4 +---
2 files changed, 2 insertions(+), 5 deletions(-)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_process_thread() loop in kernel/trace/fgraph.c with
for_each_process_thread_rculock(), which scopes the RCU read lock
to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
---
Changes in v3:
- Split from kernel/ patch in v2 (Peter Zijlstra, Steven Rostedt)
Changes in v2:
- Rename *_rcu to *_rculock
kernel/trace/fgraph.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
@@ -1057,11 +1057,10 @@ static int alloc_retstack_tasklist(unsigned long **ret_stack_list)}}-rcu_read_lock();-for_each_process_thread(g,t){+for_each_process_thread_rculock(g,t){if(start==end){ret=-EAGAIN;-gotounlock;+gotofree;}if(t->ret_stack==NULL){
@@ -1074,9 +1073,6 @@ static int alloc_retstack_tasklist(unsigned long **ret_stack_list)t->ret_stack=ret_stack_list[start++];}}--unlock:-rcu_read_unlock();free:for(i=start;i<end;i++)kmem_cache_free(fgraph_stack_cachep,ret_stack_list[i]);
From: Ye Liu <liuye@kylinos.cn>
Replace guard(rcu)() combined with for_each_process_thread() loop in
kernel/unwind/deferred.c with for_each_process_thread_rculock(),
which scopes the RCU read lock to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
---
Changes in v3:
- Split from kernel/ patch in v2 (Peter Zijlstra, Steven Rostedt)
Changes in v2:
- Rename *_rcu to *_rculock
kernel/unwind/deferred.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
@@ -319,9 +319,8 @@ void unwind_deferred_cancel(struct unwind_work *work)synchronize_srcu(&unwind_srcu);-guard(rcu)();/* Clear this bit from all threads */-for_each_process_thread(g,t){+for_each_process_thread_rculock(g,t){atomic_long_andnot(BIT(bit),&t->unwind_info.unwind_mask);if(t->unwind_info.cache)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pairs combined
with for_each_process() and for_each_process_thread() loops in fs/
with the for_each_*_rculock() macros, which scope the RCU read lock
to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Changes in v2:
- Rename *_rcu to *_rculock
fs/proc/base.c | 4 +---
fs/resctrl/rdtgroup.c | 8 ++------
2 files changed, 3 insertions(+), 9 deletions(-)
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_process() loop in lib/ with for_each_process_rculock(),
which scopes the RCU read lock to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Reviewed-by: SJ Park <sj@kernel.org>
---
Changes in v2:
- Rename *_rcu to *_rculock
lib/is_single_threaded.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
From: Ye Liu <liuye@kylinos.cn>
Replace guard(rcu)() + for_each_thread() with for_each_thread_rculock(),
which scopes the RCU read lock to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Reviewed-by: Justin Suess <redacted>
Reviewed-by: Günther Noack <redacted>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Changes in v2:
- Rename *_rcu to *_rculock
security/landlock/tsync.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
@@ -335,9 +335,7 @@ static size_t count_additional_threads(const struct tsync_works *works)caller=current;-guard(rcu)();--for_each_thread(caller,thread){+for_each_thread_rculock(caller,thread){/* Skip current, since it is initiating the sync. */if(thread==caller)continue;
@@ -376,9 +374,7 @@ static bool schedule_task_work(struct tsync_works *works,caller=current;-guard(rcu)();--for_each_thread(caller,thread){+for_each_thread_rculock(caller,thread){/* Skip current, since it is initiating the sync. */if(thread==caller)continue;
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Mixing goto-based error handling and scope-based guard() cleanup in the same function.
--
commit 70fc208f5f44114137a3c935785ed1ab665ae0e0
Author: Ye Liu [off-list ref]
tracing/fgraph: convert process/thread iterator to for_each_process_thread_rculock
This commit replaces the manual rcu_read_lock() and rcu_read_unlock() pair
combined with the for_each_process_thread() loop in fgraph.c with the
for_each_process_thread_rculock() macro, which scopes the RCU read lock to
the loop body via scoped_guard(rcu).
@@ -1057,11 +1057,10 @@ static int alloc_retstack_tasklist(unsigned long **ret_stack_list) } }- rcu_read_lock();- for_each_process_thread(g, t) {+ for_each_process_thread_rculock(g, t) { if (start == end) { ret = -EAGAIN;- goto unlock;+ goto free;
[Severity: Low]
Does this mix scope-based cleanup with manual goto-based error handling?
By replacing the manual RCU lock with for_each_process_thread_rculock()
(which uses scoped_guard(rcu)), the function now relies on scope-based
cleanup for the RCU lock, but still jumps to a manual "goto free" label
for the memory cleanup below.
The cleanup guidelines state that for a given routine, we should convert all
resources that need a "goto" cleanup to scope-based cleanup, or convert none
of them, avoiding mixed usage in the same function.
}
if (t->ret_stack == NULL) {
[ ... ]
quoted hunk
@@ -1074,9 +1073,6 @@ static int alloc_retstack_tasklist(unsigned long **ret_stack_list) t->ret_stack = ret_stack_list[start++]; } }--unlock:- rcu_read_unlock(); free: for (i = start; i < end; i++) kmem_cache_free(fgraph_stack_cachep, ret_stack_list[i]);
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The commit message incorrectly claims to replace `for_each_thread()` with `for_each_thread_rculock()`, but the code actually replaces `for_each_process()` with `for_each_process_rculock()`.
--
commit 3ac98de6fbc4e97ba4981ef0c4941692c15a9309
Author: Ye Liu [off-list ref]
cpu/hotplug: convert thread iterator to for_each_thread_rculock
This commit replaces a manual rcu_read_lock() and rcu_read_unlock() pair
in clear_tasks_mm_cpumask() with a scoped RCU lock iterator macro.
cpu/hotplug: convert thread iterator to for_each_thread_rculock
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_thread() loop in kernel/cpu.c with for_each_thread_rculock(),
[Severity: Medium]
Is the commit message accurate regarding the iterator being replaced?
The commit message subject and body describe converting a thread iterator
using for_each_thread() to for_each_thread_rculock(). However, looking at
clear_tasks_mm_cpumask() in the diff:
The code actually replaces for_each_process() with for_each_process_rculock().
Could the commit message be updated to match the process iterator actually
used in the patch to avoid confusion during review or bisection?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260911075800.491472-1-ye.liu@linux.dev?part=5
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Potential NULL pointer dereference or Use-After-Free of `folio->mapping` in `collect_procs_file()` due to concurrent truncation.
--
commit 788d1f422c3f19026d9e0d86b550c238607720ed
Author: Ye Liu [off-list ref]
mm/memory-failure: convert process iterator to for_each_process_rculock
This commit replaces the manual rcu_read_lock()/rcu_read_unlock() pairs
combined with for_each_process() loop in mm/memory-failure.c with
for_each_process_rculock(), which scopes the RCU read lock to the loop body
via scoped_guard(rcu).
[Severity: High]
This is a pre-existing issue, but could this lead to a NULL pointer
dereference or a use-after-free of mapping due to a race with truncation?
In memory_failure(), the folio lock is dropped in hwpoison_user_mappings()
before calling collect_procs(). collect_procs() performs an unlocked check of
!folio->mapping:
static void collect_procs(...)
{
if (!folio->mapping)
return;
...
else
collect_procs_file(folio, page, tokill, force_early);
}
If a concurrent truncation removes the folio from the page cache immediately
after this check, folio->mapping becomes NULL. collect_procs_file() then
initializes mapping = folio->mapping and calls i_mmap_lock_read(mapping).
If the compiler performs a second read of folio->mapping, mapping will be
NULL, crashing the kernel on the semaphore acquisition. If the compiler
caches the first read, mapping will point to an address_space whose host
inode may have been concurrently freed, leading to a use-after-free since
the elevated page refcount does not pin the inode and no RCU read lock is
held during the blocking i_mmap_lock_read() call.
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Mixing `goto`-based error handling with scope-based cleanup helpers in `__set_oom_adj()`.
--
commit dca26f99f6fe0ae1bf52a29a296c82180cb53c70
Author: Ye Liu [off-list ref]
Subject: fs: convert process/thread iterators to for_each_*_rculock
This commit replaces the manual rcu_read_lock() and rcu_read_unlock() pairs
combined with for_each_process() and for_each_process_thread() loops in
the fs/ tree with the for_each_*_rculock() macros. These new macros scope
the RCU read lock to the loop body using scoped_guard().
@@ -1161,8 +1161,7 @@ static int __set_oom_adj(struct file *file, int oom_adj, bool legacy)if(mm){structtask_struct*p;-rcu_read_lock();-for_each_process(p){+for_each_process_rculock(p){
[Severity: Medium]
Does this code introduce a regression by mixing scope-based cleanup helpers
with goto-based error handling?
The newly introduced for_each_process_rculock() macro expands to use
scoped_guard(rcu). However, __set_oom_adj() still relies on goto-based
cleanup paths earlier in the function:
fs/proc/base.c:__set_oom_adj() {
// ...
mutex_lock(&oom_adj_mutex);
if (legacy) {
if (oom_adj < task->signal->oom_score_adj &&
!capable(CAP_SYS_RESOURCE)) {
err = -EACCES;
goto err_unlock;
}
// ...
err_unlock:
mutex_unlock(&oom_adj_mutex);
put_task_struct(task);
return err;
}
The kernel cleanup subsystem guidelines in include/linux/cleanup.h note
that goto and cleanup helpers should never be mixed in the same function,
as it creates confusing ownership semantics. Could this function either
avoid using the scoped cleanup loop or fully convert the oom_adj_mutex
to a guard() pattern as well?
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Potential NULL pointer dereference or Use-After-Free of `folio->mapping` in `collect_procs_file()` due to concurrent truncation.
--
Unrelated to this patch. I will fix it later.
commit 788d1f422c3f19026d9e0d86b550c238607720ed
Author: Ye Liu [off-list ref]
mm/memory-failure: convert process iterator to for_each_process_rculock
This commit replaces the manual rcu_read_lock()/rcu_read_unlock() pairs
combined with for_each_process() loop in mm/memory-failure.c with
for_each_process_rculock(), which scopes the RCU read lock to the loop body
via scoped_guard(rcu).
[Severity: High]
This is a pre-existing issue, but could this lead to a NULL pointer
dereference or a use-after-free of mapping due to a race with truncation?
In memory_failure(), the folio lock is dropped in hwpoison_user_mappings()
before calling collect_procs(). collect_procs() performs an unlocked check of
!folio->mapping:
static void collect_procs(...)
{
if (!folio->mapping)
return;
...
else
collect_procs_file(folio, page, tokill, force_early);
}
If a concurrent truncation removes the folio from the page cache immediately
after this check, folio->mapping becomes NULL. collect_procs_file() then
initializes mapping = folio->mapping and calls i_mmap_lock_read(mapping).
If the compiler performs a second read of folio->mapping, mapping will be
NULL, crashing the kernel on the semaphore acquisition. If the compiler
caches the first read, mapping will point to an address_space whose host
inode may have been concurrently freed, leading to a use-after-free since
the elevated page refcount does not pin the inode and no RCU read lock is
held during the blocking i_mmap_lock_read() call.
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The commit message incorrectly claims to replace `for_each_thread()` with `for_each_thread_rculock()`, but the code actually replaces `for_each_process()` with `for_each_process_rculock()`.
--
commit 3ac98de6fbc4e97ba4981ef0c4941692c15a9309
Author: Ye Liu [off-list ref]
cpu/hotplug: convert thread iterator to for_each_thread_rculock
This commit replaces a manual rcu_read_lock() and rcu_read_unlock() pair
in clear_tasks_mm_cpumask() with a scoped RCU lock iterator macro.
quoted
cpu/hotplug: convert thread iterator to for_each_thread_rculock
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_thread() loop in kernel/cpu.c with for_each_thread_rculock(),
[Severity: Medium]
Is the commit message accurate regarding the iterator being replaced?
The commit message subject and body describe converting a thread iterator
using for_each_thread() to for_each_thread_rculock(). However, looking at
clear_tasks_mm_cpumask() in the diff:
The code actually replaces for_each_process() with for_each_process_rculock().
Could the commit message be updated to match the process iterator actually
used in the patch to avoid confusion during review or bisection?
Sorry, copy error. It should be the following content:
cpu/hotplug: convert process iterator to for_each_process_rculock
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_process() loop in kernel/cpu.c with for_each_process_rculock(),
which scopes the RCU read lock to the loop body via scoped_guard(rcu).
No functional change.
--
Thanks,
Ye Liu
From: Steven Rostedt <rostedt@goodmis.org> Date: 2026-09-11 12:57:04
On Fri, 11 Sep 2026 15:57:57 +0800
Ye Liu [off-list ref] wrote:
From: Ye Liu <liuye@kylinos.cn>
Replace guard(rcu)() combined with for_each_process_thread() loop in
kernel/unwind/deferred.c with for_each_process_thread_rculock(),
which scopes the RCU read lock to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
-- Steve
From: Steven Rostedt <rostedt@goodmis.org> Date: 2026-09-11 12:58:31
On Fri, 11 Sep 2026 15:57:56 +0800
Ye Liu [off-list ref] wrote:
From: Ye Liu <liuye@kylinos.cn>
Replace the manual rcu_read_lock()/rcu_read_unlock() pair combined
with for_each_process_thread() loop in kernel/trace/fgraph.c with
for_each_process_thread_rculock(), which scopes the RCU read lock
to the loop body via scoped_guard(rcu).
No functional change.
Signed-off-by: Ye Liu <liuye@kylinos.cn>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
-- Steve