Thank you for your contribution! Sashiko AI review found 1 potential issue(s) 
to consider:
- [Medium] rcu-tasks: Use-After-Free if task oopses inside irq-exit preemption
--

commit 6894dfa1f4c077dbeacd53dcfc82e1697f747465
Author: Josef Bacik <[email protected]>

rcu-tasks: Add a Tasks RCU implementation for reader-marked trampolines

This commit introduces a new Tasks RCU implementation for architectures
where trampolines are marked as Tasks Trace RCU readers. It tracks context
switches and preemptions during irq exits to wait for tasks that might
be lingering in unmarked trampoline text.

> diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h
> index 627295396cd91..e7498601c28c3 100644
> --- a/kernel/rcu/tasks.h
> +++ b/kernel/rcu/tasks.h
[ ... ]
> +static void rcu_tasks_tramp_hold(struct task_struct *t)
> +{
> +     unsigned long flags;
> +
> +     if (t->rcu_tasks_holdout)
> +             return;
> +     raw_spin_lock_irqsave(&rcu_tasks_tramp_lock, flags);
> +     list_add_tail(&t->rcu_tasks_holdout_list, &rcu_tasks_tramp_holdouts);
> +     WRITE_ONCE(t->rcu_tasks_holdout, true);
> +     raw_spin_unlock_irqrestore(&rcu_tasks_tramp_lock, flags);
> +}

[Severity: Medium]
Does this code risk a use-after-free of the task struct?

It appears that the task is added to the holdout list without incrementing its
reference count (e.g., unlike the classic flavor which deliberately calls
get_task_struct()).

If a task is preempted inside unmarked trampoline text,
rcu_tasks_irq_resched_enter() adds it to rcu_tasks_tramp_holdouts. If the task
subsequently suffers a hardware exception or kernel oops while executing
preempt_schedule_irq(), it will be killed via make_task_dead() and do_exit().
Because rcu_tasks_irq_resched_exit() would never be reached to clear
t->rcu_tasks_irq_ip, rcu_tasks_note_qs() skips removing it from the holdout
list, leaving a dangling pointer when the task is later freed.

[ ... ]
> +static void rcu_tasks_tramp_wait_holdouts(struct rcu_tasks *rtp, unsigned 
> long *lastreport)
> +{
> +     struct task_struct *t;
> +     unsigned long flags;
> +     int cpu;
> +
> +     raw_spin_lock_irqsave(&rcu_tasks_tramp_lock, flags);
> +     list_splice_tail_init(&rcu_tasks_tramp_holdouts, 
> &rcu_tasks_gp_holdouts);
> +     raw_spin_unlock_irqrestore(&rcu_tasks_tramp_lock, flags);
> +
> +     for (;;) {
> +             struct cpumask *kick = &rcu_tasks_pending_cpus;
> +             struct task_struct *show[8];
> +             int nshow = 0, i;
> +             bool empty, report;
> +
> +             report = rcu_tasks_tramp_stall(rtp, lastreport,
> +                                            "tasks preempted in trampoline 
> text");
> +             cpumask_clear(kick);
> +             raw_spin_lock_irqsave(&rcu_tasks_tramp_lock, flags);
> +             empty = list_empty(&rcu_tasks_gp_holdouts);
> +             list_for_each_entry(t, &rcu_tasks_gp_holdouts, 
> rcu_tasks_holdout_list) {
> +                     if (task_curr(t))
> +                             __cpumask_set_cpu(task_cpu(t), kick);

[Severity: Medium]
If the scenario described above occurs and the task struct has been freed,
could we hit a use-after-free here when iterating over the holdout list and
accessing t?

-- 
Sashiko AI review ยท 
https://sashiko.dev/#/patchset/20260929-b4-rcu-tasks-preempt-qs-v6-0-c111ee02c...@toxicpanda.com?part=3

Reply via email to