On Fri, 11 Sep 2026 14:26:26 +0000, [email protected] wrote:
> > +   for (i = 1; i < MAX_OPTIMIZED_LENGTH / sizeof(kprobe_opcode_t); i++)
> > +           if (get_kprobe((kprobe_opcode_t *)addr - i))
>
> [Severity: Medium]
> When kprobe_in_optimized_region() calls get_kprobe(), neither rcu_read_lock()
> nor kprobe_mutex is held (for example, when called from the Tasks RCU GP
> kthread).
>
> get_kprobe() iterates the hash list using:
>
>     hlist_for_each_entry_rcu(p, head, hlist,
>                              lockdep_is_held(&kprobe_mutex)) {
>
> Without explicitly acquiring rcu_read_lock() around get_kprobe(), lockdep
> will complain about suspicious RCU usage, because preempt_disable() (or
> running from a kthread) does not satisfy rcu_read_lock_held() on
> CONFIG_PREEMPT_RCU.

This is not right. The list iterator's check is not rcu_read_lock_held(),
it is

        RCU_LOCKDEP_WARN(!(cond) && !rcu_read_lock_any_held(), ...)

and rcu_read_lock_any_held() ends in "return !preemptible();", so any
caller with preemption or interrupts disabled satisfies it regardless of
PREEMPT_RCU. That is also the actual RCU guarantee since the flavor
consolidation: synchronize_rcu() waits for preempt/irq-disabled regions
on PREEMPT_RCU kernels too, which is what the kprobe free paths rely on
here.

Every caller is in such a region. rcu_tasks_preempt_is_qs() runs from
rcu_note_context_switch() inside __schedule() after local_irq_disable();
the grace-period kthread only evaluates it from the task_call_func()
callback in 14/15, which runs under p->pi_lock taken with irqsave; and
the preempt == false callers (cond_resched_tasks_rcu_qs(),
rcu_softirq_qs()) short-circuit before rcu_tasks_preempt_is_qs() is
evaluated at all. Nothing calls this bare from kthread context.

I also checked it the boring way: PREEMPT_DYNAMIC=y (so PREEMPT_RCU=y),
PROVE_RCU, PROVE_LOCKING, booted preempt=lazy, registering and
unregistering optimized kprobes in a loop while a kthread spins and gets
irq-preempted, which is exactly the path that reaches get_kprobe() with
kprobe_optimizer_waiting set. No lockdep output.

For the tool: when flagging hlist_for_each_entry_rcu() callers, check
against rcu_read_lock_any_held() (the condition __list_check_rcu()
actually uses), not rcu_read_lock_held().

Thanks,

Josef

Reply via email to