Subject: Re: [PATCH v2] xfrm: delay dev_put in xfrm_input to after transport reinject

From: Steffen Klassert <[email-masked]>
Date: Thu, 2 Apr 2026 12:36:12 +0200

On Tue, Mar 31, 2026 at 05:27:37PM +0800, Qi Tang wrote:
> diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
> index f28cfd88eaf5..9765fdc63ffc 100644
> --- a/net/ipv4/xfrm4_input.c
> +++ b/net/ipv4/xfrm4_input.c
> @@ -46,6 +46,28 @@ static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
>  	return NET_RX_DROP;
>  }
>  
> +static int xfrm4_rcv_encap_finish_async(struct net *net, struct sock *sk,
> +					struct sk_buff *skb)
> +{
> +	if (!skb_dst(skb)) {
> +		const struct iphdr *iph = ip_hdr(skb);
> +
> +		if (ip_route_input_noref(skb, iph->daddr, iph->saddr,
> +					 ip4h_dscp(iph), skb->dev))
> +			goto drop;
> +	}
> +
> +	if (xfrm_trans_queue_net(dev_net(skb->dev), skb,
> +				 xfrm4_rcv_encap_finish2, true))
> +		goto drop;
> +
> +	return 0;
> +drop:
> +	dev_put(skb->dev);
> +	kfree_skb(skb);
> +	return NET_RX_DROP;
> +}
> +
>  int xfrm4_transport_finish(struct sk_buff *skb, int async)
>  {
>  	struct xfrm_offload *xo = xfrm_offload(skb);
> @@ -74,7 +96,8 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)
>  
>  	NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
>  		dev_net(skb->dev), NULL, skb, skb->dev, NULL,
> -		xfrm4_rcv_encap_finish);
> +		async ? xfrm4_rcv_encap_finish_async :
> +			xfrm4_rcv_encap_finish);

What happens if the PRE_ROUTING hook returns NF_DROP, NF_QUEUE, or
NF_STOLEN before the okfn runs? Looks like we leak the dev refcnt
then.

> diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
> index 9005fc156a20..d4eede5315ac 100644
> --- a/net/ipv6/xfrm6_input.c
> +++ b/net/ipv6/xfrm6_input.c
> @@ -40,6 +40,19 @@ static int xfrm6_transport_finish2(struct net *net, struct sock *sk,
>  	return 0;
>  }
>  
> +static int xfrm6_transport_finish2_async(struct net *net, struct sock *sk,
> +					 struct sk_buff *skb)
> +{
> +	if (xfrm_trans_queue_net(dev_net(skb->dev), skb, ip6_rcv_finish,
> +				 true)) {
> +		dev_put(skb->dev);
> +		kfree_skb(skb);
> +		return NET_RX_DROP;
> +	}
> +
> +	return 0;
> +}
> +
>  int xfrm6_transport_finish(struct sk_buff *skb, int async)
>  {
>  	struct xfrm_offload *xo = xfrm_offload(skb);
> @@ -69,7 +82,8 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
>  
>  	NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
>  		dev_net(skb->dev), NULL, skb, skb->dev, NULL,
> -		xfrm6_transport_finish2);
> +		async ? xfrm6_transport_finish2_async :
> +			xfrm6_transport_finish2);

Same here.