l2ip: fix possible use-after-free [Linux 3.16.72]

This Linux kernel change "l2ip: fix possible use-after-free" is included in the Linux 3.16.72 release. This change is authored by Eric Dumazet <edumazet [at] google.com> on Tue Apr 30 06:27:58 2019 -0700. The commit for this change in Linux stable tree is 5e7bde8 (patch) which is from upstream commit a622b40. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream a622b40.

l2ip: fix possible use-after-free

commit a622b40035d16196bf19b2b33b854862595245fc upstream.

Before taking a refcount on a rcu protected structure,
we need to make sure the refcount is not zero.

syzbot reported :

refcount_t: increment on 0; use-after-free.
WARNING: CPU: 1 PID: 23533 at lib/refcount.c:156 refcount_inc_checked lib/refcount.c:156 [inline]
WARNING: CPU: 1 PID: 23533 at lib/refcount.c:156 refcount_inc_checked+0x61/0x70 lib/refcount.c:154
Kernel panic - not syncing: panic_on_warn set ...
CPU: 1 PID: 23533 Comm: syz-executor.2 Not tainted 5.1.0-rc7+ #93
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
 __dump_stack lib/dump_stack.c:77 [inline]
 dump_stack+0x172/0x1f0 lib/dump_stack.c:113
 panic+0x2cb/0x65c kernel/panic.c:214
 __warn.cold+0x20/0x45 kernel/panic.c:571
 report_bug+0x263/0x2b0 lib/bug.c:186
 fixup_bug arch/x86/kernel/traps.c:179 [inline]
 fixup_bug arch/x86/kernel/traps.c:174 [inline]
 do_error_trap+0x11b/0x200 arch/x86/kernel/traps.c:272
 do_invalid_op+0x37/0x50 arch/x86/kernel/traps.c:291
 invalid_op+0x14/0x20 arch/x86/entry/entry_64.S:973
RIP: 0010:refcount_inc_checked lib/refcount.c:156 [inline]
RIP: 0010:refcount_inc_checked+0x61/0x70 lib/refcount.c:154
Code: 1d 98 2b 2a 06 31 ff 89 de e8 db 2c 40 fe 84 db 75 dd e8 92 2b 40 fe 48 c7 c7 20 7a a1 87 c6 05 78 2b 2a 06 01 e8 7d d9 12 fe <0f> 0b eb c1 90 90 90 90 90 90 90 90 90 90 90 55 48 89 e5 41 57 41
RSP: 0018:ffff888069f0fba8 EFLAGS: 00010286
RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000
RDX: 000000000000f353 RSI: ffffffff815afcb6 RDI: ffffed100d3e1f67
RBP: ffff888069f0fbb8 R08: ffff88809b1845c0 R09: ffffed1015d23ef1
R10: ffffed1015d23ef0 R11: ffff8880ae91f787 R12: ffff8880a8f26968
R13: 0000000000000004 R14: dffffc0000000000 R15: ffff8880a49a6440
 l2tp_tunnel_inc_refcount net/l2tp/l2tp_core.h:240 [inline]
 l2tp_tunnel_get+0x250/0x580 net/l2tp/l2tp_core.c:173
 pppol2tp_connect+0xc00/0x1c70 net/l2tp/l2tp_ppp.c:702
 __sys_connect+0x266/0x330 net/socket.c:1808
 __do_sys_connect net/socket.c:1819 [inline]
 __se_sys_connect net/socket.c:1816 [inline]
 __x64_sys_connect+0x73/0xb0 net/socket.c:1816

Fixes: 54652eb12c1b ("l2tp: hold tunnel while looking up sessions in l2tp_netlink")
Signed-off-by: Eric Dumazet <[email protected]>
Reported-by: syzbot <[email protected]>
Cc: Guillaume Nault <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
[bwh: Backported to 3.16: use atomic not refcount API]
Signed-off-by: Ben Hutchings <[email protected]>

There are 8 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 net/l2tp/l2tp_core.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index ead3862..c8b5c8c 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -217,8 +217,8 @@ struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)

    rcu_read_lock_bh();
    list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
-       if (tunnel->tunnel_id == tunnel_id) {
-           l2tp_tunnel_inc_refcount(tunnel);
+       if (tunnel->tunnel_id == tunnel_id &&
+           atomic_inc_not_zero(&tunnel->ref_count)) {
            rcu_read_unlock_bh();

            return tunnel;
@@ -238,8 +238,8 @@ struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth)

    rcu_read_lock_bh();
    list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
-       if (++count > nth) {
-           l2tp_tunnel_inc_refcount(tunnel);
+       if (++count > nth &&
+           atomic_inc_not_zero(&tunnel->ref_count)) {
            rcu_read_unlock_bh();
            return tunnel;
        }

Leave a Reply

Your email address will not be published. Required fields are marked *