| From a204aef9fd77dce1efd9066ca4e44eede99cd858 Mon Sep 17 00:00:00 2001 |
| From: Xin Long <lucien.xin@gmail.com> |
| Date: Mon, 20 Apr 2020 21:51:09 +0800 |
| Subject: xfrm: call xfrm_output_gso when inner_protocol is set in xfrm_output |
| |
| From: Xin Long <lucien.xin@gmail.com> |
| |
| commit a204aef9fd77dce1efd9066ca4e44eede99cd858 upstream. |
| |
| An use-after-free crash can be triggered when sending big packets over |
| vxlan over esp with esp offload enabled: |
| |
| [] BUG: KASAN: use-after-free in ipv6_gso_pull_exthdrs.part.8+0x32c/0x4e0 |
| [] Call Trace: |
| [] dump_stack+0x75/0xa0 |
| [] kasan_report+0x37/0x50 |
| [] ipv6_gso_pull_exthdrs.part.8+0x32c/0x4e0 |
| [] ipv6_gso_segment+0x2c8/0x13c0 |
| [] skb_mac_gso_segment+0x1cb/0x420 |
| [] skb_udp_tunnel_segment+0x6b5/0x1c90 |
| [] inet_gso_segment+0x440/0x1380 |
| [] skb_mac_gso_segment+0x1cb/0x420 |
| [] esp4_gso_segment+0xae8/0x1709 [esp4_offload] |
| [] inet_gso_segment+0x440/0x1380 |
| [] skb_mac_gso_segment+0x1cb/0x420 |
| [] __skb_gso_segment+0x2d7/0x5f0 |
| [] validate_xmit_skb+0x527/0xb10 |
| [] __dev_queue_xmit+0x10f8/0x2320 <--- |
| [] ip_finish_output2+0xa2e/0x1b50 |
| [] ip_output+0x1a8/0x2f0 |
| [] xfrm_output_resume+0x110e/0x15f0 |
| [] __xfrm4_output+0xe1/0x1b0 |
| [] xfrm4_output+0xa0/0x200 |
| [] iptunnel_xmit+0x5a7/0x920 |
| [] vxlan_xmit_one+0x1658/0x37a0 [vxlan] |
| [] vxlan_xmit+0x5e4/0x3ec8 [vxlan] |
| [] dev_hard_start_xmit+0x125/0x540 |
| [] __dev_queue_xmit+0x17bd/0x2320 <--- |
| [] ip6_finish_output2+0xb20/0x1b80 |
| [] ip6_output+0x1b3/0x390 |
| [] ip6_xmit+0xb82/0x17e0 |
| [] inet6_csk_xmit+0x225/0x3d0 |
| [] __tcp_transmit_skb+0x1763/0x3520 |
| [] tcp_write_xmit+0xd64/0x5fe0 |
| [] __tcp_push_pending_frames+0x8c/0x320 |
| [] tcp_sendmsg_locked+0x2245/0x3500 |
| [] tcp_sendmsg+0x27/0x40 |
| |
| As on the tx path of vxlan over esp, skb->inner_network_header would be |
| set on vxlan_xmit() and xfrm4_tunnel_encap_add(), and the later one can |
| overwrite the former one. It causes skb_udp_tunnel_segment() to use a |
| wrong skb->inner_network_header, then the issue occurs. |
| |
| This patch is to fix it by calling xfrm_output_gso() instead when the |
| inner_protocol is set, in which gso_segment of inner_protocol will be |
| done first. |
| |
| While at it, also improve some code around. |
| |
| Fixes: 7862b4058b9f ("esp: Add gso handlers for esp4 and esp6") |
| Reported-by: Xiumei Mu <xmu@redhat.com> |
| Signed-off-by: Xin Long <lucien.xin@gmail.com> |
| Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/xfrm/xfrm_output.c | 12 +++++++----- |
| 1 file changed, 7 insertions(+), 5 deletions(-) |
| |
| --- a/net/xfrm/xfrm_output.c |
| +++ b/net/xfrm/xfrm_output.c |
| @@ -236,18 +236,20 @@ int xfrm_output(struct sock *sk, struct |
| xfrm_state_hold(x); |
| |
| if (skb_is_gso(skb)) { |
| - skb_shinfo(skb)->gso_type |= SKB_GSO_ESP; |
| + if (skb->inner_protocol) |
| + return xfrm_output_gso(net, sk, skb); |
| |
| - return xfrm_output2(net, sk, skb); |
| + skb_shinfo(skb)->gso_type |= SKB_GSO_ESP; |
| + goto out; |
| } |
| |
| if (x->xso.dev && x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM) |
| goto out; |
| + } else { |
| + if (skb_is_gso(skb)) |
| + return xfrm_output_gso(net, sk, skb); |
| } |
| |
| - if (skb_is_gso(skb)) |
| - return xfrm_output_gso(net, sk, skb); |
| - |
| if (skb->ip_summed == CHECKSUM_PARTIAL) { |
| err = skb_checksum_help(skb); |
| if (err) { |