Re: [PATCH 8/9] srcutiny: Detect duplicate callback enqueue

From: KunWu Chan

Date: Sun Oct 25 2026 - 22:36:55 EST


On Sat, Oct 24, 2026 at 3:31 AM Paul E. McKenney <paulmck@xxxxxxxxxx> wrote:
>
> On Fri, Oct 23, 2026 at 06:18:40PM +0800, Kunwu Chan wrote:
> > Detect duplicate callback enqueue attempts at call_srcu()
> > entry, before the deferred list can be corrupted.
> >
> > Signed-off-by: Kunwu Chan <chentao@xxxxxxxxxx>
>
> Same questions as for the previous patch. ;-)

Same changes applied: pr_err_ratelimited() and dump_stack.

Thanks,
Kunwu

>
> Thanx, Paul
>
> > ---
> > kernel/rcu/srcutiny.c | 19 +++++++++++++++++++
> > 1 file changed, 19 insertions(+)
> >
> > diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
> > index 98864977bed2..4400fd2b41b7 100644
> > --- a/kernel/rcu/srcutiny.c
> > +++ b/kernel/rcu/srcutiny.c
> > @@ -12,6 +12,7 @@
> > #include <linux/irq_work.h>
> > #include <linux/kasan.h>
> > #include <linux/llist.h>
> > +#include <linux/mm.h>
> > #include <linux/mutex.h>
> > #include <linux/preempt.h>
> > #include <linux/rcupdate_wait.h>
> > @@ -178,6 +179,7 @@ void srcu_drive_gp(struct work_struct *wp)
> > while (lh) {
> > rhp = lh;
> > lh = lh->next;
> > + debug_rcu_head_unqueue(rhp);
> > debug_rcu_head_callback(rhp);
> > local_bh_disable();
> > rhp->func(rhp);
> > @@ -235,6 +237,8 @@ static void srcu_gp_start_if_needed(struct srcu_struct *ssp)
> > * Also called by __srcu_defer_drain() to re-issue a deferred callback, so it
> > * must not re-check the deferral condition.
> > */
> > +static void srcu_leak_callback(struct rcu_head *rhp) { }
> > +
> > static void srcu_do_enqueue(struct srcu_struct *ssp, struct rcu_head *rhp,
> > rcu_callback_t func)
> > {
> > @@ -272,6 +276,8 @@ static void __srcu_defer_drain(struct srcu_struct *ssp)
> > llist_for_each_safe(node, next, llist_del_all(&ssp->defer_cbs)) {
> > struct rcu_head *rhp = (struct rcu_head *)node;
> >
> > + /* Bounds a node self-linked by a double call_srcu(). */
> > + rhp->next = NULL;
> > srcu_do_enqueue(ssp, rhp, rhp->func);
> > }
> > local_irq_restore(flags);
> > @@ -291,6 +297,8 @@ EXPORT_SYMBOL_GPL(srcu_defer_drain);
> > void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
> > rcu_callback_t func)
> > {
> > + static atomic_t doublefrees;
> > +
> > /* Misaligned rcu_head! */
> > WARN_ON_ONCE((unsigned long)rhp & (sizeof(void *) - 1));
> >
> > @@ -298,6 +306,16 @@ void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
> > if (WARN_ON_ONCE(!func))
> > return;
> >
> > + if (debug_rcu_head_queue(rhp)) {
> > + if (atomic_inc_return(&doublefrees) < 4) {
> > + pr_err("%s(): Duplicate call_srcu() CB %p->%pS()\n",
> > + __func__, rhp, rhp->func);
> > + mem_dump_obj(rhp);
> > + }
> > + WRITE_ONCE(rhp->func, srcu_leak_callback);
> > + return;
> > + }
> > +
> > /* Record the caller: the irq_work's stack says nothing about it. */
> > kasan_record_aux_stack(rhp);
> >
> > @@ -306,6 +324,7 @@ void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
> > if (READ_ONCE(srcu_defer_draining) && !in_nmi()) {
> > WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU),
> > "call_srcu() re-entered during callback drain; leaking callback\n");
> > + debug_rcu_head_unqueue(rhp);
> > return;
> > }
> > rhp->func = func;
> > --
> > 2.43.0
> >