Skip to content

Commit 54e6fe9

Browse files
edumazetkuba-moo
authored andcommitted
ipv6: prevent infinite loop in rt6_nlmsg_size()
While testing prior patch, I was able to trigger an infinite loop in rt6_nlmsg_size() in the following place: list_for_each_entry_rcu(sibling, &f6i->fib6_siblings, fib6_siblings) { rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len); } This is because fib6_del_route() and fib6_add_rt2node() uses list_del_rcu(), which can confuse rcu readers, because they might no longer see the head of the list. Restart the loop if f6i->fib6_nsiblings is zero. Fixes: d9ccb18 ("ipv6: Fix soft lockups in fib6_select_path under high next hop churn") Signed-off-by: Eric Dumazet <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent ea2f921 commit 54e6fe9

File tree

2 files changed

+20
-18
lines changed

2 files changed

+20
-18
lines changed

net/ipv6/ip6_fib.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
12651265
&rt->fib6_siblings,
12661266
fib6_siblings)
12671267
sibling->fib6_nsiblings--;
1268-
rt->fib6_nsiblings = 0;
1268+
WRITE_ONCE(rt->fib6_nsiblings, 0);
12691269
list_del_rcu(&rt->fib6_siblings);
12701270
rcu_read_lock();
12711271
rt6_multipath_rebalance(next_sibling);
@@ -2015,7 +2015,7 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
20152015
list_for_each_entry_safe(sibling, next_sibling,
20162016
&rt->fib6_siblings, fib6_siblings)
20172017
sibling->fib6_nsiblings--;
2018-
rt->fib6_nsiblings = 0;
2018+
WRITE_ONCE(rt->fib6_nsiblings, 0);
20192019
list_del_rcu(&rt->fib6_siblings);
20202020
rt6_multipath_rebalance(next_sibling);
20212021
}

net/ipv6/route.c

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5670,32 +5670,34 @@ static int rt6_nh_nlmsg_size(struct fib6_nh *nh, void *arg)
56705670

56715671
static size_t rt6_nlmsg_size(struct fib6_info *f6i)
56725672
{
5673+
struct fib6_info *sibling;
5674+
struct fib6_nh *nh;
56735675
int nexthop_len;
56745676

56755677
if (f6i->nh) {
56765678
nexthop_len = nla_total_size(4); /* RTA_NH_ID */
56775679
nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_nlmsg_size,
56785680
&nexthop_len);
5679-
} else {
5680-
struct fib6_nh *nh = f6i->fib6_nh;
5681-
struct fib6_info *sibling;
5682-
5683-
nexthop_len = 0;
5684-
if (f6i->fib6_nsiblings) {
5685-
rt6_nh_nlmsg_size(nh, &nexthop_len);
5686-
5687-
rcu_read_lock();
5681+
goto common;
5682+
}
56885683

5689-
list_for_each_entry_rcu(sibling, &f6i->fib6_siblings,
5690-
fib6_siblings) {
5691-
rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len);
5692-
}
5684+
rcu_read_lock();
5685+
retry:
5686+
nh = f6i->fib6_nh;
5687+
nexthop_len = 0;
5688+
if (READ_ONCE(f6i->fib6_nsiblings)) {
5689+
rt6_nh_nlmsg_size(nh, &nexthop_len);
56935690

5694-
rcu_read_unlock();
5691+
list_for_each_entry_rcu(sibling, &f6i->fib6_siblings,
5692+
fib6_siblings) {
5693+
rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len);
5694+
if (!READ_ONCE(f6i->fib6_nsiblings))
5695+
goto retry;
56955696
}
5696-
nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
56975697
}
5698-
5698+
rcu_read_unlock();
5699+
nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
5700+
common:
56995701
return NLMSG_ALIGN(sizeof(struct rtmsg))
57005702
+ nla_total_size(16) /* RTA_SRC */
57015703
+ nla_total_size(16) /* RTA_DST */

0 commit comments

Comments
 (0)