Skip to content

Commit

Permalink
ip6mr: fix tables suspicious RCU usage
Browse files Browse the repository at this point in the history
Several places call ip6mr_get_table() with no RCU nor RTNL lock.
Add RCU protection inside such helper and provide a lockless variant
for the few callers that already acquired the relevant lock.

Note that some users additionally reference the table outside the RCU
lock. That is actually safe as the table deletion can happen only
after all table accesses are completed.

Fixes: e2d5776 ("net: Provide compat support for SIOCGETMIFCNT_IN6 and SIOCGETSGCNT_IN6.")
Fixes: d7c31cb ("net: ip6mr: add RTM_GETROUTE netlink op")
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Paolo Abeni committed Nov 28, 2024
1 parent 11b6e70 commit f1553c9
Showing 1 changed file with 27 additions and 11 deletions.
38 changes: 27 additions & 11 deletions net/ipv6/ip6mr.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ static struct mr_table *ip6mr_mr_table_iter(struct net *net,
return ret;
}

static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
static struct mr_table *__ip6mr_get_table(struct net *net, u32 id)
{
struct mr_table *mrt;

Expand All @@ -141,6 +141,16 @@ static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
return NULL;
}

static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
{
struct mr_table *mrt;

rcu_read_lock();
mrt = __ip6mr_get_table(net, id);
rcu_read_unlock();
return mrt;
}

static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
struct mr_table **mrt)
{
Expand Down Expand Up @@ -182,7 +192,7 @@ static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp,

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

mrt = ip6mr_get_table(rule->fr_net, arg->table);
mrt = __ip6mr_get_table(rule->fr_net, arg->table);
if (!mrt)
return -EAGAIN;
res->mrt = mrt;
Expand Down Expand Up @@ -314,6 +324,8 @@ static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
return net->ipv6.mrt6;
}

#define __ip6mr_get_table ip6mr_get_table

static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
struct mr_table **mrt)
{
Expand Down Expand Up @@ -392,7 +404,7 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
{
struct mr_table *mrt;

mrt = ip6mr_get_table(net, id);
mrt = __ip6mr_get_table(net, id);
if (mrt)
return mrt;

Expand Down Expand Up @@ -425,13 +437,15 @@ static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos)
struct net *net = seq_file_net(seq);
struct mr_table *mrt;

mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
if (!mrt)
rcu_read_lock();
mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
if (!mrt) {
rcu_read_unlock();
return ERR_PTR(-ENOENT);
}

iter->mrt = mrt;

rcu_read_lock();
return mr_vif_seq_start(seq, pos);
}

Expand Down Expand Up @@ -2292,11 +2306,13 @@ int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
struct mfc6_cache *cache;
struct rt6_info *rt = dst_rt6_info(skb_dst(skb));

mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
if (!mrt)
rcu_read_lock();
mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
if (!mrt) {
rcu_read_unlock();
return -ENOENT;
}

rcu_read_lock();
cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr);
if (!cache && skb->dev) {
int vif = ip6mr_find_vif(mrt, skb->dev);
Expand Down Expand Up @@ -2576,7 +2592,7 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
grp = nla_get_in6_addr(tb[RTA_DST]);
tableid = nla_get_u32_default(tb[RTA_TABLE], 0);

mrt = ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
if (!mrt) {
NL_SET_ERR_MSG_MOD(extack, "MR table does not exist");
return -ENOENT;
Expand Down Expand Up @@ -2623,7 +2639,7 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
if (filter.table_id) {
struct mr_table *mrt;

mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id);
mrt = __ip6mr_get_table(sock_net(skb->sk), filter.table_id);
if (!mrt) {
if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR)
return skb->len;
Expand Down

0 comments on commit f1553c9

Please sign in to comment.