trc_del_holdout() invokes put_task_struct() may cause the task_struct is freed once the task is exiting. If happened, then we shouldn't touch the task_strcut, or it will triger an use-after-free. Here we only mark it as checked, and grace-period kthread will romove it from holdout list. CPU0 CPU1 trc_add_holdout(t, bhp) //t->usage==2 ...... ...... release_task ->put_task_struct_rcu_user ->delayed_put_task_struct ...... ->put_task_struct(t) //t->usage==1 check_all_holdout_tasks_trace ->trc_wait_for_one_reader ->get_task_struct ->try_invoke_on_locked_down_task(trc_inspect_reader) ->trc_del_holdout ->put_task_struct(t) //t->usage==0 and task_struct freed ->READ_ONCE(t->trc_reader_checked) //ops, t had been freed. BTW, do the same thing in trc_wait_for_one_reader() when the task is a current running task even if it is safe. Reported-by: syzbot+7b2b13f4943374609532@xxxxxxxxxxxxxxxxxxxxxxxxx Signed-off-by: Yanfei Xu <yanfei.xu@xxxxxxxxxxxxx> --- v1->v2: 1. improve the commit messages. 2. change the comments in codes. kernel/rcu/tasks.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h index 36607551f966..2303cb131367 100644 --- a/kernel/rcu/tasks.h +++ b/kernel/rcu/tasks.h @@ -872,10 +872,9 @@ static bool trc_inspect_reader(struct task_struct *t, void *arg) in_qs = likely(!t->trc_reader_nesting); } - // Mark as checked. Because this is called from the grace-period - // kthread, also remove the task from the holdout list. + // Mark as checked so that the grace-period kthread will + // remove it from the holdout list. t->trc_reader_checked = true; - trc_del_holdout(t); if (in_qs) return true; // Already in quiescent state, done!!! @@ -899,10 +898,11 @@ static void trc_wait_for_one_reader(struct task_struct *t, if (smp_load_acquire(&t->trc_ipi_to_cpu) != -1) // Order IPI return; - // The current task had better be in a quiescent state. + // The current task had better be in a quiescent state, and + // mark as checked so that the grace-period kthread will + // remove it from the holdout list. if (t == current) { t->trc_reader_checked = true; - trc_del_holdout(t); WARN_ON_ONCE(t->trc_reader_nesting); return; } -- 2.27.0