mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
bpf: Claim the per-CPU send_signal irq_work before filling it
irq_work_is_busy() cannot see the per-CPU send_signal_work while it is being filled: the check only matches after irq_work_queue() has claimed the work. A concurrent caller therefore passes it, both callers race for the same irq_work, and the loser's signal is silently lost along with its task reference while the queued work runs with a mix of both callers' fields. kprobe, tracepoint and perf_event programs exclude each other on the same CPU through bpf_prog_active, so for two callers to meet, one of the programs has to be a raw tracepoint or an fentry one. And since commit87c544108b("bpf: Send signals asynchronously if !preemptible") this path runs with IRQs enabled too, so a hard IRQ can interrupt the fill as well, not only an NMI. Found by code inspection and reproduced with a perf_event NMI program racing an fentry one. Claim the work through work->task itself: cmpxchg() it from NULL to the target task before touching the other fields, and set it back to NULL once the callback has consumed them. A context finding the work claimed returns the documented -EBUSY. Fixes:1bc7896e9e("bpf: Fix deadlock with rq_lock in bpf_send_signal()") Signed-off-by: Yuan Chen <chenyuan@kylinos.cn> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009022427.968304-1-chenyuan@kylinos.cn
This commit is contained in:
parent
d2141481a7
commit
2d1f101ee6
|
|
@ -823,6 +823,7 @@ const struct bpf_func_proto bpf_task_pt_regs_proto = {
|
|||
|
||||
struct send_signal_irq_work {
|
||||
struct irq_work irq_work;
|
||||
/* The claim: NULL marks the slot free for the next caller. */
|
||||
struct task_struct *task;
|
||||
u32 sig;
|
||||
enum pid_type type;
|
||||
|
|
@ -842,6 +843,8 @@ static void do_bpf_send_signal(struct irq_work *entry)
|
|||
|
||||
group_send_sig_info(work->sig, siginfo, work->task, work->type);
|
||||
put_task_struct(work->task);
|
||||
/* Release once the fields are consumed. */
|
||||
smp_store_release(&work->task, NULL);
|
||||
}
|
||||
|
||||
static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struct *task, u64 value)
|
||||
|
|
@ -885,14 +888,14 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struc
|
|||
return -EINVAL;
|
||||
|
||||
work = this_cpu_ptr(&send_signal_work);
|
||||
if (irq_work_is_busy(&work->irq_work))
|
||||
if (cmpxchg(&work->task, NULL, task))
|
||||
return -EBUSY;
|
||||
|
||||
/* Add the current task, which is the target of sending signal,
|
||||
* to the irq_work. The current task may change when queued
|
||||
* irq works get executed.
|
||||
*/
|
||||
work->task = get_task_struct(task);
|
||||
get_task_struct(task);
|
||||
work->has_siginfo = siginfo == &info;
|
||||
if (work->has_siginfo)
|
||||
copy_siginfo(&work->info, &info);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user