]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net/smc: fix UAF in smc_cdc_rx_handler() by pinning the socket
authorXiang Mei <xmei5@asu.edu>
Tue, 30 Jun 2026 18:32:27 +0000 (11:32 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Mon, 6 Jul 2026 10:58:12 +0000 (12:58 +0200)
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: d7b0e37c1ac1 ("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>
net/smc/smc_cdc.c

index 619b3bab38248b3ed65f5fc0a8fb625fd310781c..32d6d03df321459f44d3a5284f61e138a7194348 100644 (file)
@@ -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[] = {