summaryrefslogtreecommitdiff
path: root/tools/testing
diff options
context:
space:
mode:
authorYun Lu <luyun@kylinos.cn>2026-10-12 14:19:12 +0800
committerAlexei Starovoitov <ast@kernel.org>2026-10-16 00:39:48 +0000
commitc961f7b41a108313a4705baac7b27d2174cc8035 (patch)
tree290f1be5a5fc12bbf041393aeae203d389a26ed3 /tools/testing
parent70024d04ea27a92132eb83b12911dc8de2bfad4b (diff)
bpf: Fix NULL task dereference in task work cancellation
bpf_task_work_irq() publishes ctx->work with task_work_add() before changing the context from SCHEDULING to SCHEDULED. The target task can run the callback on another CPU before the handler reaches its final state transition. The handler then reloads ctx->task for cancellation if its final cmpxchg observes FREED. By that point the callback or context destruction may already have cleared ctx->task, causing task_work_cancel() to dereference a NULL task pointer. One concrete interleaving is: 1. CPU 0 changes PENDING to SCHEDULING and successfully queues ctx->work with task_work_add(), but has not attempted the final state cmpxchg. 2. The target task on CPU 1 runs the callback. It changes SCHEDULING to RUNNING, executes the BPF subprogram, resets ctx->task, publishes STANDBY and drops the callback's context reference. 3. CPU 2 deletes the map value. It changes STANDBY to FREED and drops the map's context reference without queuing asynchronous cancellation. 4. CPU 0 resumes. Its SCHEDULING-to-SCHEDULED cmpxchg observes FREED, and the cancellation attempt passes NULL to task_work_cancel(). If deletion publishes FREED before the callback claims RUNNING, the callback takes its FREED exit and drops its context reference. Context destruction can then clear ctx->task before CPU 0 attempts cancellation. A controlled reproducer with a delay before the final cmpxchg triggered the following fault on a v7.3-rc3 based x86-64 KVM guest: BUG: kernel NULL pointer dereference, address: 0000000000000890 RIP: 0010:task_work_cancel+0xd/0xa0 RDI: 0000000000000000 CR2: 0000000000000890 Call Trace: <IRQ> bpf_task_work_irq+0x95/0x100 irq_work_run_list+0x4f/0x90 irq_work_run+0x18/0x50 </IRQ> 0x890 is the task_struct::task_works offset in that build, read through the NULL task argument. Capture ctx->task before task_work_add() publishes the callback and use that pointer for the handler's cancellation attempt. The round still holds its task reference when the pointer is captured. Releasing that reference uses bpf_task_release(), which calls put_task_struct_rcu_user(), so the handler's existing RCU read-side section keeps the captured task alive even if ctx->task is subsequently cleared. The asynchronous cancellation path reads ctx->task directly, which is safe there: queuing it requires deletion to observe SCHEDULED, and that xchg wins against the callback's transition to RUNNING, so the callback takes its FREED exit without resetting ctx->task, while the map reference transferred to the cancellation handler prevents context destruction until cancellation completes. The handler-tail and asynchronous cancellation attempts are mutually exclusive: asynchronous cancellation is queued only if deletion observes SCHEDULED after the scheduling handler's final cmpxchg has succeeded. If that cmpxchg instead observes FREED, the scheduling handler attempts cancellation with the captured pointer. Fixes: 38aa7003e369 ("bpf: task work scheduling kfuncs") Reported-by: Jackie Liu <liuyun01@kylinos.cn> Suggested-by: Mykyta Yatsenko <mykyta.yatsenko5@gmail.com> Signed-off-by: Yun Lu <luyun@kylinos.cn> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261012061915.231254-2-luyun_611@163.com
Diffstat (limited to 'tools/testing')
0 files changed, 0 insertions, 0 deletions