Commit 348a4002 authored by Wei Wang's avatar Wei Wang Committed by David S. Miller

ipv6: repair fib6 tree in failure case

In fib6_add(), it is possible that fib6_add_1() picks an intermediate
node and sets the node's fn->leaf to NULL in order to add this new
route. However, if fib6_add_rt2node() fails to add the new
route for some reason, fn->leaf will be left as NULL and could
potentially cause crash when fn->leaf is accessed in fib6_locate().
This patch makes sure fib6_repair_tree() is called to properly repair
fn->leaf in the above failure case.

Here is the syzkaller reported general protection fault in fib6_locate:
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
Modules linked in:
CPU: 0 PID: 40937 Comm: syz-executor3 Not tainted
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
task: ffff8801d7d64100 ti: ffff8801d01a0000 task.ti: ffff8801d01a0000
RIP: 0010:[<ffffffff82a3e0e1>]  [<ffffffff82a3e0e1>] __ipv6_prefix_equal64_half include/net/ipv6.h:475 [inline]
RIP: 0010:[<ffffffff82a3e0e1>]  [<ffffffff82a3e0e1>] ipv6_prefix_equal include/net/ipv6.h:492 [inline]
RIP: 0010:[<ffffffff82a3e0e1>]  [<ffffffff82a3e0e1>] fib6_locate_1 net/ipv6/ip6_fib.c:1210 [inline]
RIP: 0010:[<ffffffff82a3e0e1>]  [<ffffffff82a3e0e1>] fib6_locate+0x281/0x3c0 net/ipv6/ip6_fib.c:1233
RSP: 0018:ffff8801d01a36a8  EFLAGS: 00010202
RAX: 0000000000000020 RBX: ffff8801bc790e00 RCX: ffffc90002983000
RDX: 0000000000001219 RSI: ffff8801d01a37a0 RDI: 0000000000000100
RBP: ffff8801d01a36f0 R08: 00000000000000ff R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000000 R12: 0000000000000001
R13: dffffc0000000000 R14: ffff8801d01a37a0 R15: 0000000000000000
FS:  00007f6afd68c700(0000) GS:ffff8801db400000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000004c6340 CR3: 00000000ba41f000 CR4: 00000000001426f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Stack:
 ffff8801d01a37a8 ffff8801d01a3780 ffffed003a0346f5 0000000c82a23ea0
 ffff8800b7bd7700 ffff8801d01a3780 ffff8800b6a1c940 ffffffff82a23ea0
 ffff8801d01a3920 ffff8801d01a3748 ffffffff82a223d6 ffff8801d7d64988
Call Trace:
 [<ffffffff82a223d6>] ip6_route_del+0x106/0x570 net/ipv6/route.c:2109
 [<ffffffff82a23f9d>] inet6_rtm_delroute+0xfd/0x100 net/ipv6/route.c:3075
 [<ffffffff82621359>] rtnetlink_rcv_msg+0x549/0x7a0 net/core/rtnetlink.c:3450
 [<ffffffff8274c1d1>] netlink_rcv_skb+0x141/0x370 net/netlink/af_netlink.c:2281
 [<ffffffff82613ddf>] rtnetlink_rcv+0x2f/0x40 net/core/rtnetlink.c:3456
 [<ffffffff8274ad38>] netlink_unicast_kernel net/netlink/af_netlink.c:1206 [inline]
 [<ffffffff8274ad38>] netlink_unicast+0x518/0x750 net/netlink/af_netlink.c:1232
 [<ffffffff8274b83e>] netlink_sendmsg+0x8ce/0xc30 net/netlink/af_netlink.c:1778
 [<ffffffff82564aff>] sock_sendmsg_nosec net/socket.c:609 [inline]
 [<ffffffff82564aff>] sock_sendmsg+0xcf/0x110 net/socket.c:619
 [<ffffffff82564d62>] sock_write_iter+0x222/0x3a0 net/socket.c:834
 [<ffffffff8178523d>] new_sync_write+0x1dd/0x2b0 fs/read_write.c:478
 [<ffffffff817853f4>] __vfs_write+0xe4/0x110 fs/read_write.c:491
 [<ffffffff81786c38>] vfs_write+0x178/0x4b0 fs/read_write.c:538
 [<ffffffff817892a9>] SYSC_write fs/read_write.c:585 [inline]
 [<ffffffff817892a9>] SyS_write+0xd9/0x1b0 fs/read_write.c:577
 [<ffffffff82c71e32>] entry_SYSCALL_64_fastpath+0x12/0x17

Note: there is no "Fixes" tag as this seems to be a bug introduced
very early.
Signed-off-by: default avatarWei Wang <weiwan@google.com>
Acked-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 68a66d14
...@@ -1018,7 +1018,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, ...@@ -1018,7 +1018,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
/* Create subtree root node */ /* Create subtree root node */
sfn = node_alloc(); sfn = node_alloc();
if (!sfn) if (!sfn)
goto st_failure; goto failure;
sfn->leaf = info->nl_net->ipv6.ip6_null_entry; sfn->leaf = info->nl_net->ipv6.ip6_null_entry;
atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref); atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref);
...@@ -1035,12 +1035,12 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, ...@@ -1035,12 +1035,12 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
if (IS_ERR(sn)) { if (IS_ERR(sn)) {
/* If it is failed, discard just allocated /* If it is failed, discard just allocated
root, and then (in st_failure) stale node root, and then (in failure) stale node
in main tree. in main tree.
*/ */
node_free(sfn); node_free(sfn);
err = PTR_ERR(sn); err = PTR_ERR(sn);
goto st_failure; goto failure;
} }
/* Now link new subtree to main tree */ /* Now link new subtree to main tree */
...@@ -1055,7 +1055,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, ...@@ -1055,7 +1055,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
if (IS_ERR(sn)) { if (IS_ERR(sn)) {
err = PTR_ERR(sn); err = PTR_ERR(sn);
goto st_failure; goto failure;
} }
} }
...@@ -1096,18 +1096,17 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, ...@@ -1096,18 +1096,17 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
atomic_inc(&pn->leaf->rt6i_ref); atomic_inc(&pn->leaf->rt6i_ref);
} }
#endif #endif
/* Always release dst as dst->__refcnt is guaranteed goto failure;
* to be taken before entering this function
*/
dst_release_immediate(&rt->dst);
} }
return err; return err;
#ifdef CONFIG_IPV6_SUBTREES failure:
/* Subtree creation failed, probably main tree node /* fn->leaf could be NULL if fn is an intermediate node and we
is orphan. If it is, shoot it. * failed to add the new route to it in both subtree creation
* failure and fib6_add_rt2node() failure case.
* In both cases, fib6_repair_tree() should be called to fix
* fn->leaf.
*/ */
st_failure:
if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT))) if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)))
fib6_repair_tree(info->nl_net, fn); fib6_repair_tree(info->nl_net, fn);
/* Always release dst as dst->__refcnt is guaranteed /* Always release dst as dst->__refcnt is guaranteed
...@@ -1115,7 +1114,6 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, ...@@ -1115,7 +1114,6 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
*/ */
dst_release_immediate(&rt->dst); dst_release_immediate(&rt->dst);
return err; return err;
#endif
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment