From: Qihang <q.h.hack.winter@gmail.com>
To: netdev@vger.kernel.org
Cc: steffen.klassert@secunet.com, chopps@labn.net,
herbert@gondor.apana.org.au, davem@davemloft.net,
edumazet@kernel.org, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, dsahern@kernel.org, idosch@nvidia.com,
linux-kernel@vger.kernel.org
Subject: [PATCH net v8 0/3] xfrm: fix vti tunnel device use-after-free across async crypto resumption
Date: Fri, 6 Nov 2026 09:57:58 +0800 [thread overview]
Message-ID: <20261107111617.21347-1-q.h.hack.winter@gmail.com> (raw)
This replaces the earlier vti/vti6 series and the reference-transfer
rework.
vti_input() and vti6_input_proto() cache the tunnel pointer in the skb
control buffer without taking a reference. With async crypto, the
receive callback runs from crypto completion and may run after the
tunnel device has been torn down, causing a use-after-free when the
callback dereferences the cached tunnel.
The input hook takes a reference on the tunnel device when caching the
tunnel. A successful receive callback leaves the cached tunnel in
place; xfrm_input() releases the reference on every exit, including
after delivery and on drop paths. The error callback only updates the
error counters, so the common drop path releases the tunnel reference
after the skb has been freed. IPTFS, which can free or consume the
outer skb itself, releases the reference through xfrm_input_drop() or
xfrm_input_consume(). Patch 1 makes the two exits that still used
skb->dev after the async reference was dropped use it first. Patch 3
sets the address family before the early invalid-state drop, which now
has to release the cached tunnel.
Patch 2 is new. The IPTFS drop timer is cancelled when an in-progress
reassembly completes, although the same timer ages out the reorder
window. A window that is never drained holds its queued packets and,
with patch 3, the tunnel reference they carry, so the device could not
be unregistered. Patch 2 rearms the timer for the window's own deadline
once the reassembly is done, so that a later reassembly does not
inherit the earlier expiry.
Patch 2 is deliberately minimal. It only bounds the stale window, which
is what 3/3 needs so that the reference it adds cannot pin the device; a
wider rework of the IPTFS timer is orthogonal and can land on its own.
Rebased onto net main fafbc4051, which already carries the iptfs skb
leak fix on the payload error path. That fix moved the done label ahead
of the delivery loop, so the inner packets parsed before a validation
failure are now delivered. The drain that the previous revision added
to 3/3 is therefore gone.
The issue was found by source audit of the VTI input path (LLM-assisted
review): the tunnel pointer cached in the skb control buffer is
dereferenced by the receive callback, which with async crypto can run
after the device has been torn down, and nothing keeps the device alive
across that window. It was confirmed with a KASAN reproducer on arm64
(async ESP; deleting the VTI device inside the crypto window gives
"slab-use-after-free in vti_rcv_cb").
Changes since v7:
- rebased onto net main fafbc4051. The drain of @sublist added in v5 is
dropped: "xfrm: iptfs: fix skb leak on error in
__input_process_payload()" is in net now and delivers those packets.
- new patch 2: hand the IPTFS drop timer back to the reorder window when a
reassembly completes, so a stalled window cannot pin the state (and the
tunnel reference added by patch 3) forever; reported by the AI review
of v7.
- the former 2/3 (address family before the invalid-state check) is
folded into 3/3, because the early drop path in 3/3 needs the family
to release the cached tunnel. This gives a clean bisect: no
intermediate commit sets the family without using it.
- the sentence in 3/3 that claimed the parked skbs are always released by
the drop timer is gone; patch 2 is what makes that true.
Changes since v6:
- patch 1 also drops the skb->dev reference that is held across an async
resumption only after gro_cells_receive() in the decaps success exit;
that exit still used skb->dev after the reference was dropped (found
by the netdev AI review).
Changes since v5:
- cover letter only: record how the issue was found.
Changes since v4:
- IPTFS: when an inner packet fails validation, __input_process_payload()
can reach the drop path before the inner packets queued for delivery
are sent. Drop those queued packets as well so the outer skb's tunnel
device reference is not leaked (found by the Sashiko review of v4 3/3).
Changes since v3:
- the cached pointer is always a tunnel; the address family selects its
type, and a non-NULL tunnel slot denotes one held device reference
- xfrm_input() takes the tunnel device out of the skb before
gro_cells_receive()/transport_finish(), which may consume the skb
- the device reference is dropped only after the skb has been freed,
including on error paths
- IPTFS passes the state's address family explicitly instead of
inferring it from the secpath; consumed skbs keep consume_skb()
semantics and drop paths keep their skb drop reason
- drop the xfrmi changes: it re-looks up the interface and does not
participate in the reference transfer
- merge the reference and IPTFS changes into one patch so the series
remains bisectable
- the gro_cells NAPI window is pre-existing and is documented, not
changed
Runtime testing (aarch64 QEMU; KASAN for UAF detection):
- VTI async success and error: RTM_DELLINK completes without a KASAN
report; the unfixed tree reports slab-use-after-free in vti_rcv_cb.
- Async resume with the SA deleted during the crypto window: the
reference is released and the device is removed; with patch 3
reverted, xfrm_input_take_tunnel_dev() warns and
unregister_netdevice() waits for vti0.
- IPTFS outer-skb free: RTM_DELLINK completes; with the
!first_skb xfrm_input_drop() reverted to kfree_skb(), the reference
leaks and unregister_netdevice() waits for vti0.
The IPTFS drop-timer/reorder/state-teardown paths, including the
window-pin sequence behind patch 2, are covered by code audit, not by
dedicated runtime triggers.
v8 was build-tested on fafbc4051: the arm64 objects touched by the
series (xfrm, xfrm_iptfs, ip_vti, ip6_vti, xfrm6_input) compile with
XFRM, IPTFS, VTI and ESP enabled.
Tooling: the source audit, the patch text and parts of these messages
were produced with an AI coding assistant under my direction. I reviewed
the result and stand behind every line of it; the build and runtime
results above were obtained from earlier revisions of these changes.
Previous versions:
https://lore.kernel.org/netdev/20261027105031.19928-1-q.h.hack.winter@gmail.com/ (v7)
https://lore.kernel.org/netdev/20261022032129.16118-1-q.h.hack.winter@gmail.com/ (v6)
https://lore.kernel.org/netdev/20261019153247.89957-1-q.h.hack.winter@gmail.com/ (v5)
https://lore.kernel.org/netdev/20261018090638.55686-1-q.h.hack.winter@gmail.com/ (v4)
https://lore.kernel.org/netdev/20261013074610.17808-1-q.h.hack.winter@gmail.com/ (v2)
https://lore.kernel.org/netdev/20260930090813.73901-1-q.h.hack.winter@gmail.com/ (v1)
Qihang (3):
xfrm: use skb->dev before dropping the reference held across async
xfrm: iptfs: hand the drop timer back to the reorder window
xfrm: hold the tunnel device reference across the input path
net/ipv4/ip_vti.c | 3 ++
net/ipv6/ip6_vti.c | 2 ++
net/ipv6/xfrm6_input.c | 3 ++
net/xfrm/xfrm_inout.h | 4 +++
net/xfrm/xfrm_input.c | 69 +++++++++++++++++++++++++++++++++++++++---
net/xfrm/xfrm_iptfs.c | 40 +++++++++++++++---------
6 files changed, 103 insertions(+), 18 deletions(-)
--
2.54.0 (Apple Git-157)
next reply other threads:[~2026-11-07 3:16 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 1:57 Qihang [this message]
2026-10-14 13:17 ` [PATCH net v8 1/3] xfrm: use skb->dev before dropping the reference held across async Qihang
2026-10-31 2:07 ` [PATCH net v8 2/3] xfrm: iptfs: hand the drop timer back to the reorder window Qihang
2026-10-31 2:09 ` [PATCH net v8 3/3] xfrm: hold the tunnel device reference across the input path Qihang
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=20261107111617.21347-1-q.h.hack.winter@gmail.com \
--to=q.h.hack.winter@gmail.com \
--cc=chopps@labn.net \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@kernel.org \
--cc=herbert@gondor.apana.org.au \
--cc=horms@kernel.org \
--cc=idosch@nvidia.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®