Re: [PATCH RFC 04/13] kprobes: Let Tasks RCU recognise tasks preempted in an optprobe jump window
From: Josef Bacik <josef@toxicpanda.com>
Date: 2026-09-10 22:46:36
Also in:
bpf, sashiko-reviews
On Thu, 10 Sep 2026 19:16:33 +0000, sashiko-bot@kernel.org wrote:
quoted
+bool kprobe_in_optimized_region(unsigned long addr) +{ + int i; + + for (i = 1; i < MAX_OPTIMIZED_LENGTH / sizeof(kprobe_opcode_t); i++) + if (get_kprobe((kprobe_opcode_t *)addr - i))[Severity: High] Could this loop introduce significant overhead in the preemption fast-path? On architectures like x86, this loop executes up to 18 times, querying the kprobe hash table via get_kprobe() on each iteration. Since the hash table can have long chains on systems with many active kprobes, this could result in thousands of pointer dereferences during a preemption event.
This one is fair. It is not the scheduler fast path in general, only the irq-exit preemption path (need_resched set on return to kernel with preempt_count() == 0), but 18 hash lookups there is still more than it needs to be. For v2 the walk only runs while kprobe_optimizer() is actually sitting in its synchronize_rcu_tasks() -- a flag set and cleared around that call -- and rcu_tasks_ip_in_trampoline() only asks for core and module text. A preemption that does not see the flag predates the grace period; the task is then just an ordinary preempted holdout and the jump is not written until it has run again and left the window, so skipping the walk there is safe. Common-case cost becomes one load.
Also, does this introduce a use-after-free risk for interrupted idle tasks? kprobe_optimizer() unlinks kprobes and frees them after waiting only for synchronize_rcu_tasks(). Because synchronize_rcu_tasks() explicitly ignores idle tasks, an idle CPU that is interrupted could end up traversing the kprobe_table here via get_kprobe() while the kprobe is concurrently freed, as Tasks RCU will not wait for the idle task's traversal to finish.
This one is not right, for two independent reasons: - kprobe_table is an RCU hlist and nothing frees a kprobe on the strength of Tasks RCU alone. Every unregistration path does hlist_del_rcu() and then synchronize_rcu() before the object goes away, and the optimizer's own free step runs after a Tasks RCU grace period, which begins and ends with synchronize_rcu(). The caller here runs with interrupts disabled, which is a normal RCU read-side section, so the walk cannot outlive the object regardless of what Tasks RCU thinks of the task. - The idle case cannot reach this code. irqentry_exit_to_kernel_mode_preempt() returns early when state.exit_rcu is set, i.e. when the interrupt was taken with RCU not watching, so the irq-exit preemption path (and this check with it) only ever runs with RCU watching. And the idle task is not preempted through this path in the first place. So: overhead finding valid and addressed in v2, UAF finding invalid. The commit message in v2 spells out the RCU-safety argument so the next reader does not have to reconstruct it. Thanks, Josef