Re: [PATCH net v3 1/1] xfrm: save input state data before secpath resets

zhilin zou <[email protected]>
Newsgroups org.kernel.vger.netdev
Message-ID <CANB6i5wG6_K_J-MvXetoE0-4B893_V3S43OZ+x5uHjkdwxe+jg@mail.gmail.com>
On Sat, Aug 22, 2026 at 3:03 PM Zhiling Zou <[email protected]> wrote:
>
> From: Zhiling Zou <[email protected]>
>
> xfrm_input() keeps the current state reference through the skb secpath
> while it performs final transport processing. Some input paths can reset
> the secpath before xfrm_input() is done using data from that state.
>
> Receive callback users such as VTI and XFRM interfaces can scrub packets
> that cross network namespaces by resetting the secpath. The

Hi Sashiko,

Thanks for the review. The finding is correct.

vti_rcv_cb() calls skb_scrub_packet(), which resets the skb extensions
before checking xnet. Therefore, the VTI receive path drops the secpath
regardless of whether the packet crosses network namespaces.

The cross-network-namespace qualifier only applies to the XFRM interface
path, where xfrmi_scrub_packet() calls secpath_reset() only when xnet is
true.

I will update the wording in the next revision to distinguish the two
cases. No code change is needed for this finding.

Thanks,
Zhiling

> XFRM_MAX_DEPTH error path can also reset the secpath before the final drop
> callback reports the current state's protocol. If that drops the last
> state reference and the state is concurrently deleted, xfrm_input() can
> still dereference the freed state.
>
> Save the state protocol on the stack while the state is still valid, and
> use the already saved address family for transport_finish(). A larval
> XFRM_STATE_ACQ state has no type, so retain nexthdr as its protocol. This
> preserves the existing drop-path fallback while avoiding the post-reset
> state dereferences without adding an extra state reference to every
> received packet.
>
> Fixes: df3893c176e9 ("vti: Update the ipv4 side to use it's own receive hook.")
> Cc: [email protected]
> Reported-by: Vega <[email protected]>
> Signed-off-by: Zhiling Zou <[email protected]>
> ---
> changes in v3:
> - Retain nexthdr for larval XFRM_STATE_ACQ entries, whose x->type is NULL.
> - Avoid dereferencing x->type before the existing state validity checks.
> - v2 Link: https://lore.kernel.org/all/e59b617d27acb3a38a3523649d52b90f022fa28d.1785288865.git.zhilinz@nebusec.ai/
>
> changes in v2:
> - Avoid per-packet xfrm_state refcounting by saving state data on the stack.
> - Use the saved protocol and family after secpath resets instead of
>   dereferencing x.
> - v1 Link: https://lore.kernel.org/all/89be67de93958a00193c8c4bd668733c976e2aa9.1785135576.git.zhilinz@nebusec.ai/
>
>  net/xfrm/xfrm_input.c | 11 ++++++++---
>  1 file changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
> index eecab337bd0a7..77b9f4d4568d6 100644
> --- a/net/xfrm/xfrm_input.c
> +++ b/net/xfrm/xfrm_input.c
> @@ -474,6 +474,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>         struct xfrm_state *x = NULL;
>         xfrm_address_t *daddr;
>         u32 mark = skb->mark;
> +       u8 xfrm_proto = nexthdr;
>         unsigned int family = AF_UNSPEC;
>         int decaps = 0;
>         int async = 0;
> @@ -485,6 +486,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>         if (encap_type < 0 || (xo && (xo->flags & XFRM_GRO || encap_type == 0 ||
>                                       encap_type == UDP_ENCAP_ESPINUDP))) {
>                 x = xfrm_input_state(skb);
> +               xfrm_proto = x->type ? x->type->proto : nexthdr;
>
>                 if (unlikely(x->km.state != XFRM_STATE_VALID)) {
>                         if (x->km.state == XFRM_STATE_ACQ)
> @@ -592,11 +594,13 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>
>                 x = xfrm_input_state_lookup(net, mark, daddr, spi, nexthdr, family);
>                 if (x == NULL) {
> +                       xfrm_proto = nexthdr;
>                         secpath_reset(skb);
>                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
>                         xfrm_audit_state_notfound(skb, family, spi, seq);
>                         goto drop;
>                 }
> +               xfrm_proto = x->type ? x->type->proto : nexthdr;
>
>                 if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {
>                         secpath_reset(skb);
> @@ -604,6 +608,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>                         xfrm_audit_state_notfound(skb, family, spi, seq);
>                         xfrm_state_put(x);
>                         x = NULL;
> +                       xfrm_proto = nexthdr;
>                         goto drop;
>                 }
>
> @@ -728,7 +733,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>         } while (!err);
>
>         rcu_read_lock();
> -       err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
> +       err = xfrm_rcv_cb(skb, family, xfrm_proto, 0);
>         if (err) {
>                 rcu_read_unlock();
>                 goto drop;
> @@ -753,7 +758,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>                         xfrm_gro = xo->flags & XFRM_GRO;
>
>                 err = -EAFNOSUPPORT;
> -               afinfo = xfrm_state_afinfo_get_rcu(x->props.family);
> +               afinfo = xfrm_state_afinfo_get_rcu(family);
>                 if (likely(afinfo))
>                         err = afinfo->transport_finish(skb, xfrm_gro || async);
>                 if (xfrm_gro) {
> @@ -776,7 +781,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>  drop:
>         if (async)
>                 dev_put(dev);
> -       xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
> +       xfrm_rcv_cb(skb, family, xfrm_proto, -1);
>         kfree_skb(skb);
>         return 0;
>  }
> --
> 2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.