From: Cen Zhang <zzzccc427@gmail.com>
To: steffen.klassert@secunet.com, herbert@gondor.apana.org.au,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com
Cc: horms@kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, antony.antony@secunet.com,
baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com
Subject: [PATCH ipsec v2 1/2] xfrm: protect offload statistics updates from device teardown
Date: Sat, 31 Oct 2026 11:28:48 +0800 [thread overview]
Message-ID: <pm-xfrm-core-objects-candidate-0050-v4-3c6b8c5369a802e9be0e-1@gmail.com> (raw)
In-Reply-To: <pm-xfrm-core-objects-candidate-0050-v4-3c6b8c5369a802e9be0e-0@gmail.com>
copy_to_user_state() refreshes the statistics of an offloaded SA through
the driver's xdo_dev_state_update_stats() callback. GETSA and SA
notifications run it with only a reference to the xfrm_state, which does
not keep the offload binding alive, and the netlink configuration mutex
does not serialize them with the netdevice notifier.
NETDEV_DOWN and NETDEV_UNREGISTER flush the device's states with
xfrm_dev_state_flush(). It deletes each state under x->lock and then
calls xfrm_dev_state_free() without that lock, which lets the driver
free its offload state, clears xso.dev and drops the device reference.
A refresh that has already seen xso.dev set can then run the driver
callback on freed offload state or after xso.dev became NULL, and can
read the net_device after unregistration has freed it:
GETSA Device teardown (RTNL)
----- ----------------------
look up and reference x
copy_to_user_state(x)
x->xso.dev is set
xfrm_dev_state_flush()
xfrm_state_delete(x)
xfrm_dev_state_free(x)
xdo_dev_state_free()
clear x->xso.dev
netdev_put(dev)
unregister frees dev
xfrm_dev_state_update_stats(x)
reads dev->xfrmdev_ops
The expiry timer has the same problem. It refreshes the statistics
before it checks for a dead state, and it can still fire after the
flush has deleted the state.
Every path that frees the offload state of a reachable SA marks the SA
dead under x->lock first. Refresh the statistics only for SAs that are
not dead, with x->lock held, so a refresh either completes before the
teardown or sees the dead state. GETSA, ALLOCSPI replies and SA
notifications now take x->lock around the copy. Expire notifications
already run under x->lock. Dumps run under xfrm_state_lock while the SA
is still linked, and deletion needs that lock before the offload state
can be freed. The timer now checks for a dead state before the refresh.
A dead SA keeps the counters from its last refresh.
The report below was collected on an earlier kernel with a built-in test
device that supplies no-op offload add and delete callbacks. Scheduling
probes paused GETSA after it sampled the device, then ran normal
link-down and link-delete requests before resuming the read.
KASAN report as below:
==================================================================
BUG: KASAN: slab-use-after-free in copy_to_user_state+0x6ce/0x6e0
Read of size 8 at addr ffff888104be0308 by task ip/446
Call Trace:
<TASK>
dump_stack_lvl+0x93/0xd0
print_report+0xce/0x630
kasan_report+0xe0/0x110
copy_to_user_state+0x6ce/0x6e0
copy_to_user_state_extra+0x8a/0x1450
dump_one_state+0x1fd/0x440
xfrm_state_netlink+0xda/0x150
xfrm_get_sa+0x12d/0x320
xfrm_user_rcv_msg+0x3b3/0xc20
netlink_rcv_skb+0x133/0x390
xfrm_netlink_rcv+0x76/0x90
netlink_unicast+0x504/0x840
netlink_sendmsg+0x7f7/0xcf0
____sys_sendmsg+0x88b/0x9d0
___sys_sendmsg+0x125/0x1d0
__sys_sendmsg+0x13e/0x1e0
do_syscall_64+0x115/0x6a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
</TASK>
Allocated by task 420:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0xaa/0xb0
__kvmalloc_node_noprof+0x353/0x930
alloc_netdev_mqs+0xa0/0x1250
rtnl_create_link+0xb8e/0xeb0
rtnl_newlink+0xb7b/0x1f40
rtnetlink_rcv_msg+0x7b9/0xce0
netlink_rcv_skb+0x133/0x390
netlink_unicast+0x504/0x840
netlink_sendmsg+0x7f7/0xcf0
____sys_sendmsg+0x88b/0x9d0
___sys_sendmsg+0x125/0x1d0
__sys_sendmsg+0x13e/0x1e0
do_syscall_64+0x115/0x6a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 451:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x5f/0x80
kfree+0x307/0x580
device_release+0xd7/0x280
kobject_put+0x1ad/0x610
netdev_run_todo+0x927/0x13d0
rtnl_dellink+0x3aa/0xa30
rtnetlink_rcv_msg+0x7b9/0xce0
netlink_rcv_skb+0x133/0x390
netlink_unicast+0x504/0x840
netlink_sendmsg+0x7f7/0xcf0
____sys_sendmsg+0x88b/0x9d0
___sys_sendmsg+0x125/0x1d0
__sys_sendmsg+0x13e/0x1e0
do_syscall_64+0x115/0x6a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888104be0000
which belongs to the cache kmalloc-4k of size 4096
The buggy address is located 776 bytes inside of
freed 4096-byte region [ffff888104be0000, ffff888104be1000)
==================================================================
Fixes: 07b87f9eea0c ("xfrm: Fix unregister netdevice hang on hardware offload.")
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
Assisted-by: LLM
net/xfrm/xfrm_state.c | 4 ++--
net/xfrm/xfrm_user.c | 10 +++++++++-
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 0555e5d796ed..1aeb6208ea8f 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -649,10 +649,10 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
int err = 0;
spin_lock(&x->lock);
- xfrm_dev_state_update_stats(x);
-
if (x->km.state == XFRM_STATE_DEAD)
goto out;
+
+ xfrm_dev_state_update_stats(x);
if (x->km.state == XFRM_STATE_EXPIRED)
goto expired;
if (x->lft.hard_add_expires_seconds) {
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index a5fbe38839c4..781d4ec565aa 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1179,7 +1179,11 @@ static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
memcpy(&p->id, &x->id, sizeof(p->id));
memcpy(&p->sel, &x->sel, sizeof(p->sel));
memcpy(&p->lft, &x->lft, sizeof(p->lft));
- if (x->xso.dev)
+ /* Device teardown marks the state dead under x->lock before it frees
+ * the offload state. Callers hold x->lock, or xfrm_state_lock while
+ * the state is still linked.
+ */
+ if (x->xso.dev && x->km.state != XFRM_STATE_DEAD)
xfrm_dev_state_update_stats(x);
memcpy(&p->curlft, &x->curlft, sizeof(p->curlft));
put_unaligned(x->stats.replay_window, &p->stats.replay_window);
@@ -1633,7 +1637,9 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
info.nlmsg_seq = seq;
info.nlmsg_flags = 0;
+ spin_lock_bh(&x->lock);
err = dump_one_state(x, 0, &info);
+ spin_unlock_bh(&x->lock);
if (err) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -4167,7 +4173,9 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c)
p = nla_data(attr);
}
+ spin_lock_bh(&x->lock);
err = copy_to_user_state_extra(x, p, skb);
+ spin_unlock_bh(&x->lock);
if (err)
goto out_free_skb;
next prev parent reply other threads:[~2026-10-31 3:29 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 3:28 [PATCH ipsec v2 0/2] xfrm: fix offload state use racing with " Cen Zhang
2026-10-31 3:28 ` Cen Zhang [this message]
2026-10-31 3:28 ` [PATCH ipsec v2 2/2] xfrm: use cached offload ifindex when migrating an SA Cen Zhang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pm-xfrm-core-objects-candidate-0050-v4-3c6b8c5369a802e9be0e-1@gmail.com \
--to=zzzccc427@gmail.com \
--cc=antony.antony@secunet.com \
--cc=baijiaju1990@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=herbert@gondor.apana.org.au \
--cc=horms@kernel.org \
--cc=jjzuming@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=steffen.klassert@secunet.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®