Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932511AbbLGOg1 (ORCPT ); Mon, 7 Dec 2015 09:36:27 -0500 Received: from mail-pa0-f51.google.com ([209.85.220.51]:36244 "EHLO mail-pa0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932133AbbLGOgX (ORCPT ); Mon, 7 Dec 2015 09:36:23 -0500 Message-ID: <1449498980.25029.57.camel@edumazet-glaptop2.roam.corp.google.com> Subject: Re: use-after-free in ip6_xmit From: Eric Dumazet To: Dmitry Vyukov Cc: "David S. Miller" , Alexey Kuznetsov , James Morris , Hideaki YOSHIFUJI , Patrick McHardy , netdev , LKML , Vlad Yasevich , Neil Horman , linux-sctp@vger.kernel.org, syzkaller , Kostya Serebryany , Alexander Potapenko , Eric Dumazet , Sasha Levin Date: Mon, 07 Dec 2015 06:36:20 -0800 In-Reply-To: References: Content-Type: text/plain; charset="UTF-8" X-Mailer: Evolution 3.10.4-0ubuntu2 Mime-Version: 1.0 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 12631 Lines: 298 On Mon, 2015-12-07 at 11:22 +0100, Dmitry Vyukov wrote: > Hello, > > The following program triggers use-after-free in ip6_xmit: > > // autogenerated by syzkaller (http://github.com/google/syzkaller) > #include > #include > #include > #include > #include > #include > #include > #include > #include > #include > #include > > void *thr0(void *arg) > { > *(uint32_t*)0x20000000 = 0x5; > long r5 = syscall(SYS_setsockopt, arg, 0x29ul, 0x6ul, > 0x20000000ul, 0x4ul, 0); > return 0; > } > > void *thr1(void *arg) > { > struct sockaddr_in6 sa = {}; > sa.sin6_family = AF_INET6; > sa.sin6_port = getpid(); > sa.sin6_addr.s6_addr[15] = 1; > syscall(SYS_bind, arg, &sa, sizeof(sa), 0, 0, 0); > return 0; > } > > void *thr2(void *arg) > { > struct sockaddr_in6 sa = {}; > sa.sin6_family = AF_INET6; > sa.sin6_port = getpid(); > sa.sin6_addr.s6_addr[15] = 1; > syscall(SYS_connect, arg, &sa, sizeof(sa), 0, 0, 0); > return 0; > } > > void *thr3(void *arg) > { > syscall(SYS_listen, arg, 0x3ul, 0, 0, 0, 0); > return 0; > } > > void *thr4(void *arg) > { > syscall(SYS_accept4, arg, 0, 0, 0x800ul, 0, 0); > return 0; > } > > int main() > { > srand(getpid()); > syscall(SYS_mmap, 0x20000000ul, 0x10000ul, 0x2ul, 0x32ul, > 0xfffffffffffffffful, 0x0ul); > int fd[2]; > fd[0] = syscall(SYS_socket, PF_INET6, SOCK_STREAM, IPPROTO_TCP); > fd[1] = syscall(SYS_socket, PF_INET6, SOCK_STREAM, IPPROTO_TCP); > pthread_t th; > pthread_create(&th, 0, thr0, (void*)(long)fd[0]); > pthread_create(&th, 0, thr1, (void*)(long)fd[0]); > pthread_create(&th, 0, thr2, (void*)(long)fd[0]); > pthread_create(&th, 0, thr3, (void*)(long)fd[0]); > pthread_create(&th, 0, thr4, (void*)(long)fd[0]); > pthread_create(&th, 0, thr0, (void*)(long)fd[0]); > pthread_create(&th, 0, thr1, (void*)(long)fd[0]); > pthread_create(&th, 0, thr2, (void*)(long)fd[0]); > pthread_create(&th, 0, thr3, (void*)(long)fd[0]); > pthread_create(&th, 0, thr4, (void*)(long)fd[0]); > pthread_create(&th, 0, thr0, (void*)(long)fd[1]); > pthread_create(&th, 0, thr1, (void*)(long)fd[1]); > pthread_create(&th, 0, thr2, (void*)(long)fd[1]); > pthread_create(&th, 0, thr3, (void*)(long)fd[1]); > pthread_create(&th, 0, thr4, (void*)(long)fd[1]); > pthread_create(&th, 0, thr0, (void*)(long)fd[1]); > pthread_create(&th, 0, thr1, (void*)(long)fd[1]); > pthread_create(&th, 0, thr2, (void*)(long)fd[1]); > pthread_create(&th, 0, thr3, (void*)(long)fd[1]); > pthread_create(&th, 0, thr4, (void*)(long)fd[1]); > > usleep(20000); > return 0; > } > > > > ================================================================== > BUG: KASAN: use-after-free in ip6_xmit+0x15e3/0x1f50 at addr ffff88003991c786 > Read of size 2 by task a.out/6864 > ============================================================================= > BUG kmalloc-64 (Not tainted): kasan: bad access detected > ----------------------------------------------------------------------------- > > Disabling lock debugging due to kernel taint > INFO: Allocated in sock_kmalloc+0x93/0x100 age=40 cpu=1 pid=6850 > [< none >] ___slab_alloc+0x648/0x8c0 mm/slub.c:2438 > [< none >] __slab_alloc+0x4c/0x90 mm/slub.c:2467 > [< inline >] slab_alloc_node mm/slub.c:2530 > [< inline >] slab_alloc mm/slub.c:2572 > [< none >] __kmalloc+0x2d9/0x480 mm/slub.c:3532 > [< inline >] kmalloc include/linux/slab.h:463 > [< none >] sock_kmalloc+0x93/0x100 net/core/sock.c:1772 > [< none >] do_ipv6_setsockopt.isra.5+0xf4d/0x2d30 > net/ipv6/ipv6_sockglue.c:483 > [< none >] ipv6_setsockopt+0x4f/0x150 net/ipv6/ipv6_sockglue.c:885 > [< none >] sctp_setsockopt+0x194/0x4020 net/sctp/socket.c:3702 > [< none >] sock_common_setsockopt+0xb4/0x140 net/core/sock.c:2643 > [< inline >] SYSC_setsockopt net/socket.c:1757 > [< none >] SyS_setsockopt+0x161/0x290 net/socket.c:1736 > [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a > arch/x86/entry/entry_64.S:185 > > INFO: Freed in sock_kfree_s+0x29/0x90 age=34 cpu=0 pid=6855 > [< none >] __slab_free+0x21e/0x3e0 mm/slub.c:2648 > [< inline >] slab_free mm/slub.c:2803 > [< none >] kfree+0x26f/0x3e0 mm/slub.c:3632 > [< inline >] __sock_kfree_s net/core/sock.c:1793 > [< none >] sock_kfree_s+0x29/0x90 net/core/sock.c:1799 > [< none >] do_ipv6_setsockopt.isra.5+0x100f/0x2d30 > net/ipv6/ipv6_sockglue.c:506 > [< none >] ipv6_setsockopt+0x4f/0x150 net/ipv6/ipv6_sockglue.c:885 > [< none >] sctp_setsockopt+0x194/0x4020 net/sctp/socket.c:3702 > [< none >] sock_common_setsockopt+0xb4/0x140 net/core/sock.c:2643 > [< inline >] SYSC_setsockopt net/socket.c:1757 > [< none >] SyS_setsockopt+0x161/0x290 net/socket.c:1736 > [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a > arch/x86/entry/entry_64.S:185 > > INFO: Slab 0xffffea0000e64700 objects=21 used=19 fp=0xffff88003991cc00 > flags=0x1fffc0000004080 > INFO: Object 0xffff88003991c780 @offset=1920 fp=0x (null) > CPU: 2 PID: 6864 Comm: a.out Tainted: G B 4.4.0-rc3+ #150 > Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 > 0000000000000002 ffff88005f3f6e18 ffffffff82c6f2a8 0000000041b58ab3 > ffffffff8788bf8d ffffffff82c6f1f6 ffff880062045a00 ffffffff878ac723 > ffff88003e807700 0000000000000008 ffff88003991c780 ffff88005f3f6e18 > > Call Trace: > [< inline >] __dump_stack lib/dump_stack.c:15 > [] dump_stack+0xb2/0xfa lib/dump_stack.c:50 > [] print_trailer+0x12c/0x210 mm/slub.c:652 > [] object_err+0x2f/0x40 mm/slub.c:659 > [< inline >] print_address_description mm/kasan/report.c:138 > [] kasan_report_error+0x5d9/0x860 mm/kasan/report.c:251 > [< inline >] kasan_report mm/kasan/report.c:274 > [] __asan_report_load2_noabort+0x54/0x70 > mm/kasan/report.c:293 > [] ip6_xmit+0x15e3/0x1f50 net/ipv6/ip6_output.c:176 > [] sctp_v6_xmit+0x319/0x530 net/sctp/ipv6.c:223 > [] sctp_packet_transmit+0x113d/0x2680 net/sctp/output.c:595 > [] sctp_outq_flush+0x717/0x2840 net/sctp/outqueue.c:1090 > [] sctp_outq_uncork+0x57/0x80 net/sctp/outqueue.c:692 > [< inline >] sctp_cmd_interpreter net/sctp/sm_sideeffect.c:1750 > [< inline >] sctp_side_effects net/sctp/sm_sideeffect.c:1153 > [] sctp_do_sm+0x38a3/0x5be0 net/sctp/sm_sideeffect.c:1125 > [] sctp_primitive_SHUTDOWN+0xa9/0xd0 net/sctp/primitive.c:104 > [] sctp_close+0x6fd/0x9b0 net/sctp/socket.c:1519 > [] inet_release+0x111/0x270 net/ipv4/af_inet.c:413 > [] inet6_release+0x55/0x90 net/ipv6/af_inet6.c:406 > [] sock_release+0x96/0x260 net/socket.c:571 > [] sock_close+0x16/0x20 net/socket.c:1022 > [] __fput+0x244/0x860 fs/file_table.c:208 > [] ____fput+0x15/0x20 fs/file_table.c:244 > [] task_work_run+0x130/0x240 kernel/task_work.c:115 > [< inline >] exit_task_work include/linux/task_work.h:21 > [] do_exit+0x885/0x3050 kernel/exit.c:750 > [] do_group_exit+0xec/0x390 kernel/exit.c:880 > [] get_signal+0x677/0x1bf0 kernel/signal.c:2307 > [] do_signal+0x7e/0x2170 arch/x86/kernel/signal.c:709 > [] exit_to_usermode_loop+0xfe/0x1e0 > arch/x86/entry/common.c:247 > [< inline >] prepare_exit_to_usermode arch/x86/entry/common.c:282 > [] syscall_return_slowpath+0x16b/0x240 > arch/x86/entry/common.c:344 > [] int_ret_from_sys_call+0x25/0x9f > arch/x86/entry/entry_64.S:281 > ================================================================== > > On commit 31ade3b83e1821da5fbb2f11b5b3d4ab2ec39db8. > > The allocation/free stacks look similar to the ones I reported in > "memory leak in do_ipv6_setsockopt". However, the fix for that leak > suggests that it could lead only to leaks, not to use-after-frees. > -- I thought I already fixed these, based on your report ? commit 6bd4f355df2eae80b8a5c7b097371cd1e05f20d5 Author: Eric Dumazet Date: Wed Dec 2 21:53:57 2015 -0800 ipv6: kill sk_dst_lock While testing the np->opt RCU conversion, I found that UDP/IPv6 was using a mixture of xchg() and sk_dst_lock to protect concurrent changes to sk->sk_dst_cache, leading to possible corruptions and crashes. ip6_sk_dst_lookup_flow() uses sk_dst_check() anyway, so the simplest way to fix the mess is to remove sk_dst_lock completely, as we did for IPv4. __ip6_dst_store() and ip6_dst_store() share same implementation. sk_setup_caps() being called with socket lock being held or not, we have to use sk_dst_set() instead of __sk_dst_set() Note that I had to move the "np->dst_cookie = rt6_get_cookie(rt);" in ip6_dst_store() before the sk_setup_caps(sk, dst) call. This is because ip6_dst_store() can be called from process context, without any lock held. As soon as the dst is installed in sk->sk_dst_cache, dst can be freed from another cpu doing a concurrent ip6_dst_store() Doing the dst dereference before doing the install is needed to make sure no use after free would trigger. Signed-off-by: Eric Dumazet Reported-by: Dmitry Vyukov Signed-off-by: David S. Miller commit 45f6fad84cc305103b28d73482b344d7f5b76f39 Author: Eric Dumazet Date: Sun Nov 29 19:37:57 2015 -0800 ipv6: add complete rcu protection around np->opt This patch addresses multiple problems : UDP/RAW sendmsg() need to get a stable struct ipv6_txoptions while socket is not locked : Other threads can change np->opt concurrently. Dmitry posted a syzkaller (http://github.com/google/syzkaller) program desmonstrating use-after-free. Starting with TCP/DCCP lockless listeners, tcp_v6_syn_recv_sock() and dccp_v6_request_recv_sock() also need to use RCU protection to dereference np->opt once (before calling ipv6_dup_options()) This patch adds full RCU protection to np->opt Reported-by: Dmitry Vyukov Signed-off-by: Eric Dumazet Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller commit 602dd62dfbda3e63a2d6a3cbde953ebe82bf5087 Author: Eric Dumazet Date: Tue Dec 1 07:20:07 2015 -0800 ipv6: sctp: implement sctp_v6_destroy_sock() Dmitry Vyukov reported a memory leak using IPV6 SCTP sockets. We need to call inet6_destroy_sock() to properly release inet6 specific fields. Reported-by: Dmitry Vyukov Signed-off-by: Eric Dumazet Acked-by: Daniel Borkmann Signed-off-by: David S. Miller commit c836a8ba93869d6a0290a6ae0047fbef09066871 Author: Eric Dumazet Date: Wed Dec 2 21:48:14 2015 -0800 ipv6: sctp: add rcu protection around np->opt This patch completes the work I did in commit 45f6fad84cc3 ("ipv6: add complete rcu protection around np->opt"), as I missed sctp part. This simply makes sure np->opt is used with proper RCU locking and accessors. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Can you double check based on linux-4.4-rc4 instead of linux-4.4-rc3 ? Thanks -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/