From 68f633e58deb6edffa435a3b2bdc4a12a5e36daf Mon Sep 17 00:00:00 2001 From: Rahul Chandelkar Date: Fri, 29 May 2026 12:32:34 +0530 Subject: [PATCH net] dibs: fix use-after-free in dmb lookup functions dibs_lo_attach_dmb(), dibs_lo_detach_dmb(), and dibs_lo_unregister_dmb() all release read_lock_bh before operating on the dmb_node pointer found during hash table lookup. A concurrent unregister on the same token can kfree() the node in this window, causing a use-after-free when the original caller subsequently performs refcount_inc_not_zero() or refcount_dec_and_test() on the freed object. Fix by performing the refcount operation while still holding read_lock_bh, so the node cannot be freed between lookup and use. Release the lock before any code path that may call __dibs_lo_unregister_dmb(), since that function acquires write_lock_bh internally via hash_del(). Fixes: cb990a45d7f6 ("dibs: Define dibs loopback") Signed-off-by: Rahul Chandelkar --- drivers/dibs/dibs_loopback.c | 42 +++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index ec3b48cb0e87..d7c779cc0d9b 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -150,17 +150,21 @@ static int dibs_lo_unregister_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb) break; } } - read_unlock_bh(&ldev->dmb_ht_lock); - if (!dmb_node) + if (!dmb_node) { + read_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; + } + if (!refcount_dec_and_test(&dmb_node->refcnt)) { + read_unlock_bh(&ldev->dmb_ht_lock); + return 0; + } + read_unlock_bh(&ldev->dmb_ht_lock); - if (refcount_dec_and_test(&dmb_node->refcnt)) { - spin_lock_irqsave(&dibs->lock, flags); - dibs->dmb_clientid_arr[dmb_node->sba_idx] = NO_DIBS_CLIENT; - spin_unlock_irqrestore(&dibs->lock, flags); + spin_lock_irqsave(&dibs->lock, flags); + dibs->dmb_clientid_arr[dmb_node->sba_idx] = NO_DIBS_CLIENT; + spin_unlock_irqrestore(&dibs->lock, flags); - __dibs_lo_unregister_dmb(ldev, dmb_node); - } + __dibs_lo_unregister_dmb(ldev, dmb_node); return 0; } @@ -184,16 +188,10 @@ static int dibs_lo_attach_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb) break; } } - if (!dmb_node) { - read_unlock_bh(&ldev->dmb_ht_lock); - return -EINVAL; - } + if (dmb_node && !refcount_inc_not_zero(&dmb_node->refcnt)) + dmb_node = NULL; read_unlock_bh(&ldev->dmb_ht_lock); - - if (!refcount_inc_not_zero(&dmb_node->refcnt)) - /* the dmb is being unregistered, but has - * not been removed from the hash table. - */ + if (!dmb_node) return -EINVAL; /* provide dmb information */ @@ -220,14 +218,14 @@ static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token) break; } } - if (!dmb_node) { + if (dmb_node && refcount_dec_and_test(&dmb_node->refcnt)) { read_unlock_bh(&ldev->dmb_ht_lock); - return -EINVAL; + __dibs_lo_unregister_dmb(ldev, dmb_node); + return 0; } read_unlock_bh(&ldev->dmb_ht_lock); - - if (refcount_dec_and_test(&dmb_node->refcnt)) - __dibs_lo_unregister_dmb(ldev, dmb_node); + if (!dmb_node) + return -EINVAL; return 0; } -- 2.54.0