summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIdo Schimmel <idosch@mellanox.com>2019-07-17 23:39:33 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-07-28 08:29:24 +0200
commitc0f4a6447977b165354940db4c0f2e14a54654dc (patch)
tree738d055a3186a25560ac2e837aeb2d473144b23b
parent0bd84505f16f43e588d91ec8e0834faa5d42e8b9 (diff)
downloadlinux-stable-c0f4a6447977b165354940db4c0f2e14a54654dc.tar.gz
linux-stable-c0f4a6447977b165354940db4c0f2e14a54654dc.tar.bz2
linux-stable-c0f4a6447977b165354940db4c0f2e14a54654dc.zip
ipv6: Unlink sibling route in case of failure
[ Upstream commit 54851aa90cf27041d64b12f65ac72e9f97bd90fd ] When a route needs to be appended to an existing multipath route, fib6_add_rt2node() first appends it to the siblings list and increments the number of sibling routes on each sibling. Later, the function notifies the route via call_fib6_entry_notifiers(). In case the notification is vetoed, the route is not unlinked from the siblings list, which can result in a use-after-free. Fix this by unlinking the route from the siblings list before returning an error. Audited the rest of the call sites from which the FIB notification chain is called and could not find more problems. Fixes: 2233000cba40 ("net/ipv6: Move call_fib6_entry_notifiers up for route adds") Signed-off-by: Ido Schimmel <idosch@mellanox.com> Reported-by: Alexander Petrovskiy <alexpe@mellanox.com> Reviewed-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--net/ipv6/ip6_fib.c18
1 files changed, 17 insertions, 1 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index a6c0479c1d55..bbb5ffb3397d 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1081,8 +1081,24 @@ add:
err = call_fib6_entry_notifiers(info->nl_net,
FIB_EVENT_ENTRY_ADD,
rt, extack);
- if (err)
+ if (err) {
+ struct fib6_info *sibling, *next_sibling;
+
+ /* If the route has siblings, then it first
+ * needs to be unlinked from them.
+ */
+ if (!rt->fib6_nsiblings)
+ return err;
+
+ list_for_each_entry_safe(sibling, next_sibling,
+ &rt->fib6_siblings,
+ fib6_siblings)
+ sibling->fib6_nsiblings--;
+ rt->fib6_nsiblings = 0;
+ list_del_init(&rt->fib6_siblings);
+ rt6_multipath_rebalance(next_sibling);
return err;
+ }
rcu_assign_pointer(rt->fib6_next, iter);
atomic_inc(&rt->fib6_ref);