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 09/12] esp: downgrade zerocopy managed frags before mutating skb frags
Date: Tue, 8 Sep 2026 15:49:00 -0700 [thread overview]
Message-ID: <20260908224900.1591301-1-kuba@kernel.org> (raw)
In-Reply-To: <20260907093020.2228346-10-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.
---
esp: downgrade zerocopy managed frags before mutating skb frags
This adds a skb_zcopy_downgrade_managed() call to the out-of-place ESP
output path in esp_output_head() and esp6_output_head(), so that the frag
array is no longer mutated while SKBFL_MANAGED_FRAG_REFS is set. Real
page references are taken on the existing frags before ESP appends its
trailer frag.
> diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
> index a6c18aea7498d..e76db5817e78e 100644
> --- a/net/ipv4/esp4.c
> +++ b/net/ipv4/esp4.c
> @@ -441,6 +441,12 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
>
> esp->inplace = false;
>
> + /* Take real page refs and clear SKBFL_MANAGED_FRAG_REFS before
> + * we mutate the frag array, so the per-frag unref stays balanced
> + * for zerocopy managed frags (see __ip_append_data()).
> + */
> + skb_zcopy_downgrade_managed(skb);
> +
[Severity: High]
This isn't a bug introduced by this patch, but does the sibling xfrm
transform need the same treatment? ipcomp_post_acomp() in
net/xfrm/xfrm_ipcomp.c rewrites the frag array too, and there is no
skb_zcopy_downgrade_managed() anywhere under net/xfrm/.
ipcomp_setup_req() has a fast path that deliberately avoids any cow, so
the implicit downgrade inside pskb_expand_head() never runs:
net/xfrm/xfrm_ipcomp.c:ipcomp_setup_req() {
...
if (!skb_cloned(skb) && skb_headlen(skb) >= minhead) {
if (!skb_is_nonlinear(skb)) {
nfrags = 1;
break;
} else if (!skb_has_frag_list(skb)) {
nfrags = skb_shinfo(skb)->nr_frags;
nfrags++;
break;
}
}
...
}
ipcomp_post_acomp() then drops the old frags and installs its own pages:
net/xfrm/xfrm_ipcomp.c:ipcomp_post_acomp() {
...
pskb_trim_unique(skb, 0);
__skb_put(skb, hlen);
...
skb_frag_fill_page_desc(frag, page, 0, len);
skb_shinfo(skb)->nr_frags++;
} while ((dlen -= len));
...
}
Since the skb is not cloned here, ___pskb_trim() takes the drop_pages
path, and skb_frag_unref() is a no-op while SKBFL_MANAGED_FRAG_REFS is
set, so the flag survives the trim. The pages installed afterwards come
from alloc_page() in ipcomp_setup_req(), i.e. they carry real references.
At release time skb_release_data() then skips the unref loop:
net/core/skbuff.c:skb_release_data() {
...
if (skb_zcopy(skb)) {
bool skip_unref = shinfo->flags & SKBFL_MANAGED_FRAG_REFS;
skb_zcopy_clear(skb, true);
if (skip_unref)
goto free_head;
}
...
}
Does this leak the compression output pages, one or more order-0 pages per
transmitted IPComp packet? That looks like the same failure mode the
commit message describes for ESP ("leaking the x->xfrag page at packet
rate"), just reached through IPComp instead.
[Severity: Medium]
This is a pre-existing issue, but the patch adds a second irreversible
side effect at the same point, so it may be worth handling here.
esp->inplace = false is committed (and now the downgrade as well) before
the allocation that can fail:
net/ipv4/esp4.c:esp_output_head() {
...
esp->inplace = false;
...
skb_zcopy_downgrade_managed(skb);
allocsize = ALIGN(tailen, L1_CACHE_BYTES);
spin_lock_bh(&x->lock);
if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) {
spin_unlock_bh(&x->lock);
goto cow;
}
...
}
On the refill failure, should esp->inplace be restored to true before the
goto cow?
The cow path calls skb_cow_data(), which pulls all paged data into the
linear area via __pskb_pull_tail(), and since the branch required
!skb_has_frag_list(skb) it returns with *trailer == skb. pskb_put() then
only extends the linear region, so the skb leaves esp_output_head() with
nr_frags == 0 and data_len == 0.
esp_output_tail() still branches only on esp->inplace:
net/ipv4/esp4.c:esp_output_tail() {
...
if (!esp->inplace) {
int allocsize;
struct page_frag *pfrag = &x->xfrag;
allocsize = ALIGN(skb->data_len, L1_CACHE_BYTES);
...
skb_shinfo(skb)->nr_frags = 1;
page = pfrag->page;
get_page(page);
__skb_fill_page_desc(skb, 0, page, pfrag->offset, skb->data_len);
...
}
With data_len == 0 the refill of size 0 succeeds trivially, nr_frags is
forced to 1 and a zero-length frag is installed with an extra page
reference, while pfrag->offset is not advanced. Can this hand a zero
length fragment to the driver, and issue the out-of-place AEAD request
with src and dst scatterlists that both describe the same linear buffer?
> allocsize = ALIGN(tailen, L1_CACHE_BYTES);
>
> spin_lock_bh(&x->lock);
> diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
> index 72ec0d7d11209..b1c9b36f76dc4 100644
> --- a/net/ipv6/esp6.c
> +++ b/net/ipv6/esp6.c
> @@ -471,6 +471,12 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
>
> esp->inplace = false;
>
> + /* Take real page refs and clear SKBFL_MANAGED_FRAG_REFS before
> + * we mutate the frag array, so the per-frag unref stays balanced
> + * for zerocopy managed frags (see __ip_append_data()).
> + */
> + skb_zcopy_downgrade_managed(skb);
> +
[Severity: Medium]
This is a pre-existing issue, but the same missing esp->inplace rollback
applies to the IPv6 path: esp6_output_head() commits esp->inplace = false
before the skb_page_frag_refill() that can goto cow, and esp6_output_tail()
branches only on esp->inplace.
For reference, net/ipv4/esp4.c and net/ipv6/esp6.c are unchanged in the
rest of this series, so the stale esp->inplace behaviour is still present
at its end.
> allocsize = ALIGN(tailen, L1_CACHE_BYTES);
>
> spin_lock_bh(&x->lock);
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
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 [this message]
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=20260908224900.1591301-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