![Eric Dumazet](/assets/img/avatar_default.png)
My prior commit missed the fact that these functions
were using udp_hdr() (aka skb_transport_header())
to get access to GUE header.
Since pskb_transport_may_pull() does not exist yet, we have to add
transport_offset to our pskb_may_pull() calls.
BUG: KMSAN: uninit-value in gue_err+0x514/0xfa0 net/ipv4/fou.c:1032
CPU: 1 PID: 10648 Comm: syz-executor.1 Not tainted 5.0.0+ #11
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x173/0x1d0 lib/dump_stack.c:113
kmsan_report+0x12e/0x2a0 mm/kmsan/kmsan.c:600
__msan_warning+0x82/0xf0 mm/kmsan/kmsan_instr.c:313
gue_err+0x514/0xfa0 net/ipv4/fou.c:1032
__udp4_lib_err_encap_no_sk net/ipv4/udp.c:571 [inline]
__udp4_lib_err_encap net/ipv4/udp.c:626 [inline]
__udp4_lib_err+0x12e6/0x1d40 net/ipv4/udp.c:665
udp_err+0x74/0x90 net/ipv4/udp.c:737
icmp_socket_deliver net/ipv4/icmp.c:767 [inline]
icmp_unreach+0xb65/0x1070 net/ipv4/icmp.c:884
icmp_rcv+0x11a1/0x1950 net/ipv4/icmp.c:1066
ip_protocol_deliver_rcu+0x584/0xbb0 net/ipv4/ip_input.c:208
ip_local_deliver_finish net/ipv4/ip_input.c:234 [inline]
NF_HOOK include/linux/netfilter.h:289 [inline]
ip_local_deliver+0x624/0x7b0 net/ipv4/ip_input.c:255
dst_input include/net/dst.h:450 [inline]
ip_rcv_finish net/ipv4/ip_input.c:414 [inline]
NF_HOOK include/linux/netfilter.h:289 [inline]
ip_rcv+0x6bd/0x740 net/ipv4/ip_input.c:524
__netif_receive_skb_one_core net/core/dev.c:4973 [inline]
__netif_receive_skb net/core/dev.c:5083 [inline]
process_backlog+0x756/0x10e0 net/core/dev.c:5923
napi_poll net/core/dev.c:6346 [inline]
net_rx_action+0x78b/0x1a60 net/core/dev.c:6412
__do_softirq+0x53f/0x93a kernel/softirq.c:293
invoke_softirq kernel/softirq.c:375 [inline]
irq_exit+0x214/0x250 kernel/softirq.c:416
exiting_irq+0xe/0x10 arch/x86/include/asm/apic.h:536
smp_apic_timer_interrupt+0x48/0x70 arch/x86/kernel/apic/apic.c:1064
apic_timer_interrupt+0x2e/0x40 arch/x86/entry/entry_64.S:814
</IRQ>
RIP: 0010:finish_lock_switch+0x2b/0x40 kernel/sched/core.c:2597
Code: 48 89 e5 53 48 89 fb e8 63 e7 95 00 8b b8 88 0c 00 00 48 8b 00 48 85 c0 75 12 48 89 df e8 dd db 95 00 c6 00 00 c6 03 00 fb 5b <5d> c3 e8 4e e6 95 00 eb e7 66 90 66 2e 0f 1f 84 00 00 00 00 00 55
RSP: 0018:ffff888081a0fc80 EFLAGS: 00000296 ORIG_RAX: ffffffffffffff13
RAX: ffff88821fd6bd80 RBX: ffff888027898000 RCX: ccccccccccccd000
RDX: ffff88821fca8d80 RSI: ffff888000000000 RDI: 00000000000004a0
RBP: ffff888081a0fc80 R08: 0000000000000002 R09: ffff888081a0fb08
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000001
R13: ffff88811130e388 R14: ffff88811130da00 R15: ffff88812fdb7d80
finish_task_switch+0xfc/0x2d0 kernel/sched/core.c:2698
context_switch kernel/sched/core.c:2851 [inline]
__schedule+0x6cc/0x800 kernel/sched/core.c:3491
schedule+0x15b/0x240 kernel/sched/core.c:3535
freezable_schedule include/linux/freezer.h:172 [inline]
do_nanosleep+0x2ba/0x980 kernel/time/hrtimer.c:1679
hrtimer_nanosleep kernel/time/hrtimer.c:1733 [inline]
__do_sys_nanosleep kernel/time/hrtimer.c:1767 [inline]
__se_sys_nanosleep+0x746/0x960 kernel/time/hrtimer.c:1754
__x64_sys_nanosleep+0x3e/0x60 kernel/time/hrtimer.c:1754
do_syscall_64+0xbc/0xf0 arch/x86/entry/common.c:291
entry_SYSCALL_64_after_hwframe+0x63/0xe7
RIP: 0033:0x4855a0
Code: 00 00 48 c7 c0 d4 ff ff ff 64 c7 00 16 00 00 00 31 c0 eb be 66 0f 1f 44 00 00 83 3d b1 11 5d 00 00 75 14 b8 23 00 00 00 0f 05 <48> 3d 01 f0 ff ff 0f 83 04 e2 f8 ff c3 48 83 ec 08 e8 3a 55 fd ff
RSP: 002b:0000000000a4fd58 EFLAGS: 00000246 ORIG_RAX: 0000000000000023
RAX: ffffffffffffffda RBX: 0000000000085780 RCX: 00000000004855a0
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000a4fd60
RBP: 00000000000007ec R08: 0000000000000001 R09: 0000000000ceb940
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000008
R13: 0000000000a4fdb0 R14: 0000000000085711 R15: 0000000000a4fdc0
Uninit was created at:
kmsan_save_stack_with_flags mm/kmsan/kmsan.c:205 [inline]
kmsan_internal_poison_shadow+0x92/0x150 mm/kmsan/kmsan.c:159
kmsan_kmalloc+0xa6/0x130 mm/kmsan/kmsan_hooks.c:176
kmsan_slab_alloc+0xe/0x10 mm/kmsan/kmsan_hooks.c:185
slab_post_alloc_hook mm/slab.h:445 [inline]
slab_alloc_node mm/slub.c:2773 [inline]
__kmalloc_node_track_caller+0xe9e/0xff0 mm/slub.c:4398
__kmalloc_reserve net/core/skbuff.c:140 [inline]
__alloc_skb+0x309/0xa20 net/core/skbuff.c:208
alloc_skb include/linux/skbuff.h:1012 [inline]
alloc_skb_with_frags+0x186/0xa60 net/core/skbuff.c:5287
sock_alloc_send_pskb+0xafd/0x10a0 net/core/sock.c:2091
sock_alloc_send_skb+0xca/0xe0 net/core/sock.c:2108
__ip_append_data+0x34cd/0x5000 net/ipv4/ip_output.c:998
ip_append_data+0x324/0x480 net/ipv4/ip_output.c:1220
icmp_push_reply+0x23d/0x7e0 net/ipv4/icmp.c:375
__icmp_send+0x2ea3/0x30f0 net/ipv4/icmp.c:737
icmp_send include/net/icmp.h:47 [inline]
ipv4_link_failure+0x6d/0x230 net/ipv4/route.c:1190
dst_link_failure include/net/dst.h:427 [inline]
arp_error_report+0x106/0x1a0 net/ipv4/arp.c:297
neigh_invalidate+0x359/0x8e0 net/core/neighbour.c:992
neigh_timer_handler+0xdf2/0x1280 net/core/neighbour.c:1078
call_timer_fn+0x285/0x600 kernel/time/timer.c:1325
expire_timers kernel/time/timer.c:1362 [inline]
__run_timers+0xdb4/0x11d0 kernel/time/timer.c:1681
run_timer_softirq+0x2e/0x50 kernel/time/timer.c:1694
__do_softirq+0x53f/0x93a kernel/softirq.c:293
Fixes: 26fc181e6c
("fou, fou6: do not assume linear skbs")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: Stefano Brivio <sbrivio@redhat.com>
Cc: Sabrina Dubroca <sd@queasysnail.net>
Acked-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
226 lines
5 KiB
C
226 lines
5 KiB
C
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/socket.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/udp.h>
|
|
#include <linux/icmpv6.h>
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <net/fou.h>
|
|
#include <net/ip.h>
|
|
#include <net/ip6_tunnel.h>
|
|
#include <net/ip6_checksum.h>
|
|
#include <net/protocol.h>
|
|
#include <net/udp.h>
|
|
#include <net/udp_tunnel.h>
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6_FOU_TUNNEL)
|
|
|
|
static void fou6_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
|
|
struct flowi6 *fl6, u8 *protocol, __be16 sport)
|
|
{
|
|
struct udphdr *uh;
|
|
|
|
skb_push(skb, sizeof(struct udphdr));
|
|
skb_reset_transport_header(skb);
|
|
|
|
uh = udp_hdr(skb);
|
|
|
|
uh->dest = e->dport;
|
|
uh->source = sport;
|
|
uh->len = htons(skb->len);
|
|
udp6_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM6), skb,
|
|
&fl6->saddr, &fl6->daddr, skb->len);
|
|
|
|
*protocol = IPPROTO_UDP;
|
|
}
|
|
|
|
static int fou6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
|
|
u8 *protocol, struct flowi6 *fl6)
|
|
{
|
|
__be16 sport;
|
|
int err;
|
|
int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM6 ?
|
|
SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
|
|
|
|
err = __fou_build_header(skb, e, protocol, &sport, type);
|
|
if (err)
|
|
return err;
|
|
|
|
fou6_build_udp(skb, e, fl6, protocol, sport);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gue6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
|
|
u8 *protocol, struct flowi6 *fl6)
|
|
{
|
|
__be16 sport;
|
|
int err;
|
|
int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM6 ?
|
|
SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
|
|
|
|
err = __gue_build_header(skb, e, protocol, &sport, type);
|
|
if (err)
|
|
return err;
|
|
|
|
fou6_build_udp(skb, e, fl6, protocol, sport);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gue6_err_proto_handler(int proto, struct sk_buff *skb,
|
|
struct inet6_skb_parm *opt,
|
|
u8 type, u8 code, int offset, __be32 info)
|
|
{
|
|
const struct inet6_protocol *ipprot;
|
|
|
|
ipprot = rcu_dereference(inet6_protos[proto]);
|
|
if (ipprot && ipprot->err_handler) {
|
|
if (!ipprot->err_handler(skb, opt, type, code, offset, info))
|
|
return 0;
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
static int gue6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
|
u8 type, u8 code, int offset, __be32 info)
|
|
{
|
|
int transport_offset = skb_transport_offset(skb);
|
|
struct guehdr *guehdr;
|
|
size_t len, optlen;
|
|
int ret;
|
|
|
|
len = sizeof(struct udphdr) + sizeof(struct guehdr);
|
|
if (!pskb_may_pull(skb, transport_offset + len))
|
|
return -EINVAL;
|
|
|
|
guehdr = (struct guehdr *)&udp_hdr(skb)[1];
|
|
|
|
switch (guehdr->version) {
|
|
case 0: /* Full GUE header present */
|
|
break;
|
|
case 1: {
|
|
/* Direct encasulation of IPv4 or IPv6 */
|
|
skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr));
|
|
|
|
switch (((struct iphdr *)guehdr)->version) {
|
|
case 4:
|
|
ret = gue6_err_proto_handler(IPPROTO_IPIP, skb, opt,
|
|
type, code, offset, info);
|
|
goto out;
|
|
case 6:
|
|
ret = gue6_err_proto_handler(IPPROTO_IPV6, skb, opt,
|
|
type, code, offset, info);
|
|
goto out;
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
goto out;
|
|
}
|
|
}
|
|
default: /* Undefined version */
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
if (guehdr->control)
|
|
return -ENOENT;
|
|
|
|
optlen = guehdr->hlen << 2;
|
|
|
|
if (!pskb_may_pull(skb, transport_offset + len + optlen))
|
|
return -EINVAL;
|
|
|
|
guehdr = (struct guehdr *)&udp_hdr(skb)[1];
|
|
if (validate_gue_flags(guehdr, optlen))
|
|
return -EINVAL;
|
|
|
|
/* Handling exceptions for direct UDP encapsulation in GUE would lead to
|
|
* recursion. Besides, this kind of encapsulation can't even be
|
|
* configured currently. Discard this.
|
|
*/
|
|
if (guehdr->proto_ctype == IPPROTO_UDP ||
|
|
guehdr->proto_ctype == IPPROTO_UDPLITE)
|
|
return -EOPNOTSUPP;
|
|
|
|
skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr));
|
|
ret = gue6_err_proto_handler(guehdr->proto_ctype, skb,
|
|
opt, type, code, offset, info);
|
|
|
|
out:
|
|
skb_set_transport_header(skb, transport_offset);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static const struct ip6_tnl_encap_ops fou_ip6tun_ops = {
|
|
.encap_hlen = fou_encap_hlen,
|
|
.build_header = fou6_build_header,
|
|
.err_handler = gue6_err,
|
|
};
|
|
|
|
static const struct ip6_tnl_encap_ops gue_ip6tun_ops = {
|
|
.encap_hlen = gue_encap_hlen,
|
|
.build_header = gue6_build_header,
|
|
.err_handler = gue6_err,
|
|
};
|
|
|
|
static int ip6_tnl_encap_add_fou_ops(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = ip6_tnl_encap_add_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU);
|
|
if (ret < 0) {
|
|
pr_err("can't add fou6 ops\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = ip6_tnl_encap_add_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE);
|
|
if (ret < 0) {
|
|
pr_err("can't add gue6 ops\n");
|
|
ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ip6_tnl_encap_del_fou_ops(void)
|
|
{
|
|
ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU);
|
|
ip6_tnl_encap_del_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE);
|
|
}
|
|
|
|
#else
|
|
|
|
static int ip6_tnl_encap_add_fou_ops(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void ip6_tnl_encap_del_fou_ops(void)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
static int __init fou6_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = ip6_tnl_encap_add_fou_ops();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit fou6_fini(void)
|
|
{
|
|
ip6_tnl_encap_del_fou_ops();
|
|
}
|
|
|
|
module_init(fou6_init);
|
|
module_exit(fou6_fini);
|
|
MODULE_AUTHOR("Tom Herbert <therbert@google.com>");
|
|
MODULE_LICENSE("GPL");
|