mirror of
https://github.com/torvalds/linux.git
synced 2024-12-14 23:25:54 +00:00
2423496af3
The KASAN warning repoted below was discovered with a syzkaller program. The reproducer is basically: int s = socket(AF_INET6, SOCK_RAW, NEXTHDR_HOP); send(s, &one_byte_of_data, 1, MSG_MORE); send(s, &more_than_mtu_bytes_data, 2000, 0); The socket() call sets the nexthdr field of the v6 header to NEXTHDR_HOP, the first send call primes the payload with a non zero byte of data, and the second send call triggers the fragmentation path. The fragmentation code tries to parse the header options in order to figure out where to insert the fragment option. Since nexthdr points to an invalid option, the calculation of the size of the network header can made to be much larger than the linear section of the skb and data is read outside of it. This fix makes ip6_find_1stfrag return an error if it detects running out-of-bounds. [ 42.361487] ================================================================== [ 42.364412] BUG: KASAN: slab-out-of-bounds in ip6_fragment+0x11c8/0x3730 [ 42.365471] Read of size 840 at addr ffff88000969e798 by task ip6_fragment-oo/3789 [ 42.366469] [ 42.366696] CPU: 1 PID: 3789 Comm: ip6_fragment-oo Not tainted 4.11.0+ #41 [ 42.367628] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1ubuntu1 04/01/2014 [ 42.368824] Call Trace: [ 42.369183] dump_stack+0xb3/0x10b [ 42.369664] print_address_description+0x73/0x290 [ 42.370325] kasan_report+0x252/0x370 [ 42.370839] ? ip6_fragment+0x11c8/0x3730 [ 42.371396] check_memory_region+0x13c/0x1a0 [ 42.371978] memcpy+0x23/0x50 [ 42.372395] ip6_fragment+0x11c8/0x3730 [ 42.372920] ? nf_ct_expect_unregister_notifier+0x110/0x110 [ 42.373681] ? ip6_copy_metadata+0x7f0/0x7f0 [ 42.374263] ? ip6_forward+0x2e30/0x2e30 [ 42.374803] ip6_finish_output+0x584/0x990 [ 42.375350] ip6_output+0x1b7/0x690 [ 42.375836] ? ip6_finish_output+0x990/0x990 [ 42.376411] ? ip6_fragment+0x3730/0x3730 [ 42.376968] ip6_local_out+0x95/0x160 [ 42.377471] ip6_send_skb+0xa1/0x330 [ 42.377969] ip6_push_pending_frames+0xb3/0xe0 [ 42.378589] rawv6_sendmsg+0x2051/0x2db0 [ 42.379129] ? rawv6_bind+0x8b0/0x8b0 [ 42.379633] ? _copy_from_user+0x84/0xe0 [ 42.380193] ? debug_check_no_locks_freed+0x290/0x290 [ 42.380878] ? ___sys_sendmsg+0x162/0x930 [ 42.381427] ? rcu_read_lock_sched_held+0xa3/0x120 [ 42.382074] ? sock_has_perm+0x1f6/0x290 [ 42.382614] ? ___sys_sendmsg+0x167/0x930 [ 42.383173] ? lock_downgrade+0x660/0x660 [ 42.383727] inet_sendmsg+0x123/0x500 [ 42.384226] ? inet_sendmsg+0x123/0x500 [ 42.384748] ? inet_recvmsg+0x540/0x540 [ 42.385263] sock_sendmsg+0xca/0x110 [ 42.385758] SYSC_sendto+0x217/0x380 [ 42.386249] ? SYSC_connect+0x310/0x310 [ 42.386783] ? __might_fault+0x110/0x1d0 [ 42.387324] ? lock_downgrade+0x660/0x660 [ 42.387880] ? __fget_light+0xa1/0x1f0 [ 42.388403] ? __fdget+0x18/0x20 [ 42.388851] ? sock_common_setsockopt+0x95/0xd0 [ 42.389472] ? SyS_setsockopt+0x17f/0x260 [ 42.390021] ? entry_SYSCALL_64_fastpath+0x5/0xbe [ 42.390650] SyS_sendto+0x40/0x50 [ 42.391103] entry_SYSCALL_64_fastpath+0x1f/0xbe [ 42.391731] RIP: 0033:0x7fbbb711e383 [ 42.392217] RSP: 002b:00007ffff4d34f28 EFLAGS: 00000246 ORIG_RAX: 000000000000002c [ 42.393235] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fbbb711e383 [ 42.394195] RDX: 0000000000001000 RSI: 00007ffff4d34f60 RDI: 0000000000000003 [ 42.395145] RBP: 0000000000000046 R08: 00007ffff4d34f40 R09: 0000000000000018 [ 42.396056] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000400aad [ 42.396598] R13: 0000000000000066 R14: 00007ffff4d34ee0 R15: 00007fbbb717af00 [ 42.397257] [ 42.397411] Allocated by task 3789: [ 42.397702] save_stack_trace+0x16/0x20 [ 42.398005] save_stack+0x46/0xd0 [ 42.398267] kasan_kmalloc+0xad/0xe0 [ 42.398548] kasan_slab_alloc+0x12/0x20 [ 42.398848] __kmalloc_node_track_caller+0xcb/0x380 [ 42.399224] __kmalloc_reserve.isra.32+0x41/0xe0 [ 42.399654] __alloc_skb+0xf8/0x580 [ 42.400003] sock_wmalloc+0xab/0xf0 [ 42.400346] __ip6_append_data.isra.41+0x2472/0x33d0 [ 42.400813] ip6_append_data+0x1a8/0x2f0 [ 42.401122] rawv6_sendmsg+0x11ee/0x2db0 [ 42.401505] inet_sendmsg+0x123/0x500 [ 42.401860] sock_sendmsg+0xca/0x110 [ 42.402209] ___sys_sendmsg+0x7cb/0x930 [ 42.402582] __sys_sendmsg+0xd9/0x190 [ 42.402941] SyS_sendmsg+0x2d/0x50 [ 42.403273] entry_SYSCALL_64_fastpath+0x1f/0xbe [ 42.403718] [ 42.403871] Freed by task 1794: [ 42.404146] save_stack_trace+0x16/0x20 [ 42.404515] save_stack+0x46/0xd0 [ 42.404827] kasan_slab_free+0x72/0xc0 [ 42.405167] kfree+0xe8/0x2b0 [ 42.405462] skb_free_head+0x74/0xb0 [ 42.405806] skb_release_data+0x30e/0x3a0 [ 42.406198] skb_release_all+0x4a/0x60 [ 42.406563] consume_skb+0x113/0x2e0 [ 42.406910] skb_free_datagram+0x1a/0xe0 [ 42.407288] netlink_recvmsg+0x60d/0xe40 [ 42.407667] sock_recvmsg+0xd7/0x110 [ 42.408022] ___sys_recvmsg+0x25c/0x580 [ 42.408395] __sys_recvmsg+0xd6/0x190 [ 42.408753] SyS_recvmsg+0x2d/0x50 [ 42.409086] entry_SYSCALL_64_fastpath+0x1f/0xbe [ 42.409513] [ 42.409665] The buggy address belongs to the object at ffff88000969e780 [ 42.409665] which belongs to the cache kmalloc-512 of size 512 [ 42.410846] The buggy address is located 24 bytes inside of [ 42.410846] 512-byte region [ffff88000969e780, ffff88000969e980) [ 42.411941] The buggy address belongs to the page: [ 42.412405] page:ffffea000025a780 count:1 mapcount:0 mapping: (null) index:0x0 compound_mapcount: 0 [ 42.413298] flags: 0x100000000008100(slab|head) [ 42.413729] raw: 0100000000008100 0000000000000000 0000000000000000 00000001800c000c [ 42.414387] raw: ffffea00002a9500 0000000900000007 ffff88000c401280 0000000000000000 [ 42.415074] page dumped because: kasan: bad access detected [ 42.415604] [ 42.415757] Memory state around the buggy address: [ 42.416222] ffff88000969e880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 42.416904] ffff88000969e900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 42.417591] >ffff88000969e980: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 42.418273] ^ [ 42.418588] ffff88000969ea00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 42.419273] ffff88000969ea80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 42.419882] ================================================================== Reported-by: Andrey Konovalov <andreyknvl@google.com> Signed-off-by: Craig Gallek <kraig@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
179 lines
4.2 KiB
C
179 lines
4.2 KiB
C
/*
|
|
* IPv6 library code, needed by static components when full IPv6 support is
|
|
* not configured or static. These functions are needed by GSO/GRO implementation.
|
|
*/
|
|
#include <linux/export.h>
|
|
#include <net/ip.h>
|
|
#include <net/ipv6.h>
|
|
#include <net/ip6_fib.h>
|
|
#include <net/addrconf.h>
|
|
#include <net/secure_seq.h>
|
|
#include <linux/netfilter.h>
|
|
|
|
static u32 __ipv6_select_ident(struct net *net, u32 hashrnd,
|
|
const struct in6_addr *dst,
|
|
const struct in6_addr *src)
|
|
{
|
|
u32 hash, id;
|
|
|
|
hash = __ipv6_addr_jhash(dst, hashrnd);
|
|
hash = __ipv6_addr_jhash(src, hash);
|
|
hash ^= net_hash_mix(net);
|
|
|
|
/* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve,
|
|
* set the hight order instead thus minimizing possible future
|
|
* collisions.
|
|
*/
|
|
id = ip_idents_reserve(hash, 1);
|
|
if (unlikely(!id))
|
|
id = 1 << 31;
|
|
|
|
return id;
|
|
}
|
|
|
|
/* This function exists only for tap drivers that must support broken
|
|
* clients requesting UFO without specifying an IPv6 fragment ID.
|
|
*
|
|
* This is similar to ipv6_select_ident() but we use an independent hash
|
|
* seed to limit information leakage.
|
|
*
|
|
* The network header must be set before calling this.
|
|
*/
|
|
void ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
|
|
{
|
|
static u32 ip6_proxy_idents_hashrnd __read_mostly;
|
|
struct in6_addr buf[2];
|
|
struct in6_addr *addrs;
|
|
u32 id;
|
|
|
|
addrs = skb_header_pointer(skb,
|
|
skb_network_offset(skb) +
|
|
offsetof(struct ipv6hdr, saddr),
|
|
sizeof(buf), buf);
|
|
if (!addrs)
|
|
return;
|
|
|
|
net_get_random_once(&ip6_proxy_idents_hashrnd,
|
|
sizeof(ip6_proxy_idents_hashrnd));
|
|
|
|
id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd,
|
|
&addrs[1], &addrs[0]);
|
|
skb_shinfo(skb)->ip6_frag_id = htonl(id);
|
|
}
|
|
EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);
|
|
|
|
__be32 ipv6_select_ident(struct net *net,
|
|
const struct in6_addr *daddr,
|
|
const struct in6_addr *saddr)
|
|
{
|
|
static u32 ip6_idents_hashrnd __read_mostly;
|
|
u32 id;
|
|
|
|
net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));
|
|
|
|
id = __ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr);
|
|
return htonl(id);
|
|
}
|
|
EXPORT_SYMBOL(ipv6_select_ident);
|
|
|
|
int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
|
|
{
|
|
u16 offset = sizeof(struct ipv6hdr);
|
|
unsigned int packet_len = skb_tail_pointer(skb) -
|
|
skb_network_header(skb);
|
|
int found_rhdr = 0;
|
|
*nexthdr = &ipv6_hdr(skb)->nexthdr;
|
|
|
|
while (offset <= packet_len) {
|
|
struct ipv6_opt_hdr *exthdr;
|
|
|
|
switch (**nexthdr) {
|
|
|
|
case NEXTHDR_HOP:
|
|
break;
|
|
case NEXTHDR_ROUTING:
|
|
found_rhdr = 1;
|
|
break;
|
|
case NEXTHDR_DEST:
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
|
if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
|
|
break;
|
|
#endif
|
|
if (found_rhdr)
|
|
return offset;
|
|
break;
|
|
default:
|
|
return offset;
|
|
}
|
|
|
|
if (offset + sizeof(struct ipv6_opt_hdr) > packet_len)
|
|
return -EINVAL;
|
|
|
|
exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
|
|
offset);
|
|
offset += ipv6_optlen(exthdr);
|
|
*nexthdr = &exthdr->nexthdr;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
EXPORT_SYMBOL(ip6_find_1stfragopt);
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
int ip6_dst_hoplimit(struct dst_entry *dst)
|
|
{
|
|
int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
|
|
if (hoplimit == 0) {
|
|
struct net_device *dev = dst->dev;
|
|
struct inet6_dev *idev;
|
|
|
|
rcu_read_lock();
|
|
idev = __in6_dev_get(dev);
|
|
if (idev)
|
|
hoplimit = idev->cnf.hop_limit;
|
|
else
|
|
hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
|
|
rcu_read_unlock();
|
|
}
|
|
return hoplimit;
|
|
}
|
|
EXPORT_SYMBOL(ip6_dst_hoplimit);
|
|
#endif
|
|
|
|
int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
int len;
|
|
|
|
len = skb->len - sizeof(struct ipv6hdr);
|
|
if (len > IPV6_MAXPLEN)
|
|
len = 0;
|
|
ipv6_hdr(skb)->payload_len = htons(len);
|
|
IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
|
|
|
|
/* if egress device is enslaved to an L3 master device pass the
|
|
* skb to its handler for processing
|
|
*/
|
|
skb = l3mdev_ip6_out(sk, skb);
|
|
if (unlikely(!skb))
|
|
return 0;
|
|
|
|
skb->protocol = htons(ETH_P_IPV6);
|
|
|
|
return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
|
|
net, sk, skb, NULL, skb_dst(skb)->dev,
|
|
dst_output);
|
|
}
|
|
EXPORT_SYMBOL_GPL(__ip6_local_out);
|
|
|
|
int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
int err;
|
|
|
|
err = __ip6_local_out(net, sk, skb);
|
|
if (likely(err == 1))
|
|
err = dst_output(net, sk, skb);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ip6_local_out);
|