Skip to content

Commit 11b6e70

Browse files
author
Paolo Abeni
committed
ipmr: add debug check for mr table cleanup
The multicast route tables lifecycle, for both ipv4 and ipv6, is protected by RCU using the RTNL lock for write access. In many places a table pointer escapes the RCU (or RTNL) protected critical section, but such scenarios are actually safe because tables are deleted only at namespace cleanup time or just after allocation, in case of default rule creation failure. Tables freed at namespace cleanup time are assured to be alive for the whole netns lifetime; tables freed just after creation time are never exposed to other possible users. Ensure that the free conditions are respected in ip{,6}mr_free_table, to document the locking schema and to prevent future possible introduction of 'table del' operation from breaking it. Reviewed-by: David Ahern <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 663a917 commit 11b6e70

File tree

2 files changed

+28
-0
lines changed

2 files changed

+28
-0
lines changed

net/ipv4/ipmr.c

Lines changed: 14 additions & 0 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
{
@@ -302,6 +307,11 @@ EXPORT_SYMBOL(ipmr_rule_default);
302307
#define ipmr_for_each_table(mrt, net) \
303308
for (mrt = net->ipv4.mrt; mrt; mrt = NULL)
304309

310+
static bool ipmr_can_free_table(struct net *net)
311+
{
312+
return !check_net(net);
313+
}
314+
305315
static struct mr_table *ipmr_mr_table_iter(struct net *net,
306316
struct mr_table *mrt)
307317
{
@@ -413,6 +423,10 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
413423

414424
static void ipmr_free_table(struct mr_table *mrt)
415425
{
426+
struct net *net = read_pnet(&mrt->net);
427+
428+
DEBUG_NET_WARN_ON_ONCE(!ipmr_can_free_table(net));
429+
416430
timer_shutdown_sync(&mrt->ipmr_expire_timer);
417431
mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC |
418432
MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC);

net/ipv6/ip6mr.c

Lines changed: 14 additions & 0 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
{
@@ -291,6 +296,11 @@ EXPORT_SYMBOL(ip6mr_rule_default);
291296
#define ip6mr_for_each_table(mrt, net) \
292297
for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
293298

299+
static bool ip6mr_can_free_table(struct net *net)
300+
{
301+
return !check_net(net);
302+
}
303+
294304
static struct mr_table *ip6mr_mr_table_iter(struct net *net,
295305
struct mr_table *mrt)
296306
{
@@ -392,6 +402,10 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
392402

393403
static void ip6mr_free_table(struct mr_table *mrt)
394404
{
405+
struct net *net = read_pnet(&mrt->net);
406+
407+
DEBUG_NET_WARN_ON_ONCE(!ip6mr_can_free_table(net));
408+
395409
timer_shutdown_sync(&mrt->ipmr_expire_timer);
396410
mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
397411
MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC);

0 commit comments

Comments
 (0)