mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 14:04:27 -04:00
net: smc: fix splice entry lifetime imbalance in smc_rx_splice
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: 9014db202c ("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>
This commit is contained in:
committed by
Jakub Kicinski
parent
1cb4298810
commit
5d9686af29
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user