Skip to content

Commit 0dd2985

Browse files
author
Paolo Abeni
committed
Merge branch 'net-fix-mcast-rcu-splats'
Paolo Abeni says: ==================== net: fix mcast RCU splats This series addresses the RCU splat triggered by the forwarding mroute tests. The first patch does not address any specific issue, but makes the following ones more clear. Patch 2 and 3 address the issue for ipv6 and ipv4 respectively. ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
2 parents 663a917 + fc9c273 commit 0dd2985

File tree

2 files changed

+84
-24
lines changed

2 files changed

+84
-24
lines changed

net/ipv4/ipmr.c

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ static void ipmr_expire_process(struct timer_list *t);
120120
lockdep_rtnl_is_held() || \
121121
list_empty(&net->ipv4.mr_tables))
122122

123+
static bool ipmr_can_free_table(struct net *net)
124+
{
125+
return !check_net(net) || !net->ipv4.mr_rules_ops;
126+
}
127+
123128
static struct mr_table *ipmr_mr_table_iter(struct net *net,
124129
struct mr_table *mrt)
125130
{
@@ -137,7 +142,7 @@ static struct mr_table *ipmr_mr_table_iter(struct net *net,
137142
return ret;
138143
}
139144

140-
static struct mr_table *ipmr_get_table(struct net *net, u32 id)
145+
static struct mr_table *__ipmr_get_table(struct net *net, u32 id)
141146
{
142147
struct mr_table *mrt;
143148

@@ -148,6 +153,16 @@ static struct mr_table *ipmr_get_table(struct net *net, u32 id)
148153
return NULL;
149154
}
150155

156+
static struct mr_table *ipmr_get_table(struct net *net, u32 id)
157+
{
158+
struct mr_table *mrt;
159+
160+
rcu_read_lock();
161+
mrt = __ipmr_get_table(net, id);
162+
rcu_read_unlock();
163+
return mrt;
164+
}
165+
151166
static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,
152167
struct mr_table **mrt)
153168
{
@@ -189,7 +204,7 @@ static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp,
189204

190205
arg->table = fib_rule_get_table(rule, arg);
191206

192-
mrt = ipmr_get_table(rule->fr_net, arg->table);
207+
mrt = __ipmr_get_table(rule->fr_net, arg->table);
193208
if (!mrt)
194209
return -EAGAIN;
195210
res->mrt = mrt;
@@ -302,6 +317,11 @@ EXPORT_SYMBOL(ipmr_rule_default);
302317
#define ipmr_for_each_table(mrt, net) \
303318
for (mrt = net->ipv4.mrt; mrt; mrt = NULL)
304319

320+
static bool ipmr_can_free_table(struct net *net)
321+
{
322+
return !check_net(net);
323+
}
324+
305325
static struct mr_table *ipmr_mr_table_iter(struct net *net,
306326
struct mr_table *mrt)
307327
{
@@ -315,6 +335,8 @@ static struct mr_table *ipmr_get_table(struct net *net, u32 id)
315335
return net->ipv4.mrt;
316336
}
317337

338+
#define __ipmr_get_table ipmr_get_table
339+
318340
static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,
319341
struct mr_table **mrt)
320342
{
@@ -403,7 +425,7 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
403425
if (id != RT_TABLE_DEFAULT && id >= 1000000000)
404426
return ERR_PTR(-EINVAL);
405427

406-
mrt = ipmr_get_table(net, id);
428+
mrt = __ipmr_get_table(net, id);
407429
if (mrt)
408430
return mrt;
409431

@@ -413,6 +435,10 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
413435

414436
static void ipmr_free_table(struct mr_table *mrt)
415437
{
438+
struct net *net = read_pnet(&mrt->net);
439+
440+
DEBUG_NET_WARN_ON_ONCE(!ipmr_can_free_table(net));
441+
416442
timer_shutdown_sync(&mrt->ipmr_expire_timer);
417443
mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC |
418444
MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC);
@@ -1374,7 +1400,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
13741400
goto out_unlock;
13751401
}
13761402

1377-
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1403+
mrt = __ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
13781404
if (!mrt) {
13791405
ret = -ENOENT;
13801406
goto out_unlock;
@@ -2262,11 +2288,13 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
22622288
struct mr_table *mrt;
22632289
int err;
22642290

2265-
mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
2266-
if (!mrt)
2291+
rcu_read_lock();
2292+
mrt = __ipmr_get_table(net, RT_TABLE_DEFAULT);
2293+
if (!mrt) {
2294+
rcu_read_unlock();
22672295
return -ENOENT;
2296+
}
22682297

2269-
rcu_read_lock();
22702298
cache = ipmr_cache_find(mrt, saddr, daddr);
22712299
if (!cache && skb->dev) {
22722300
int vif = ipmr_find_vif(mrt, skb->dev);
@@ -2550,7 +2578,7 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
25502578
grp = nla_get_in_addr_default(tb[RTA_DST], 0);
25512579
tableid = nla_get_u32_default(tb[RTA_TABLE], 0);
25522580

2553-
mrt = ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT);
2581+
mrt = __ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT);
25542582
if (!mrt) {
25552583
err = -ENOENT;
25562584
goto errout_free;
@@ -2604,7 +2632,7 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
26042632
if (filter.table_id) {
26052633
struct mr_table *mrt;
26062634

2607-
mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id);
2635+
mrt = __ipmr_get_table(sock_net(skb->sk), filter.table_id);
26082636
if (!mrt) {
26092637
if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IPMR)
26102638
return skb->len;
@@ -2712,7 +2740,7 @@ static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh,
27122740
break;
27132741
}
27142742
}
2715-
mrt = ipmr_get_table(net, tblid);
2743+
mrt = __ipmr_get_table(net, tblid);
27162744
if (!mrt) {
27172745
ret = -ENOENT;
27182746
goto out;
@@ -2920,13 +2948,15 @@ static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
29202948
struct net *net = seq_file_net(seq);
29212949
struct mr_table *mrt;
29222950

2923-
mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
2924-
if (!mrt)
2951+
rcu_read_lock();
2952+
mrt = __ipmr_get_table(net, RT_TABLE_DEFAULT);
2953+
if (!mrt) {
2954+
rcu_read_unlock();
29252955
return ERR_PTR(-ENOENT);
2956+
}
29262957

29272958
iter->mrt = mrt;
29282959

2929-
rcu_read_lock();
29302960
return mr_vif_seq_start(seq, pos);
29312961
}
29322962

