This commit adds DEBUG_OBJECTS_RCU_HEAD checking to detect call_srcu()
counterparts to double-free bugs.

Signed-off-by: Paul E. McKenney <[email protected]>
---
 kernel/rcu/srcutree.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
index a46142de5133..31203469b2d1 100644
--- a/kernel/rcu/srcutree.c
+++ b/kernel/rcu/srcutree.c
@@ -763,6 +763,13 @@ static bool srcu_might_be_idle(struct srcu_struct *sp)
 }
 
 /*
+ * SRCU callback function to leak a callback.
+ */
+static void srcu_leak_callback(struct rcu_head *rhp)
+{
+}
+
+/*
  * Enqueue an SRCU callback on the srcu_data structure associated with
  * the current CPU and the specified srcu_struct structure, initiating
  * grace-period processing if it is not already running.
@@ -800,6 +807,12 @@ void __call_srcu(struct srcu_struct *sp, struct rcu_head 
*rhp,
        struct srcu_data *sdp;
 
        check_init_srcu_struct(sp);
+       if (debug_rcu_head_queue(rhp)) {
+               /* Probable double call_srcu(), so leak the callback. */
+               WRITE_ONCE(rhp->func, srcu_leak_callback);
+               WARN_ONCE(1, "call_srcu(): Leaked duplicate callback\n");
+               return;
+       }
        rhp->func = func;
        local_irq_save(flags);
        sdp = this_cpu_ptr(sp->sda);
@@ -974,9 +987,12 @@ void srcu_barrier(struct srcu_struct *sp)
                spin_lock_irq(&sdp->lock);
                atomic_inc(&sp->srcu_barrier_cpu_cnt);
                sdp->srcu_barrier_head.func = srcu_barrier_cb;
+               debug_rcu_head_queue(&sdp->srcu_barrier_head);
                if (!rcu_segcblist_entrain(&sdp->srcu_cblist,
-                                          &sdp->srcu_barrier_head, 0))
+                                          &sdp->srcu_barrier_head, 0)) {
+                       debug_rcu_head_unqueue(&sdp->srcu_barrier_head);
                        atomic_dec(&sp->srcu_barrier_cpu_cnt);
+               }
                spin_unlock_irq(&sdp->lock);
        }
 
@@ -1101,6 +1117,7 @@ static void srcu_invoke_callbacks(struct work_struct 
*work)
        spin_unlock_irq(&sdp->lock);
        rhp = rcu_cblist_dequeue(&ready_cbs);
        for (; rhp != NULL; rhp = rcu_cblist_dequeue(&ready_cbs)) {
+               debug_rcu_head_unqueue(rhp);
                local_bh_disable();
                rhp->func(rhp);
                local_bh_enable();
-- 
2.5.2

Reply via email to