aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2018-09-19 09:02:07 -0400
committerDavid S. Miller <davem@davemloft.net>2018-09-20 00:24:28 -0400
commit76c0ddd8c3a683f6e2c6e60e11dc1a1558caf4bc (patch)
tree764d9d269765b774be16ae02284dc91241c6e3fb
parentaa86b03c3ee65fc7bd4438dbb70665534df86050 (diff)
ip6_tunnel: be careful when accessing the inner header
the ip6 tunnel xmit ndo assumes that the processed skb always contains an ip[v6] header, but syzbot has found a way to send frames that fall short of this assumption, leading to the following splat: BUG: KMSAN: uninit-value in ip6ip6_tnl_xmit net/ipv6/ip6_tunnel.c:1307 [inline] BUG: KMSAN: uninit-value in ip6_tnl_start_xmit+0x7d2/0x1ef0 net/ipv6/ip6_tunnel.c:1390 CPU: 0 PID: 4504 Comm: syz-executor558 Not tainted 4.16.0+ #87 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:17 [inline] dump_stack+0x185/0x1d0 lib/dump_stack.c:53 kmsan_report+0x142/0x240 mm/kmsan/kmsan.c:1067 __msan_warning_32+0x6c/0xb0 mm/kmsan/kmsan_instr.c:683 ip6ip6_tnl_xmit net/ipv6/ip6_tunnel.c:1307 [inline] ip6_tnl_start_xmit+0x7d2/0x1ef0 net/ipv6/ip6_tunnel.c:1390 __netdev_start_xmit include/linux/netdevice.h:4066 [inline] netdev_start_xmit include/linux/netdevice.h:4075 [inline] xmit_one net/core/dev.c:3026 [inline] dev_hard_start_xmit+0x5f1/0xc70 net/core/dev.c:3042 __dev_queue_xmit+0x27ee/0x3520 net/core/dev.c:3557 dev_queue_xmit+0x4b/0x60 net/core/dev.c:3590 packet_snd net/packet/af_packet.c:2944 [inline] packet_sendmsg+0x7c70/0x8a30 net/packet/af_packet.c:2969 sock_sendmsg_nosec net/socket.c:630 [inline] sock_sendmsg net/socket.c:640 [inline] ___sys_sendmsg+0xec0/0x1310 net/socket.c:2046 __sys_sendmmsg+0x42d/0x800 net/socket.c:2136 SYSC_sendmmsg+0xc4/0x110 net/socket.c:2167 SyS_sendmmsg+0x63/0x90 net/socket.c:2162 do_syscall_64+0x309/0x430 arch/x86/entry/common.c:287 entry_SYSCALL_64_after_hwframe+0x3d/0xa2 RIP: 0033:0x441819 RSP: 002b:00007ffe58ee8268 EFLAGS: 00000213 ORIG_RAX: 0000000000000133 RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 0000000000441819 RDX: 0000000000000002 RSI: 0000000020000100 RDI: 0000000000000003 RBP: 00000000006cd018 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000213 R12: 0000000000402510 R13: 00000000004025a0 R14: 0000000000000000 R15: 0000000000000000 Uninit was created at: kmsan_save_stack_with_flags mm/kmsan/kmsan.c:278 [inline] kmsan_internal_poison_shadow+0xb8/0x1b0 mm/kmsan/kmsan.c:188 kmsan_kmalloc+0x94/0x100 mm/kmsan/kmsan.c:314 kmsan_slab_alloc+0x11/0x20 mm/kmsan/kmsan.c:321 slab_post_alloc_hook mm/slab.h:445 [inline] slab_alloc_node mm/slub.c:2737 [inline] __kmalloc_node_track_caller+0xaed/0x11c0 mm/slub.c:4369 __kmalloc_reserve net/core/skbuff.c:138 [inline] __alloc_skb+0x2cf/0x9f0 net/core/skbuff.c:206 alloc_skb include/linux/skbuff.h:984 [inline] alloc_skb_with_frags+0x1d4/0xb20 net/core/skbuff.c:5234 sock_alloc_send_pskb+0xb56/0x1190 net/core/sock.c:2085 packet_alloc_skb net/packet/af_packet.c:2803 [inline] packet_snd net/packet/af_packet.c:2894 [inline] packet_sendmsg+0x6454/0x8a30 net/packet/af_packet.c:2969 sock_sendmsg_nosec net/socket.c:630 [inline] sock_sendmsg net/socket.c:640 [inline] ___sys_sendmsg+0xec0/0x1310 net/socket.c:2046 __sys_sendmmsg+0x42d/0x800 net/socket.c:2136 SYSC_sendmmsg+0xc4/0x110 net/socket.c:2167 SyS_sendmmsg+0x63/0x90 net/socket.c:2162 do_syscall_64+0x309/0x430 arch/x86/entry/common.c:287 entry_SYSCALL_64_after_hwframe+0x3d/0xa2 This change addresses the issue adding the needed check before accessing the inner header. The ipv4 side of the issue is apparently there since the ipv4 over ipv6 initial support, and the ipv6 side predates git history. Fixes: c4d3efafcc93 ("[IPV6] IP6TUNNEL: Add support to IPv4 over IPv6 tunnel.") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot+3fde91d4d394747d6db4@syzkaller.appspotmail.com Tested-by: Alexander Potapenko <glider@google.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv6/ip6_tunnel.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 419960b0ba16..a0b6932c3afd 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1234,7 +1234,7 @@ static inline int
1234ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) 1234ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
1235{ 1235{
1236 struct ip6_tnl *t = netdev_priv(dev); 1236 struct ip6_tnl *t = netdev_priv(dev);
1237 const struct iphdr *iph = ip_hdr(skb); 1237 const struct iphdr *iph;
1238 int encap_limit = -1; 1238 int encap_limit = -1;
1239 struct flowi6 fl6; 1239 struct flowi6 fl6;
1240 __u8 dsfield; 1240 __u8 dsfield;
@@ -1242,6 +1242,11 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
1242 u8 tproto; 1242 u8 tproto;
1243 int err; 1243 int err;
1244 1244
1245 /* ensure we can access the full inner ip header */
1246 if (!pskb_may_pull(skb, sizeof(struct iphdr)))
1247 return -1;
1248
1249 iph = ip_hdr(skb);
1245 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 1250 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
1246 1251
1247 tproto = READ_ONCE(t->parms.proto); 1252 tproto = READ_ONCE(t->parms.proto);
@@ -1306,7 +1311,7 @@ static inline int
1306ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) 1311ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
1307{ 1312{
1308 struct ip6_tnl *t = netdev_priv(dev); 1313 struct ip6_tnl *t = netdev_priv(dev);
1309 struct ipv6hdr *ipv6h = ipv6_hdr(skb); 1314 struct ipv6hdr *ipv6h;
1310 int encap_limit = -1; 1315 int encap_limit = -1;
1311 __u16 offset; 1316 __u16 offset;
1312 struct flowi6 fl6; 1317 struct flowi6 fl6;
@@ -1315,6 +1320,10 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
1315 u8 tproto; 1320 u8 tproto;
1316 int err; 1321 int err;
1317 1322
1323 if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
1324 return -1;
1325
1326 ipv6h = ipv6_hdr(skb);
1318 tproto = READ_ONCE(t->parms.proto); 1327 tproto = READ_ONCE(t->parms.proto);
1319 if ((tproto != IPPROTO_IPV6 && tproto != 0) || 1328 if ((tproto != IPPROTO_IPV6 && tproto != 0) ||
1320 ip6_tnl_addr_conflict(t, ipv6h)) 1329 ip6_tnl_addr_conflict(t, ipv6h))