xfrm async resumption hold skb->dev refcnt until after transport_finish. However, xfrm_rcv_cb may modify skb->dev to tunnel dev without taking device reference, such as vti_rcv_cb. The subsequent async resumption will decrement the tunnel device's reference count, which lead to uaf of tunnel dev and refcnt leak of orig dev. Stash original skb->dev to fix it. Fixes: 1c428b038400 ("xfrm: hold dev ref until after transport_finish NF_HOOK") Reported-by: Xu Chunxiao <xuchunxiao3@huawei.com> Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com> --- net/xfrm/xfrm_input.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index f65291eba1f6..810600d4614a 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -467,6 +467,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) { const struct xfrm_state_afinfo *afinfo; struct net *net = dev_net(skb->dev); + struct net_device *dev = skb->dev; int err; __be32 seq; __be32 seq_hi; @@ -493,7 +494,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) LINUX_MIB_XFRMINSTATEINVALID); if (encap_type == -1) - dev_put(skb->dev); + dev_put(dev); goto drop; } @@ -655,16 +656,16 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (!crypto_done) { spin_unlock(&x->lock); - dev_hold(skb->dev); + dev_hold(dev); nexthdr = x->type->input(x, skb); if (nexthdr == -EINPROGRESS) { if (async) - dev_put(skb->dev); + dev_put(dev); return 0; } - dev_put(skb->dev); + dev_put(dev); spin_lock(&x->lock); } resume: @@ -699,7 +700,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) err = xfrm_inner_mode_input(x, skb); if (err == -EINPROGRESS) { if (async) - dev_put(skb->dev); + dev_put(dev); return 0; } else if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); @@ -739,7 +740,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (skb_valid_dst(skb)) skb_dst_drop(skb); if (async) - dev_put(skb->dev); + dev_put(dev); gro_cells_receive(&gro_cells, skb); return 0; } else { @@ -760,7 +761,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (skb_valid_dst(skb)) skb_dst_drop(skb); if (async) - dev_put(skb->dev); + dev_put(dev); gro_cells_receive(&gro_cells, skb); return err; } @@ -772,7 +773,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) spin_unlock(&x->lock); drop: if (async) - dev_put(skb->dev); + dev_put(dev); xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); kfree_skb(skb); return 0; -- 2.25.1