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

Steffen Klassert
2026-04-02T10:36:20Z

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.