net/ipv6/ip6mr.c

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ static void ipmr_expire_process(struct timer_list *t);
108108
lockdep_rtnl_is_held() || \
109109
list_empty(&net->ipv6.mr6_tables))
110110

111+
static bool ip6mr_can_free_table(struct net *net)
112+
{
113+
return !check_net(net) || !net->ipv6.mr6_rules_ops;
114+
}
115+
111116
static struct mr_table *ip6mr_mr_table_iter(struct net *net,
112117
struct mr_table *mrt)
113118
{
@@ -125,7 +130,7 @@ static struct mr_table *ip6mr_mr_table_iter(struct net *net,
125130
return ret;
126131
}
127132

128-
static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
133+
static struct mr_table *__ip6mr_get_table(struct net *net, u32 id)
129134
{
130135
struct mr_table *mrt;
131136

@@ -136,6 +141,16 @@ static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
136141
return NULL;
137142
}
138143

144+
static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
145+
{
146+
struct mr_table *mrt;
147+
148+
rcu_read_lock();
149+
mrt = __ip6mr_get_table(net, id);
150+
rcu_read_unlock();
151+
return mrt;
152+
}
153+
139154
static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
140155
struct mr_table **mrt)
141156
{
@@ -177,7 +192,7 @@ static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp,
177192

178193
arg->table = fib_rule_get_table(rule, arg);
179194

180-
mrt = ip6mr_get_table(rule->fr_net, arg->table);
195+
mrt = __ip6mr_get_table(rule->fr_net, arg->table);
181196
if (!mrt)
182197
return -EAGAIN;
183198
res->mrt = mrt;
@@ -291,6 +306,11 @@ EXPORT_SYMBOL(ip6mr_rule_default);
291306
#define ip6mr_for_each_table(mrt, net) \
292307
for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
293308

309+
static bool ip6mr_can_free_table(struct net *net)
310+
{
311+
return !check_net(net);
312+
}
313+
294314
static struct mr_table *ip6mr_mr_table_iter(struct net *net,
295315
struct mr_table *mrt)
296316
{
@@ -304,6 +324,8 @@ static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
304324
return net->ipv6.mrt6;
305325
}
306326

327+
#define __ip6mr_get_table ip6mr_get_table
328+
307329
static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
308330
struct mr_table **mrt)
309331
{
@@ -382,7 +404,7 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
382404
{
383405
struct mr_table *mrt;
384406

385-
mrt = ip6mr_get_table(net, id);
407+
mrt = __ip6mr_get_table(net, id);
386408
if (mrt)
387409
return mrt;
388410

@@ -392,6 +414,10 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
392414

393415
static void ip6mr_free_table(struct mr_table *mrt)
394416
{
417+
struct net *net = read_pnet(&mrt->net);
418+
419+
DEBUG_NET_WARN_ON_ONCE(!ip6mr_can_free_table(net));
420+
395421
timer_shutdown_sync(&mrt->ipmr_expire_timer);
396422
mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
397423
MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC);
@@ -411,13 +437,15 @@ static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos)
411437
struct net *net = seq_file_net(seq);
412438
struct mr_table *mrt;
413439

414-
mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
415-
if (!mrt)
440+
rcu_read_lock();
441+
mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
442+
if (!mrt) {
443+
rcu_read_unlock();
416444
return ERR_PTR(-ENOENT);
445+
}
417446

418447
iter->mrt = mrt;
419448

420-
rcu_read_lock();
421449
return mr_vif_seq_start(seq, pos);
422450
}
423451

@@ -2278,11 +2306,13 @@ int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
22782306
struct mfc6_cache *cache;
22792307
struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
22802308

2281-
mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
2282-
if (!mrt)
2309+
rcu_read_lock();
2310+
mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
2311+
if (!mrt) {
2312+
rcu_read_unlock();
22832313
return -ENOENT;
2314+
}
22842315

2285-
rcu_read_lock();
22862316
cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr);
22872317
if (!cache && skb->dev) {
22882318
int vif = ip6mr_find_vif(mrt, skb->dev);
@@ -2562,7 +2592,7 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
25622592
grp = nla_get_in6_addr(tb[RTA_DST]);
25632593
tableid = nla_get_u32_default(tb[RTA_TABLE], 0);
25642594

2565-
mrt = ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
2595+
mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
25662596
if (!mrt) {
25672597
NL_SET_ERR_MSG_MOD(extack, "MR table does not exist");
25682598
return -ENOENT;
@@ -2609,7 +2639,7 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
26092639
if (filter.table_id) {
26102640
struct mr_table *mrt;
26112641

2612-
mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id);
2642+
mrt = __ip6mr_get_table(sock_net(skb->sk), filter.table_id);
26132643
if (!mrt) {
26142644
if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR)
26152645
return skb->len;

0 commit comments

Comments
 (0)