]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: smc: fix splice entry lifetime imbalance in smc_rx_splice
authorDaming Li <d4n.for.sec@gmail.com>
Thu, 30 Jul 2026 14:55:52 +0000 (22:55 +0800)
committerJakub Kicinski <kuba@kernel.org>
Tue, 4 Aug 2026 01:27:51 +0000 (18:27 -0700)
smc_rx_splice() passes pages to splice_to_pipe() before taking the
references that cover the lifetime of each splice entry. In the
VM-backed RMB path, splice_to_pipe() may drop unqueued entries through
smc_rx_spd_release(), while queued entries are released later via the
pipe buffer callback.

The old post-splice accounting also derives the number of queued VM pages
from an offset mutated while building the descriptor, and a multi-page
splice pairs one sock_hold() with multiple sock_put() calls.

Take the page and socket references for every candidate entry before
splice_to_pipe(), and drop the matching private state, page reference,
and socket reference from smc_rx_spd_release() for entries that never
get queued. This fixes a refcount imbalance that can underflow page
refcounts and trigger a use-after-free.

Fixes: 9014db202cb7 ("smc: add support for splice()")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Co-developed-by: Xiao Liu <lx24@stu.ynu.edu.cn>
Signed-off-by: Xiao Liu <lx24@stu.ynu.edu.cn>
Signed-off-by: Daming Li <d4n.for.sec@gmail.com>
Signed-off-by: Ren Wei <enjou1224z@gmail.com>
Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
Link: https://patch.msgid.link/20260730145552.360287-2-enjou1224z@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/smc/smc_rx.c

index c1d9b923938dada934e3fa23696350b94cf49829..5c9e4d8b57de4d0285091ea7499f18cfb8b4cb50 100644 (file)
@@ -150,7 +150,12 @@ static const struct pipe_buf_operations smc_pipe_ops = {
 static void smc_rx_spd_release(struct splice_pipe_desc *spd,
                               unsigned int i)
 {
+       struct smc_spd_priv *priv = (struct smc_spd_priv *)spd->partial[i].private;
+       struct sock *sk = &priv->smc->sk;
+
+       kfree(priv);
        put_page(spd->pages[i]);
+       sock_put(sk);
 }
 
 static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
@@ -209,6 +214,10 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
                        offset = 0;
                }
        }
+       for (i = 0; i < nr_pages; i++) {
+               get_page(pages[i]);
+               sock_hold(&smc->sk);
+       }
        spd.nr_pages_max = nr_pages;
        spd.nr_pages = nr_pages;
        spd.pages = pages;
@@ -217,16 +226,8 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
        spd.spd_release = smc_rx_spd_release;
 
        bytes = splice_to_pipe(pipe, &spd);
-       if (bytes > 0) {
-               sock_hold(&smc->sk);
-               if (!lgr->is_smcd && smc->conn.rmb_desc->is_vm) {
-                       for (i = 0; i < PAGE_ALIGN(bytes + offset) / PAGE_SIZE; i++)
-                               get_page(pages[i]);
-               } else {
-                       get_page(smc->conn.rmb_desc->pages);
-               }
+       if (bytes > 0)
                atomic_add(bytes, &smc->conn.splice_pending);
-       }
        kfree(priv);
        kfree(partial);
        kfree(pages);