mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 19:13:39 -04:00
net/smc: fix UAF in smc_cdc_rx_handler() by pinning the socket
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: d7b0e37c1a ("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>
This commit is contained in:
@@ -470,9 +470,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
|
||||
{
|
||||
struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
|
||||
struct smc_cdc_msg *cdc = buf;
|
||||
struct smc_sock *smc = NULL;
|
||||
struct smc_connection *conn;
|
||||
struct smc_link_group *lgr;
|
||||
struct smc_sock *smc;
|
||||
|
||||
if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved))
|
||||
return; /* short message */
|
||||
@@ -483,21 +483,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
|
||||
lgr = smc_get_lgr(link);
|
||||
read_lock_bh(&lgr->conns_lock);
|
||||
conn = smc_lgr_find_conn(ntohl(cdc->token), lgr);
|
||||
read_unlock_bh(&lgr->conns_lock);
|
||||
if (!conn || conn->out_of_sync)
|
||||
if (!conn || conn->out_of_sync) {
|
||||
read_unlock_bh(&lgr->conns_lock);
|
||||
return;
|
||||
}
|
||||
smc = container_of(conn, struct smc_sock, conn);
|
||||
sock_hold(&smc->sk);
|
||||
read_unlock_bh(&lgr->conns_lock);
|
||||
|
||||
if (cdc->prod_flags.failover_validation) {
|
||||
smc_cdc_msg_validate(smc, cdc, link);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
if (smc_cdc_before(ntohs(cdc->seqno),
|
||||
conn->local_rx_ctrl.seqno))
|
||||
/* received seqno is old */
|
||||
return;
|
||||
goto out;
|
||||
|
||||
smc_cdc_msg_recv(smc, cdc);
|
||||
out:
|
||||
sock_put(&smc->sk);
|
||||
}
|
||||
|
||||
static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = {
|
||||
|
||||
Reference in New Issue
Block a user