All of lore.kernel.org
 help / color / mirror / Atom feed
From: Steffen Klassert <steffen.klassert@secunet.com>
To: Jakub Kicinski <kuba@kernel.org>, Maher Azzouzi <maherazz04@gmail.com>
Cc: <davem@davemloft.net>, <herbert@gondor.apana.org.au>,
	<netdev@vger.kernel.org>
Subject: Re: [PATCH 2/8] esp: do not unref managed frag pages in esp_ssg_unref()
Date: Mon, 3 Aug 2026 18:56:35 +0200	[thread overview]
Message-ID: <anDIQ9-8Sf_6StfZ@secunet.com> (raw)
In-Reply-To: <20260731021544.789075-1-kuba@kernel.org>

Maher, please look at this review of your patch.

Thanks!

On Thu, Jul 30, 2026 at 07:15:44PM -0700, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> --- Patch [2]: [PATCH 2/8] esp: do not unref managed frag pages in esp_ssg_unref() ---
> commit 1913e3a658480a2d9a14804dfb34715f589f403d
> Author: Maher Azzouzi <maherazz04@gmail.com>
> 
> esp: do not unref managed frag pages in esp_ssg_unref()
> 
> The patch adds an early return to esp_ssg_unref() in both esp4.c and esp6.c
> so that no per-frag page reference is dropped when the skb carries managed
> frags (SKBFL_MANAGED_FRAG_REFS), mirroring the skip_unref handling in
> skb_release_data().
> 
> A couple of questions on the commit message and on the scope of the guard.
> 
> >     Fixes: cac2661c53f3 ("esp4: Avoid skb_cow_data whenever possible")
> >     Fixes: 03e2a30f6a27 ("esp6: Avoid skb_cow_data whenever possible")
> 
> [Severity: Medium]
> [Finding: main-1-2]
> [Sources: opus-5]
> 
> Are these the right commits to blame?  Both are dated 2017-01-17, while
> SKBFL_MANAGED_FRAG_REFS and the skb_zcopy_managed() helper used by the new
> guard only arrived with 753f1ca4e1e5 ("net: introduce managed frags
> infrastructure", 2022-07-12, v6.1).
> 
> The esp code as written in 2017 had nothing to be wrong about here; the
> described scenario only becomes possible once managed-frag skbs can reach
> the xfrm output path.
> 
> Since Fixes: tags drive stable and AUTOSEL selection, this points the patch
> at trees older than v6.1, where skb_zcopy_managed() does not exist at all
> (build failure) and where the described scenario cannot happen.  Would the
> commit that made managed-frag skbs reachable from esp_output() be the better
> blame target, or no Fixes: tag plus an explicit stable range?
> 
> > diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
> > index dfc81ee969ae0..fa1710e27e505 100644
> > --- a/net/ipv4/esp4.c
> > +++ b/net/ipv4/esp4.c
> > @@ -104,6 +104,13 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb,
> >  	struct aead_request *req;
> >  	struct scatterlist *sg;
> >  
> > +	/* Managed frags are owned by the zerocopy ubuf; the skb holds no
> > +	 * per-frag page reference, so we must not drop one here.  Mirrors
> > +	 * the SKBFL_MANAGED_FRAG_REFS handling in skb_release_data().
> > +	 */
> > +	if (skb_zcopy_managed(skb))
> > +		return;
> > +
> 
> [Severity: High]
> [Finding: main-1-0]
> [Sources: opus-5]
> 
> Does this early return also suppress the release of the trailer page that
> ESP itself referenced?
> 
> The src scatterlist looks like it has mixed ownership.  Besides the
> ubuf-owned payload frags, esp_output_head() adds a trailer frag with an
> explicit reference in the only branch that sets esp->inplace = false:
> 
> net/ipv4/esp4.c:esp_output_head() {
> 	...
> 			page = pfrag->page;
> 			get_page(page);
> 	...
> 			__skb_fill_page_desc(skb, nfrags, page, pfrag->offset,
> 					     tailen);
> 			skb_shinfo(skb)->nr_frags = ++nfrags;
> 	...
> }
> 
> esp_output_tail() then drops that frag from the skb entirely:
> 
> net/ipv4/esp4.c:esp_output_tail() {
> 	...
> 		skb_shinfo(skb)->nr_frags = 1;
> 
> 		page = pfrag->page;
> 		get_page(page);
> 		/* replace page frags in skb with new page */
> 		__skb_fill_page_desc(skb, 0, page, pfrag->offset, skb->data_len);
> 	...
> }
> 
> so the loop in esp_ssg_unref() appears to be the only remaining release
> site for that trailer reference.  skb_release_data() cannot reach the
> orphaned frag, and for SKBFL_MANAGED_FRAG_REFS it takes the skip_unref
> branch to free_head anyway.
> 
> Both esp_ssg_unref() call sites in esp_output_tail() are affected: the error
> path
> 
> 		if (unlikely(err < 0)) {
> 			esp_ssg_unref(x, tmp, skb, true);
> 
> and the success path
> 
> 	if (sg != dsg)
> 		esp_ssg_unref(x, tmp, skb, false);
> 
> Note the loop only ever ran in exactly that non-inplace case
> (already_unref || req->src != req->dst), which is also the case that adds
> the trailer frag.  So does the get_page() from esp_output_head() stay
> unbalanced for a managed-frag skb?
> 
> Since a single stray reference pins the whole high-order x->xfrag
> page_frag page, would every x->xfrag page used for trailers become
> unfreeable, at packet rate?
> 
> Would it be better to distinguish ubuf-owned frags from the ESP-added
> trailer frag, for instance by calling skb_zcopy_downgrade_managed(skb)
> before ESP mutates the frag array, the way __ip_append_data() and
> __ip6_append_data() do, rather than skipping the unref wholesale?
> 
> Related to that, the new comment says "the skb holds no per-frag page
> reference".  Is that accurate?  It holds true for the ubuf-owned frags, but
> not for the trailer frag ESP added with get_page().
> 
> [Severity: High]
> [Finding: main-1-1]
> [Sources: opus-5]
> 
> This isn't a bug introduced by this patch, but it is the invariant the
> guard works around, and it stays unfixed: esp_output_tail() replaces the
> frag array of a managed-frag skb with a page it references itself, while
> SKBFL_MANAGED_FRAG_REFS remains set:
> 
> net/ipv4/esp4.c:esp_output_tail() {
> 	...
> 		skb_shinfo(skb)->nr_frags = 1;
> 
> 		page = pfrag->page;
> 		get_page(page);
> 		/* replace page frags in skb with new page */
> 		__skb_fill_page_desc(skb, 0, page, pfrag->offset, skb->data_len);
> 	...
> }
> 
> That destination page reference is owned by frag 0, but with the flag still
> set skb_release_data() takes the skip_unref branch:
> 
> 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;
> 	}
> 	...
> }
> 
> so is that reference ever dropped?  Neither net/ipv4/esp4.c nor
> net/ipv6/esp6.c calls skb_zcopy_downgrade_managed(); the canonical way to
> make a managed-frag skb safe for frag mutation and per-frag unref is:
> 
> net/core/skbuff.c:__skb_zcopy_downgrade_managed() {
> 	skb_shinfo(skb)->flags &= ~SKBFL_MANAGED_FRAG_REFS;
> 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
> 		skb_frag_ref(skb, i);
> }
> 
> which every other frag-mutating site uses (net/core/skbuff.c,
> net/ipv4/ip_output.c, net/ipv6/ip6_output.c, net/ipv4/tcp.c).
> 
> Would a single skb_zcopy_downgrade_managed(skb) before ESP touches the
> frags address both the original refcount underflow and this leak, instead
> of leaving a mixed managed/refcounted frag array as an ESP-specific special
> case?  In that light, is the commit message statement "The rest of the
> stack honours this invariant" only half applied here?
> 
> >  	if (x->props.flags & XFRM_STATE_ESN)
> >  		extralen += sizeof(struct esp_output_extra);
> >  
> > diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
> > index 296b57926abb9..7d216b9c59f04 100644
> > --- a/net/ipv6/esp6.c
> > +++ b/net/ipv6/esp6.c
> > @@ -121,6 +121,13 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb,
> >  	struct aead_request *req;
> >  	struct scatterlist *sg;
> >  
> > +	/* Managed frags are owned by the zerocopy ubuf; the skb holds no
> > +	 * per-frag page reference, so we must not drop one here.  Mirrors
> > +	 * the SKBFL_MANAGED_FRAG_REFS handling in skb_release_data().
> > +	 */
> > +	if (skb_zcopy_managed(skb))
> > +		return;
> > +
> 
> [Severity: High]
> [Finding: main-1-0]
> [Sources: opus-5]
> 
> Do both questions above apply identically to the IPv6 copy?
> esp6_output_head() takes the same get_page() for the trailer frag,
> esp6_output_tail() performs the same nr_frags = 1 frag replacement, and both
> esp_ssg_unref() calls (already_unref == true in the skb_to_sgvec() error
> path, and the sg != dsg call after encryption) return early here too.
> 
> >  	if (x->props.flags & XFRM_STATE_ESN)
> >  		extralen += sizeof(struct esp_output_extra);
> >

  reply	other threads:[~2026-08-03 16:56 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-29  6:50 [PATCH 0/8] pull request (net): ipsec 2026-07-29 Steffen Klassert
2026-07-29  6:50 ` [PATCH 1/8] xfrm6: fix out-of-bounds write in xfrm6_input_addr() when secpath is full Steffen Klassert
2026-07-29  6:50 ` [PATCH 2/8] esp: do not unref managed frag pages in esp_ssg_unref() Steffen Klassert
2026-07-31  2:15   ` Jakub Kicinski
2026-08-03 16:56     ` Steffen Klassert [this message]
2026-07-29  6:50 ` [PATCH 3/8] xfrm: espintcp: fix UAF during close Steffen Klassert
2026-07-29  6:50 ` [PATCH 4/8] xfrm: drop ESP-in-TCP packets with no ingress device Steffen Klassert
2026-07-29  6:50 ` [PATCH 5/8] xfrm: avoid lock inversion in nat keepalive work Steffen Klassert
2026-07-31  2:15   ` Jakub Kicinski
2026-08-03 16:58     ` Steffen Klassert
2026-07-29  6:50 ` [PATCH 6/8] xfrm: Fix skb double-free in xfrm_dev_direct_output() Steffen Klassert
2026-07-29  6:50 ` [PATCH 7/8] xfrm: ah6: validate routing header segments_left Steffen Klassert
2026-07-29  6:50 ` [PATCH 8/8] xfrm: fix xfrm_state_construct() auth-trunc leak Steffen Klassert
2026-07-31  2:17 ` [PATCH 0/8] pull request (net): ipsec 2026-07-29 Jakub Kicinski
2026-08-03 16:54   ` 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=anDIQ9-8Sf_6StfZ@secunet.com \
    --to=steffen.klassert@secunet.com \
    --cc=davem@davemloft.net \
    --cc=herbert@gondor.apana.org.au \
    --cc=kuba@kernel.org \
    --cc=maherazz04@gmail.com \
    --cc=netdev@vger.kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.