Skip to content

Commit 1951bff

Browse files
n132gregkh
authored andcommitted
net/smc: fix UAF in smc_cdc_rx_handler() by pinning the socket
[ Upstream commit 9d160b3 ] smc_cdc_rx_handler() looks up the connection by token under the link group's conns_lock, drops the lock, and then dereferences conn and the smc_sock derived from it, ending in sock_hold(&smc->sk) inside smc_cdc_msg_recv(). No reference is held across the lock release. The only reference pinning the socket while the connection is discoverable in the link group is taken in smc_lgr_register_conn() (sock_hold) and dropped in __smc_lgr_unregister_conn() (sock_put), both under conns_lock. Once the handler drops conns_lock, a concurrent close() -> smc_release() -> smc_conn_free() -> smc_lgr_unregister_conn() can drop that reference and free the smc_sock, so the handler's later sock_hold() runs on freed memory: WARNING: lib/refcount.c:25 at refcount_warn_saturate Workqueue: rxe_wq do_work refcount_warn_saturate (lib/refcount.c:25) smc_cdc_msg_recv (net/smc/smc_cdc.c:430) smc_cdc_rx_handler (net/smc/smc_cdc.c:502) smc_wr_rx_tasklet_fn (net/smc/smc_wr.c:445) tasklet_action_common (kernel/softirq.c:938) handle_softirqs (kernel/softirq.c:622) Kernel panic - not syncing: panic_on_warn set Only SMC-R is affected. The SMC-D receive tasklet is stopped by tasklet_kill(&conn->rx_tsklet) in smc_conn_free() before the connection is unregistered, so it cannot run concurrently with the free. Take the socket reference while still holding conns_lock, so the registration reference can no longer be the last one, and drop it once the handler is done. Fixes: d7b0e37 ("net/smc: restructure CDC message reception") Reported-by: Weiming Shi <bestswngs@gmail.com> Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei <xmei5@asu.edu> Link: https://patch.msgid.link/20260630183227.2044998-1-xmei5@asu.edu Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 00f4c36 commit 1951bff

1 file changed

Lines changed: 10 additions & 5 deletions

File tree

net/smc/smc_cdc.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,9 +438,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
438438
{
439439
struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
440440
struct smc_cdc_msg *cdc = buf;
441+
struct smc_sock *smc = NULL;
441442
struct smc_connection *conn;
442443
struct smc_link_group *lgr;
443-
struct smc_sock *smc;
444444

445445
if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved))
446446
return; /* short message */
@@ -451,21 +451,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
451451
lgr = smc_get_lgr(link);
452452
read_lock_bh(&lgr->conns_lock);
453453
conn = smc_lgr_find_conn(ntohl(cdc->token), lgr);
454-
read_unlock_bh(&lgr->conns_lock);
455-
if (!conn || conn->out_of_sync)
454+
if (!conn || conn->out_of_sync) {
455+
read_unlock_bh(&lgr->conns_lock);
456456
return;
457+
}
457458
smc = container_of(conn, struct smc_sock, conn);
459+
sock_hold(&smc->sk);
460+
read_unlock_bh(&lgr->conns_lock);
458461

459462
if (cdc->prod_flags.failover_validation) {
460463
smc_cdc_msg_validate(smc, cdc, link);
461-
return;
464+
goto out;
462465
}
463466
if (smc_cdc_before(ntohs(cdc->seqno),
464467
conn->local_rx_ctrl.seqno))
465468
/* received seqno is old */
466-
return;
469+
goto out;
467470

468471
smc_cdc_msg_recv(smc, cdc);
472+
out:
473+
sock_put(&smc->sk);
469474
}
470475

471476
static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = {

0 commit comments

Comments
 (0)