Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

xfrm: hold dev ref until after transport_finish NF_HOOK

After async crypto completes, xfrm_input_resume() calls dev_put()
immediately on re-entry before the skb reaches transport_finish.
The skb->dev pointer is then used inside NF_HOOK and its okfn,
which can race with device teardown.

Remove the dev_put from the async resumption entry and instead
drop the reference after the NF_HOOK call in transport_finish,
using a saved device pointer since NF_HOOK may consume the skb.
This covers NF_DROP, NF_QUEUE and NF_STOLEN paths that skip
the okfn.

For non-transport exits (decaps, gro, drop) and secondary
async return points, release the reference inline when
async is set.

Suggested-by: Florian Westphal <fw@strlen.de>
Fixes: acf568ee859f ("xfrm: Reinject transport-mode packets through tasklet")
Cc: stable@vger.kernel.org
Signed-off-by: Qi Tang <tpluszz77@gmail.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

authored by

Qi Tang and committed by
Steffen Klassert
1c428b03 069daad4

+22 -6
+4 -1
net/ipv4/xfrm4_input.c
··· 50 50 { 51 51 struct xfrm_offload *xo = xfrm_offload(skb); 52 52 struct iphdr *iph = ip_hdr(skb); 53 + struct net_device *dev = skb->dev; 53 54 54 55 iph->protocol = XFRM_MODE_SKB_CB(skb)->protocol; 55 56 ··· 74 73 } 75 74 76 75 NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, 77 - dev_net(skb->dev), NULL, skb, skb->dev, NULL, 76 + dev_net(dev), NULL, skb, dev, NULL, 78 77 xfrm4_rcv_encap_finish); 78 + if (async) 79 + dev_put(dev); 79 80 return 0; 80 81 } 81 82
+4 -1
net/ipv6/xfrm6_input.c
··· 43 43 int xfrm6_transport_finish(struct sk_buff *skb, int async) 44 44 { 45 45 struct xfrm_offload *xo = xfrm_offload(skb); 46 + struct net_device *dev = skb->dev; 46 47 int nhlen = -skb_network_offset(skb); 47 48 48 49 skb_network_header(skb)[IP6CB(skb)->nhoff] = ··· 69 68 } 70 69 71 70 NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, 72 - dev_net(skb->dev), NULL, skb, skb->dev, NULL, 71 + dev_net(dev), NULL, skb, dev, NULL, 73 72 xfrm6_transport_finish2); 73 + if (async) 74 + dev_put(dev); 74 75 return 0; 75 76 } 76 77
+14 -4
net/xfrm/xfrm_input.c
··· 506 506 /* An encap_type of -1 indicates async resumption. */ 507 507 if (encap_type == -1) { 508 508 async = 1; 509 - dev_put(skb->dev); 510 509 seq = XFRM_SKB_CB(skb)->seq.input.low; 511 510 spin_lock(&x->lock); 512 511 goto resume; ··· 658 659 dev_hold(skb->dev); 659 660 660 661 nexthdr = x->type->input(x, skb); 661 - if (nexthdr == -EINPROGRESS) 662 + if (nexthdr == -EINPROGRESS) { 663 + if (async) 664 + dev_put(skb->dev); 662 665 return 0; 666 + } 663 667 664 668 dev_put(skb->dev); 665 669 spin_lock(&x->lock); ··· 697 695 XFRM_MODE_SKB_CB(skb)->protocol = nexthdr; 698 696 699 697 err = xfrm_inner_mode_input(x, skb); 700 - if (err == -EINPROGRESS) 698 + if (err == -EINPROGRESS) { 699 + if (async) 700 + dev_put(skb->dev); 701 701 return 0; 702 - else if (err) { 702 + } else if (err) { 703 703 XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); 704 704 goto drop; 705 705 } ··· 738 734 sp->olen = 0; 739 735 if (skb_valid_dst(skb)) 740 736 skb_dst_drop(skb); 737 + if (async) 738 + dev_put(skb->dev); 741 739 gro_cells_receive(&gro_cells, skb); 742 740 return 0; 743 741 } else { ··· 759 753 sp->olen = 0; 760 754 if (skb_valid_dst(skb)) 761 755 skb_dst_drop(skb); 756 + if (async) 757 + dev_put(skb->dev); 762 758 gro_cells_receive(&gro_cells, skb); 763 759 return err; 764 760 } ··· 771 763 drop_unlock: 772 764 spin_unlock(&x->lock); 773 765 drop: 766 + if (async) 767 + dev_put(skb->dev); 774 768 xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); 775 769 kfree_skb(skb); 776 770 return 0;