]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ipv6: Unlink sibling route in case of failure
authorIdo Schimmel <idosch@mellanox.com>
Wed, 17 Jul 2019 20:39:33 +0000 (23:39 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 28 Jul 2019 06:28:25 +0000 (08:28 +0200)
[ 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>
net/ipv6/ip6_fib.c

index 9915f64b38a04e42b676208e473f641a781f6d82..4b1a898982d0a9a8ae003c55202ccd6154abd079 100644 (file)
@@ -1113,8 +1113,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);