| From f747c7e15d7bc71a967a94ceda686cf2460b69e8 Mon Sep 17 00:00:00 2001 |
| From: "Paul E. McKenney" <paulmck@kernel.org> |
| Date: Tue, 15 Sep 2020 14:27:38 -0700 |
| Subject: rcu-tasks: Enclose task-list scan in rcu_read_lock() |
| |
| From: Paul E. McKenney <paulmck@kernel.org> |
| |
| commit f747c7e15d7bc71a967a94ceda686cf2460b69e8 upstream. |
| |
| The rcu_tasks_trace_postgp() function uses for_each_process_thread() |
| to scan the task list without the benefit of RCU read-side protection, |
| which can result in use-after-free errors on task_struct structures. |
| This error was missed because the TRACE01 rcutorture scenario enables |
| lockdep, but also builds with CONFIG_PREEMPT_NONE=y. In this situation, |
| preemption is disabled everywhere, so lockdep thinks everywhere can |
| be a legitimate RCU reader. This commit therefore adds the needed |
| rcu_read_lock() and rcu_read_unlock(). |
| |
| Note that this bug can occur only after an RCU Tasks Trace CPU stall |
| warning, which by default only happens after a grace period has extended |
| for ten minutes (yes, not a typo, minutes). |
| |
| Fixes: 4593e772b502 ("rcu-tasks: Add stall warnings for RCU Tasks Trace") |
| Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com> |
| Cc: Daniel Borkmann <daniel@iogearbox.net> |
| Cc: Jiri Olsa <jolsa@redhat.com> |
| Cc: <bpf@vger.kernel.org> |
| Cc: <stable@vger.kernel.org> # 5.7.x |
| Signed-off-by: Paul E. McKenney <paulmck@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/rcu/tasks.h | 2 ++ |
| 1 file changed, 2 insertions(+) |
| |
| --- a/kernel/rcu/tasks.h |
| +++ b/kernel/rcu/tasks.h |
| @@ -1078,9 +1078,11 @@ static void rcu_tasks_trace_postgp(struc |
| if (ret) |
| break; // Count reached zero. |
| // Stall warning time, so make a list of the offenders. |
| + rcu_read_lock(); |
| for_each_process_thread(g, t) |
| if (READ_ONCE(t->trc_reader_special.b.need_qs)) |
| trc_add_holdout(t, &holdouts); |
| + rcu_read_unlock(); |
| firstreport = true; |
| list_for_each_entry_safe(t, g, &holdouts, trc_holdout_list) { |
| if (READ_ONCE(t->trc_reader_special.b.need_qs)) |