From: Jakub Kicinski <kuba@kernel.org>
To: steffen.klassert@secunet.com
Cc: Jakub Kicinski <kuba@kernel.org>,
davem@davemloft.net, herbert@gondor.apana.org.au,
netdev@vger.kernel.org
Subject: Re: [PATCH 04/12] xfrm: iptfs: fix runt reassembly panic from short inner tot_len
Date: Tue, 8 Sep 2026 15:48:55 -0700 [thread overview]
Message-ID: <20260908224855.1591147-1-kuba@kernel.org> (raw)
In-Reply-To: <20260907093020.2228346-5-steffen.klassert@secunet.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
xfrm: iptfs: fix runt reassembly panic from short inner tot_len
This tightens the runt continuation path in iptfs_reassem_cont() so the
declared inner IP length must cover at least the IP header size before the
in-progress reassembly skb is allocated, replacing the old check against
sizeof(xtfs->ra_runt). A few questions below on how completely this matches
the in-place validation, plus two pre-existing observations in the same
function.
> diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c
> index 2ce15c472cc4d..6920940a35b49 100644
> --- a/net/xfrm/xfrm_iptfs.c
> +++ b/net/xfrm/xfrm_iptfs.c
> @@ -828,8 +828,8 @@ static u32 iptfs_reassem_cont(struct xfrm_iptfs_data *xtfs, u64 seq,
> * allocate an in progress skb
> */
> ipremain = __iptfs_iplen(xtfs->ra_runt);
> - if (ipremain < sizeof(xtfs->ra_runt)) {
> - /* length has to be at least runtsize large */
> + if (ipremain < __iptfs_iphlen(xtfs->ra_runt)) {
> + /* length has to be at least the IP header size */
[Severity: Medium]
The changelog says this will "Align the runt path with the normal path by
requiring the declared inner length to cover at least the IP header size."
Is that alignment complete for IPv4 with options?
__iptfs_iphlen() never looks at iph->ihl:
net/xfrm/xfrm_iptfs.c:__iptfs_iphlen() {
if (iph->version == 0x4)
return sizeof(*iph);
return sizeof(struct ipv6hdr);
}
The in-place parser uses the declared header length instead:
net/xfrm/xfrm_iptfs.c:__input_process_payload() {
...
iplen = be16_to_cpu(iph->tot_len);
iphlen = iph->ihl << 2;
if (iplen < iphlen || iphlen < sizeof(*iph)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
...
}
So a runt-started inner IPv4 packet declaring ihl < 5, or ihl * 4 greater
than tot_len (for example tot_len 20 with ihl 15), passes the new check,
gets a 20 byte allocation, is treated as completely reassembled and is
handed to iptfs_complete_inner_skb() and xfrm_input(), while byte-identical
input arriving wholly inside one outer payload is rejected with
LINUX_MIB_XFRMINHDRERROR. Should the runt path also test iph->ihl so the
two parsers agree on both acceptance and MIB accounting?
As far as I can tell the residual effect is bounded, since ip_rcv_core()
drops such a packet:
net/ipv4/ip_input.c:ip_rcv_core() {
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;
...
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;
}
The panic class described in the changelog (tot_len in [6, 19] making
"ipremain -= copylen" underflow) does look closed by this change for the
runt path.
Also, the kernel-doc on __iptfs_iphlen() says it returns "the IP header
size based on the IP version", and the new comment here says "length has to
be at least the IP header size". For IPv4 with options that is the minimum
header size rather than the header size. Would it be worth rewording either
the comment or the helper's kernel-doc?
> XFRM_INC_STATS(xs_net(xtfs->x),
> LINUX_MIB_XFRMINIPTFSERROR);
> goto abandon;
Two further observations in this same function, both outside the hunk.
[Severity: High]
This isn't a bug introduced by this patch, but can the copy fallback later
in iptfs_reassem_cont() still reach skb_over_panic() on the normal
(non-runt) path?
net/xfrm/xfrm_iptfs.c:iptfs_reassem_cont() {
} else {
if (skb_linearize(newskb)) {
XFRM_INC_STATS(xs_net(xtfs->x),
LINUX_MIB_XFRMINBUFFERERROR);
goto abandon;
}
/* copy fragment data into newskb */
if (skb_copy_seq_read(st, data, skb_put(newskb, copylen),
copylen)) {
...
}
There is no tailroom check before skb_put(newskb, copylen), and copylen is
min(min(blkoff, remaining), ipremain), so it is bounded only by the outer
payload length and the declared inner IP length.
When the in-progress skb came from the frag-sharing path, its linear head
is only iphlen plus XFRM_IPTFS_MIN_L2HEADROOM:
net/xfrm/xfrm_iptfs.c:iptfs_pskb_add_frags() {
skb = iptfs_alloc_skb(tpl, copy_len, false);
...
}
reached from __input_process_payload():
!(skb = iptfs_pskb_add_frags(first_skb, fragwalk,
data, capturelen,
skbseq, iphlen))) {
and skb_linearize() only adds about 128 bytes of tailroom:
net/core/skbuff.c:__pskb_pull_tail() {
int i, k, eat = (skb->tail + delta) - skb->end;
...
if (eat > 0 || skb_cloned(skb)) {
if (pskb_expand_head(skb, 0, eat > 0 ? eat + 128 : 0,
GFP_ATOMIC))
...
}
A continuation outer packet that is a plain kmalloc'd linear skb fails the
sharing precondition (head_frag is 0 and skb->len != skb->data_len), so it
lands in this fallback. With a roughly 1420 byte reassembly the head ends
up with a couple of hundred bytes of tailroom, and a continuation carrying
several hundred bytes more makes skb->tail exceed skb->end, which
skb_put() turns into skb_over_panic() and BUG(). Would guaranteeing the
tailroom (pskb_expand_head() or a skb_tailroom() test against ipremain)
before the skb_put() be appropriate here? This path still appears to be
present at the tip of the series.
[Severity: Low]
This is a pre-existing asymmetry rather than something this patch changes,
but should the runt path also arm the drop timer when it publishes
xtfs->ra_newskb?
net/xfrm/xfrm_iptfs.c:iptfs_reassem_cont() {
newskb = iptfs_alloc_skb(skb, ipremain, false);
if (!newskb) {
XFRM_INC_STATS(xs_net(xtfs->x), LINUX_MIB_XFRMINERROR);
goto abandon;
}
xtfs->ra_newskb = newskb;
...
}
The reassembly-start site in __input_process_payload() does arm it:
xtfs->ra_newskb = skb;
xtfs->ra_wantseq = seq + 1;
if (!hrtimer_is_queued(&xtfs->drop_timer)) {
hrtimer_start(&xtfs->drop_timer,
xtfs->drop_time_ns,
IPTFS_HRTIMER_MODE);
}
So a reassembly started from the runt path has no expiry, and if the peer
never sends the next sequence the skb (sized by the declared inner length,
up to about 64KB) stays pinned on the SA until another packet arrives or
the state goes away. The memory is reclaimed at teardown:
net/xfrm/xfrm_iptfs.c:iptfs_destroy_state() {
hrtimer_cancel(&xtfs->drop_timer);
if (xtfs->ra_newskb)
kfree_skb(xtfs->ra_newskb);
...
}
so this looks like retention past the intended drop time rather than a
leak. The publication itself is serialized, since iptfs_input_ordered()
holds xtfs->drop_lock across the iptfs_reassem_cont() call.
next prev parent reply other threads:[~2026-09-08 22:49 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-07 9:29 [PATCH 0/12] pull request (net): ipsec 2026-09-07 Steffen Klassert
2026-09-07 9:29 ` [PATCH 01/12] xfrm: iptfs: fix stack OOB read in iptfs_skb_reset_frag_walk() Steffen Klassert
2026-09-08 22:48 ` Jakub Kicinski
2026-09-07 9:29 ` [PATCH 02/12] xfrm: serialize state GC with device state flush Steffen Klassert
2026-09-08 22:48 ` Jakub Kicinski
2026-09-07 9:29 ` [PATCH 03/12] xfrm: add missing RCU read lock in xfrm_send_migrate_state() Steffen Klassert
2026-09-07 9:29 ` [PATCH 04/12] xfrm: iptfs: fix runt reassembly panic from short inner tot_len Steffen Klassert
2026-09-08 22:48 ` Jakub Kicinski [this message]
2026-09-07 9:29 ` [PATCH 05/12] ipv6: xfrm: use full sockets in local error paths Steffen Klassert
2026-09-08 22:48 ` Jakub Kicinski
2026-09-07 9:29 ` [PATCH 06/12] xfrm: fix compat ALLOCSPI request use-after-free Steffen Klassert
2026-09-07 9:29 ` [PATCH 07/12] xfrm: add missing rcu_read_lock(), skb_dst_force() and dev_hold() for xfrm_trans_reinject() Steffen Klassert
2026-09-08 22:48 ` Jakub Kicinski
2026-09-07 9:29 ` [PATCH 08/12] xfrm: use hlist_del_init_rcu for state_cache and state_cache_input Steffen Klassert
2026-09-08 22:48 ` Jakub Kicinski
2026-09-07 9:29 ` [PATCH 09/12] esp: downgrade zerocopy managed frags before mutating skb frags Steffen Klassert
2026-09-08 22:49 ` Jakub Kicinski
2026-09-07 9:29 ` [PATCH 10/12] xfrm: hold net_device reference under RCU in bundle creation Steffen Klassert
2026-09-08 22:49 ` Jakub Kicinski
2026-09-07 9:29 ` [PATCH 11/12] xfrm: save input state data before secpath resets Steffen Klassert
2026-09-07 9:29 ` [PATCH 12/12] net: xfrm: reject unrepresentable espintcp transport headers Steffen Klassert
2026-09-08 22:49 ` Jakub Kicinski
2026-09-09 6:38 ` Some clarifications on the upstreaming process (was: [PATCH 0/12] pull request (net): ipsec 2026-09-07) Steffen Klassert
2026-09-09 9:23 ` Some clarifications on the upstreaming process Paolo Abeni
2026-09-09 10:22 ` Matthieu Baerts
2026-09-10 8:17 ` Steffen Klassert
2026-09-10 8:35 ` Matthieu Baerts
2026-09-10 9:28 ` Steffen Klassert
2026-09-09 10:23 ` Steffen Klassert
2026-09-09 10:34 ` Paolo Abeni
2026-09-09 10:44 ` Steffen Klassert
2026-09-09 18:57 ` Jakub Kicinski
2026-09-10 8:29 ` Matthieu Baerts
2026-09-10 9:02 ` Steffen Klassert
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=20260908224855.1591147-1-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=davem@davemloft.net \
--cc=herbert@gondor.apana.org.au \
--cc=netdev@vger.kernel.org \
--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