]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/4.9.164/route-set-the-deleted-fnhe-fnhe_daddr-to-0-in-ip_del_fnhe-to-fix-a-race.patch
Linux 4.9.164
[thirdparty/kernel/stable-queue.git] / releases / 4.9.164 / route-set-the-deleted-fnhe-fnhe_daddr-to-0-in-ip_del_fnhe-to-fix-a-race.patch
CommitLineData
8cbcfc8d
GKH
1From foo@baz Fri Mar 15 21:00:09 PDT 2019
2From: Xin Long <lucien.xin@gmail.com>
3Date: Fri, 8 Mar 2019 14:50:54 +0800
4Subject: route: set the deleted fnhe fnhe_daddr to 0 in ip_del_fnhe to fix a race
5
6From: Xin Long <lucien.xin@gmail.com>
7
8[ Upstream commit ee60ad219f5c7c4fb2f047f88037770063ef785f ]
9
10The race occurs in __mkroute_output() when 2 threads lookup a dst:
11
12 CPU A CPU B
13 find_exception()
14 find_exception() [fnhe expires]
15 ip_del_fnhe() [fnhe is deleted]
16 rt_bind_exception()
17
18In rt_bind_exception() it will bind a deleted fnhe with the new dst, and
19this dst will get no chance to be freed. It causes a dev defcnt leak and
20consecutive dmesg warnings:
21
22 unregister_netdevice: waiting for ethX to become free. Usage count = 1
23
24Especially thanks Jon to identify the issue.
25
26This patch fixes it by setting fnhe_daddr to 0 in ip_del_fnhe() to stop
27binding the deleted fnhe with a new dst when checking fnhe's fnhe_daddr
28and daddr in rt_bind_exception().
29
30It works as both ip_del_fnhe() and rt_bind_exception() are protected by
31fnhe_lock and the fhne is freed by kfree_rcu().
32
33Fixes: deed49df7390 ("route: check and remove route cache when we get route")
34Signed-off-by: Jon Maxwell <jmaxwell37@gmail.com>
35Signed-off-by: Xin Long <lucien.xin@gmail.com>
36Reviewed-by: David Ahern <dsahern@gmail.com>
37Signed-off-by: David S. Miller <davem@davemloft.net>
38Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
39---
40 net/ipv4/route.c | 4 ++++
41 1 file changed, 4 insertions(+)
42
43--- a/net/ipv4/route.c
44+++ b/net/ipv4/route.c
45@@ -1613,6 +1613,10 @@ static void ip_del_fnhe(struct fib_nh *n
46 if (fnhe->fnhe_daddr == daddr) {
47 rcu_assign_pointer(*fnhe_p, rcu_dereference_protected(
48 fnhe->fnhe_next, lockdep_is_held(&fnhe_lock)));
49+ /* set fnhe_daddr to 0 to ensure it won't bind with
50+ * new dsts in rt_bind_exception().
51+ */
52+ fnhe->fnhe_daddr = 0;
53 fnhe_flush_routes(fnhe);
54 kfree_rcu(fnhe, rcu);
55 break;