]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ip6mr: Fix notifiers call on mroute_clean_tables()
authorNir Dotan <nird@mellanox.com>
Sun, 27 Jan 2019 07:26:22 +0000 (09:26 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Jan 2019 07:16:07 +0000 (23:16 -0800)
When the MC route socket is closed, mroute_clean_tables() is called to
cleanup existing routes. Mistakenly notifiers call was put on the cleanup
of the unresolved MC route entries cache.
In a case where the MC socket closes before an unresolved route expires,
the notifier call leads to a crash, caused by the driver trying to
increment a non initialized refcount_t object [1] and then when handling
is done, to decrement it [2]. This was detected by a test recently added in
commit deb5e22c949d ("selftests: forwarding: Add multicast routing test").

Fix that by putting notifiers call on the resolved entries traversal,
instead of on the unresolved entries traversal.

[1]

[  245.748967] refcount_t: increment on 0; use-after-free.
[  245.754829] WARNING: CPU: 3 PID: 3223 at lib/refcount.c:153 refcount_inc_checked+0x2b/0x30
...
[  245.802357] Hardware name: Mellanox Technologies Ltd. MSN2740/SA001237, BIOS 5.6.5 06/07/2016
[  245.811873] RIP: 0010:refcount_inc_checked+0x2b/0x30
...
[  245.907487] Call Trace:
[  245.910231]  mlxsw_sp_router_fib_event.cold.181+0x42/0x47 [mlxsw_spectrum]
[  245.917913]  notifier_call_chain+0x45/0x7
[  245.922484]  atomic_notifier_call_chain+0x15/0x20
[  245.927729]  call_fib_notifiers+0x15/0x30
[  245.932205]  mroute_clean_tables+0x372/0x3f
[  245.936971]  ip6mr_sk_done+0xb1/0xc0
[  245.940960]  ip6_mroute_setsockopt+0x1da/0x5f0
...

[2]

[  246.128487] refcount_t: underflow; use-after-free.
[  246.133859] WARNING: CPU: 0 PID: 7 at lib/refcount.c:187 refcount_sub_and_test_checked+0x4c/0x60
[  246.183521] Hardware name: Mellanox Technologies Ltd. MSN2740/SA001237, BIOS 5.6.5 06/07/2016
...
[  246.193062] Workqueue: mlxsw_core_ordered mlxsw_sp_router_fibmr_event_work [mlxsw_spectrum]
[  246.202394] RIP: 0010:refcount_sub_and_test_checked+0x4c/0x60
...
[  246.298889] Call Trace:
[  246.301617]  refcount_dec_and_test_checked+0x11/0x20
[  246.307170]  mlxsw_sp_router_fibmr_event_work.cold.196+0x47/0x78 [mlxsw_spectrum]
[  246.315531]  process_one_work+0x1fa/0x3f0
[  246.320005]  worker_thread+0x2f/0x3e0
[  246.324083]  kthread+0x118/0x130
[  246.327683]  ? wq_update_unbound_numa+0x1b0/0x1b0
[  246.332926]  ? kthread_park+0x80/0x80
[  246.337013]  ret_from_fork+0x1f/0x30

Fixes: 635ffa8e7a59 ("ip6mr: Support fib notifications")
Signed-off-by: Nir Dotan <nird@mellanox.com>
Reviewed-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/ip6mr.c

index 30337b38274b29f0f4816f9f0a3c7f6a1ffbf1cb..cc01aa3f2b5e3aed278dbe08c889f727ff565dac 100644 (file)
@@ -1516,6 +1516,9 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
                        continue;
                rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
                list_del_rcu(&c->list);
+               call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
+                                              FIB_EVENT_ENTRY_DEL,
+                                              (struct mfc6_cache *)c, mrt->id);
                mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
                mr_cache_put(c);
        }
@@ -1524,10 +1527,6 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
                spin_lock_bh(&mfc_unres_lock);
                list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
                        list_del(&c->list);
-                       call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
-                                                      FIB_EVENT_ENTRY_DEL,
-                                                      (struct mfc6_cache *)c,
-                                                      mrt->id);
                        mr6_netlink_event(mrt, (struct mfc6_cache *)c,
                                          RTM_DELROUTE);
                        ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);