[RHEL7,COMMIT] ms/ipv6: fix NULL dereference in ip6_route_dev_notify()

Submitted by Konstantin Khorenko on June 26, 2018, 8:13 a.m.

Details

Message ID 201806260813.w5Q8DCxN014346@finist_ce7.work
State New
Series "ms/ipv6: fix NULL dereference in ip6_route_dev_notify()"
Headers show

Commit Message

Konstantin Khorenko June 26, 2018, 8:13 a.m.
The commit is pushed to "branch-rh7-3.10.0-862.3.2.vz7.61.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-862.3.2.vz7.61.5
------>
commit d5644659f4ad1e1d51cf3c99120aa5912ef102e7
Author: Eric Dumazet <edumazet@google.com>
Date:   Tue Aug 15 04:09:51 2017 -0700

    ms/ipv6: fix NULL dereference in ip6_route_dev_notify()
    
    Based on a syzkaller report [1], I found that a per cpu allocation
    failure in snmp6_alloc_dev() would then lead to NULL dereference in
    ip6_route_dev_notify().
    
    It seems this is a very old bug, thus no Fixes tag in this submission.
    
    Let's add in6_dev_put_clear() helper, as we will probably use
    it elsewhere (once available/present in net-next)
    
    [1]
    kasan: CONFIG_KASAN_INLINE enabled
    kasan: GPF could be caused by NULL-ptr deref or user memory access
    general protection fault: 0000 [#1] SMP KASAN
    Dumping ftrace buffer:
       (ftrace buffer empty)
    Modules linked in:
    CPU: 1 PID: 17294 Comm: syz-executor6 Not tainted 4.13.0-rc2+ #10
    Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
    task: ffff88019f456680 task.stack: ffff8801c6e58000
    RIP: 0010:__read_once_size include/linux/compiler.h:250 [inline]
    RIP: 0010:atomic_read arch/x86/include/asm/atomic.h:26 [inline]
    RIP: 0010:refcount_sub_and_test+0x7d/0x1b0 lib/refcount.c:178
    RSP: 0018:ffff8801c6e5f1b0 EFLAGS: 00010202
    RAX: 0000000000000037 RBX: dffffc0000000000 RCX: ffffc90005d25000
    RDX: ffff8801c6e5f218 RSI: ffffffff82342bbf RDI: 0000000000000001
    RBP: ffff8801c6e5f240 R08: 0000000000000001 R09: 0000000000000000
    R10: 0000000000000000 R11: 0000000000000000 R12: 1ffff10038dcbe37
    R13: 0000000000000006 R14: 0000000000000001 R15: 00000000000001b8
    FS:  00007f21e0429700(0000) GS:ffff8801dc100000(0000) knlGS:0000000000000000
    CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
    CR2: 0000001ddbc22000 CR3: 00000001d632b000 CR4: 00000000001426e0
    DR0: 0000000020000000 DR1: 0000000000000000 DR2: 0000000000000000
    DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000600
    Call Trace:
     refcount_dec_and_test+0x1a/0x20 lib/refcount.c:211
     in6_dev_put include/net/addrconf.h:335 [inline]
     ip6_route_dev_notify+0x1c9/0x4a0 net/ipv6/route.c:3732
     notifier_call_chain+0x136/0x2c0 kernel/notifier.c:93
     __raw_notifier_call_chain kernel/notifier.c:394 [inline]
     raw_notifier_call_chain+0x2d/0x40 kernel/notifier.c:401
     call_netdevice_notifiers_info+0x51/0x90 net/core/dev.c:1678
     call_netdevice_notifiers net/core/dev.c:1694 [inline]
     rollback_registered_many+0x91c/0xe80 net/core/dev.c:7107
     rollback_registered+0x1be/0x3c0 net/core/dev.c:7149
     register_netdevice+0xbcd/0xee0 net/core/dev.c:7587
     register_netdev+0x1a/0x30 net/core/dev.c:7669
     loopback_net_init+0x76/0x160 drivers/net/loopback.c:214
     ops_init+0x10a/0x570 net/core/net_namespace.c:118
     setup_net+0x313/0x710 net/core/net_namespace.c:294
     copy_net_ns+0x27c/0x580 net/core/net_namespace.c:418
     create_new_namespaces+0x425/0x880 kernel/nsproxy.c:107
     unshare_nsproxy_namespaces+0xae/0x1e0 kernel/nsproxy.c:206
     SYSC_unshare kernel/fork.c:2347 [inline]
     SyS_unshare+0x653/0xfa0 kernel/fork.c:2297
     entry_SYSCALL_64_fastpath+0x1f/0xbe
    RIP: 0033:0x4512c9
    RSP: 002b:00007f21e0428c08 EFLAGS: 00000216 ORIG_RAX: 0000000000000110
    RAX: ffffffffffffffda RBX: 0000000000718150 RCX: 00000000004512c9
    RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000062020200
    RBP: 0000000000000086 R08: 0000000000000000 R09: 0000000000000000
    R10: 0000000000000000 R11: 0000000000000216 R12: 00000000004b973d
    R13: 00000000ffffffff R14: 000000002001d000 R15: 00000000000002dd
    Code: 50 2b 34 82 c7 00 f1 f1 f1 f1 c7 40 04 04 f2 f2 f2 c7 40 08 f3 f3
    f3 f3 e8 a1 43 39 ff 4c 89 f8 48 8b 95 70 ff ff ff 48 c1 e8 03 <0f> b6
    0c 18 4c 89 f8 83 e0 07 83 c0 03 38 c8 7c 08 84 c9 0f 85
    RIP: __read_once_size include/linux/compiler.h:250 [inline] RSP:
    ffff8801c6e5f1b0
    RIP: atomic_read arch/x86/include/asm/atomic.h:26 [inline] RSP:
    ffff8801c6e5f1b0
    RIP: refcount_sub_and_test+0x7d/0x1b0 lib/refcount.c:178 RSP:
    ffff8801c6e5f1b0
    ---[ end trace e441d046c6410d31 ]---
    
    Signed-off-by: Eric Dumazet <edumazet@google.com>
    Reported-by: Dmitry Vyukov <dvyukov@google.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    
    https://jira.sw.ru/browse/PSBM-86093
    
    (cherry picked from commit 12d94a804946af291e24b80fc53ec86264765781)
    Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
---
 include/net/addrconf.h | 10 ++++++++++
 net/ipv6/route.c       |  6 +++---
 2 files changed, 13 insertions(+), 3 deletions(-)

Patch hide | download patch | download mbox

diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index d8a3ad53854a..a962327d19ca 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -322,6 +322,16 @@  static inline void in6_dev_put(struct inet6_dev *idev)
 		in6_dev_finish_destroy(idev);
 }
 
+static inline void in6_dev_put_clear(struct inet6_dev **pidev)
+{
+	struct inet6_dev *idev = *pidev;
+
+	if (idev) {
+		in6_dev_put(idev);
+		*pidev = NULL;
+	}
+}
+
 static inline void __in6_dev_put(struct inet6_dev *idev)
 {
 	atomic_dec(&idev->refcnt);
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index d7b59806c909..503be275ce87 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3419,10 +3419,10 @@  static int ip6_route_dev_notify(struct notifier_block *this,
 		/* NETDEV_UNREGISTER could be fired for multiple times by
 		 * netdev_wait_allrefs(). Make sure we only call this once.
 		 */
-		in6_dev_put(net->ipv6.ip6_null_entry->rt6i_idev);
+		in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
-		in6_dev_put(net->ipv6.ip6_prohibit_entry->rt6i_idev);
-		in6_dev_put(net->ipv6.ip6_blk_hole_entry->rt6i_idev);
+		in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
+		in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
 #endif
 	}