Same issue as on the IPv4 side: vti6_input_proto() caches the tunnel pointer without taking a device reference, and vti6_rcv_cb() runs from the async crypto completion, after a possible device teardown. Take a device reference while the pointer is cached and drop it in vti6_rcv_cb() after the last use, like the IPv4 side. Fixes: fa9ad96d4905 ("vti6: Update the ipv6 side to use its own receive hook.") Cc: stable@vger.kernel.org Signed-off-by: Qihang --- net/ipv6/ip6_vti.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index ab94b3a4ba9c..b53830d5cf54 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -322,6 +322,9 @@ static int vti6_input_proto(struct sk_buff *skb, int nexthdr, __be32 spi, rcu_read_unlock(); XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = t; + + dev_hold(t->dev); + XFRM_SPI_SKB_CB(skb)->family = AF_INET6; XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); return xfrm_input(skb, nexthdr, spi, encap_type); @@ -355,10 +358,14 @@ static int vti6_rcv_cb(struct sk_buff *skb, int err) dev = t->dev; + /* Drop the reference taken in vti6_input_proto(). -EINVAL/-EPERM + * make xfrm_input() re-invoke us with err = -1 and drop it then. + */ if (err) { DEV_STATS_INC(dev, rx_errors); DEV_STATS_INC(dev, rx_dropped); + dev_put(dev); return 0; } @@ -387,6 +394,7 @@ static int vti6_rcv_cb(struct sk_buff *skb, int err) skb_scrub_packet(skb, !net_eq(t->net, dev_net(skb->dev))); skb->dev = dev; dev_sw_netstats_rx_add(dev, skb->len); + dev_put(dev); return 0; } -- 2.54.0 (Apple Git-157)