* [PATCH] IB/cm: hold a cm_id reference across the send error callback
@ 2026-10-24 15:55 Yehyeong Lee
2026-10-24 16:01 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Yehyeong Lee @ 2026-10-24 15:55 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky
Cc: Sean Hefty, Vlad Dumitrescu, linux-rdma, linux-kernel, stable,
yhlee
cm_process_send_error() lets cm_free_priv_msg() release the reference the
message held on the cm_id and then keeps using cm_id_priv, dropping
cm_id_priv->lock and calling the consumer's cm_handler() callback. If that
was the last reference, a concurrent ib_destroy_cm_id() takes the count to
zero and returns while the callback is still running, so the consumer frees
its context underneath it. For an RDMA CM consumer that context is the
rdma_id_private which _destroy_id() kfree()s once ib_destroy_cm_id() has
returned, and the callback reads it through cm_id->context before any state
check.
Reaching this needs a send that expects a response, so a REQ, REP, DREQ or
SIDR_REQ, to complete with a real error rather than a flush, and the
consumer to destroy the id while that error is being processed.
The report below comes from an out-of-tree KUnit test. The race is a few
instructions wide, so the test blocks after the spin_unlock_irq() until the
racing destroy has finished and KASAN can observe the access. The cm_kt_
frames stand in for a consumer and its destroy path, and the test is not
part of this patch.
BUG: KASAN: slab-use-after-free in cm_kt_handler+0xaa/0xd0
Read of size 4 at addr ffff8880029582c0 by task kunit_try_catch/39
CPU: 0 UID: 0 PID: 39 Comm: kunit_try_catch Tainted: G N 7.3.0-07363-g734bef548a96-dirty #1 PREEMPT(full)
Tainted: [N]=TEST
Call Trace:
<TASK>
print_report+0xcd/0x620
? cm_kt_handler+0xaa/0xd0
? __virt_addr_valid+0x208/0x3f0
? cm_kt_handler+0xaa/0xd0
kasan_report+0xfb/0x130
? cm_kt_handler+0xaa/0xd0
cm_kt_handler+0xaa/0xd0
cm_process_send_error+0x2d9/0x560
? __pfx_cm_process_send_error+0x10/0x10
? __lock_acquire+0x57d/0x24a0
? trace_hardirqs_on+0x17/0x100
cm_kt_destroy_returns_during_callback+0x1e8/0x700
? __pfx_cm_kt_destroy_returns_during_callback+0x10/0x10
? kunit_try_run_case+0x1a4/0x4c0
? lock_release+0xc3/0x250
? lockdep_hardirqs_on_prepare+0xd5/0x180
? ktime_get_ts64+0x1e5/0x2e0
? trace_hardirqs_on+0x17/0x100
? __pfx_read_tsc+0x10/0x10
? ktime_get_ts64+0x242/0x2e0
? __init_swait_queue_head+0xca/0x150
kunit_try_run_case+0x1ca/0x4c0
? __pfx_kunit_try_run_case+0x10/0x10
? lock_release+0xc3/0x250
? lockdep_hardirqs_on_prepare+0xd5/0x180
? _raw_spin_unlock_irqrestore+0x3f/0x50
? trace_hardirqs_on+0x17/0x100
? __pfx_kunit_try_run_case+0x10/0x10
? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10
kunit_generic_run_threadfn_adapter+0x7b/0xe0
kthread+0x3aa/0x550
? _raw_spin_unlock_irq+0x23/0x40
? __pfx_kthread+0x10/0x10
ret_from_fork+0x493/0x940
? __pfx_ret_from_fork+0x10/0x10
? __switch_to+0x22/0xdb0
? __pfx_kthread+0x10/0x10
ret_from_fork_asm+0x19/0x30
</TASK>
Allocated by task 39:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0x8f/0xa0
__kmalloc_cache_noprof+0x201/0x4a0
cm_kt_make_id.constprop.0+0xa0/0x470
cm_kt_destroy_returns_during_callback+0x84/0x700
kunit_try_run_case+0x1ca/0x4c0
kunit_generic_run_threadfn_adapter+0x7b/0xe0
kthread+0x3aa/0x550
ret_from_fork+0x493/0x940
ret_from_fork_asm+0x19/0x30
Freed by task 40:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x43/0x70
kfree+0x119/0x490
cm_kt_destroyer+0x31/0x50
kthread+0x3aa/0x550
ret_from_fork+0x493/0x940
ret_from_fork_asm+0x19/0x30
The buggy address belongs to the object at ffff8880029582c0
which belongs to the cache kmalloc-8 of size 8
The buggy address is located 0 bytes inside of
freed 8-byte region [ffff8880029582c0, ffff8880029582c8)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888002958900 pfn:0x2958
flags: 0x4000000000000200(workingset|zone=1)
page_type: f5(slab)
raw: 4000000000000200 ffff888001041500 ffff888001040348 ffff888001040348
raw: ffff888002958900 0000000000800048 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888002958180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888002958200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888002958280: fc fc fc fc 00 fc fc fc fa fc fc fc 00 fc fc fc
^
ffff888002958300: 02 fc fc fc 02 fc fc fc 02 fc fc fc 02 fc fc fc
ffff888002958380: 02 fc fc fc 02 fc fc fc 02 fc fc fc 02 fc fc fc
This reference used to be released after the callback, which kept the cm_id
alive for its duration. Restore that ordering and drop it on every return
path.
Fixes: c1cf6d9f743aa ("IB/cm: Call the correct message free functions in cm_send_handler()")
Cc: stable@vger.kernel.org
Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
---
drivers/infiniband/core/cm.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 1a2c2775b14d5..67efb70ba58d1 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -3768,6 +3768,13 @@ static void cm_process_send_error(struct cm_id_private *cm_id_priv,
cm_deref_id(cm_id_priv);
return;
}
+
+ /*
+ * cm_free_priv_msg() drops the reference the message held, so hold one
+ * across the callback below or a concurrent ib_destroy_cm_id() can free
+ * the id underneath it.
+ */
+ refcount_inc(&cm_id_priv->refcount);
cm_free_priv_msg(msg);
if (state != cm_id_priv->id.state || wc_status == IB_WC_SUCCESS ||
@@ -3802,11 +3809,13 @@ static void cm_process_send_error(struct cm_id_private *cm_id_priv,
/* No other events can occur on the cm_id at this point. */
ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, &cm_event);
+ cm_deref_id(cm_id_priv);
if (ret)
ib_destroy_cm_id(&cm_id_priv->id);
return;
out_unlock:
spin_unlock_irq(&cm_id_priv->lock);
+ cm_deref_id(cm_id_priv);
}
static void cm_send_handler(struct ib_mad_agent *mad_agent,
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH] IB/cm: hold a cm_id reference across the send error callback
2026-10-24 15:55 [PATCH] IB/cm: hold a cm_id reference across the send error callback Yehyeong Lee
@ 2026-10-24 16:01 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-10-24 16:01 UTC (permalink / raw)
To: Yehyeong Lee; +Cc: linux-rdma
> cm_process_send_error() lets cm_free_priv_msg() release the reference the
> message held on the cm_id and then keeps using cm_id_priv, dropping
> cm_id_priv->lock and calling the consumer's cm_handler() callback. If that
> was the last reference, a concurrent ib_destroy_cm_id() takes the count to
> zero and returns while the callback is still running, so the consumer frees
> its context underneath it. For an RDMA CM consumer that context is the
> rdma_id_private which _destroy_id() kfree()s once ib_destroy_cm_id() has
> returned, and the callback reads it through cm_id->context before any state
> check.
>
> Reaching this needs a send that expects a response, so a REQ, REP, DREQ or
> SIDR_REQ, to complete with a real error rather than a flush, and the
> consumer to destroy the id while that error is being processed.
>
> The report below comes from an out-of-tree KUnit test. The race is a few
> [ ... ]
> path.
>
> Fixes: c1cf6d9f743aa ("IB/cm: Call the correct message free functions in cm_send_handler()")
> Cc: stable@vger.kernel.org
> Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261024155507.3418345-1-yhlee@isslab.korea.ac.kr?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-24 16:01 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-24 15:55 [PATCH] IB/cm: hold a cm_id reference across the send error callback Yehyeong Lee
2026-10-24 16:01 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox