@@ -1964,19 +1964,35 @@ int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
struct mfc6_cache *c;
struct net *net = sock_net(sk);
struct mr_table *mrt;
-
- mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
- if (!mrt)
- return -ENOENT;
+ int err;
switch (cmd) {
case SIOCGETMIFCNT_IN6:
if (copy_from_user(&vr, arg, sizeof(vr)))
return -EFAULT;
- if (vr.mifi >= mrt->maxvif)
- return -EINVAL;
+ break;
+ case SIOCGETSGCNT_IN6:
+ if (copy_from_user(&sr, arg, sizeof(sr)))
+ return -EFAULT;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ rcu_read_lock();
+ mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
+ if (!mrt) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ switch (cmd) {
+ case SIOCGETMIFCNT_IN6:
+ if (vr.mifi >= mrt->maxvif) {
+ err = -EINVAL;
+ goto out;
+ }
vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif);
- rcu_read_lock();
vif = &mrt->vif_table[vr.mifi];
if (VIF_EXISTS(mrt, vr.mifi)) {
vr.icount = READ_ONCE(vif->pkt_in);
@@ -1989,13 +2005,9 @@ int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
return -EFAULT;
return 0;
}
- rcu_read_unlock();
- return -EADDRNOTAVAIL;
+ err = -EADDRNOTAVAIL;
+ goto out;
case SIOCGETSGCNT_IN6:
- if (copy_from_user(&sr, arg, sizeof(sr)))
- return -EFAULT;
-
- rcu_read_lock();
c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr);
if (c) {
sr.pktcnt = c->_c.mfc_un.res.pkt;
@@ -2007,11 +2019,13 @@ int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
return -EFAULT;
return 0;
}
- rcu_read_unlock();
- return -EADDRNOTAVAIL;
- default:
- return -ENOIOCTLCMD;
+ err = -EADDRNOTAVAIL;
+ goto out;
}
+
+out:
+ rcu_read_unlock();
+ return err;
}
#endif
When IPV6_MROUTE_MULTIPLE_TABLES is enabled, multicast routing tables must be read under RCU or RTNL lock. Copy from user space must be performed beforehand as we are not allowed to sleep under RCU lock. Signed-off-by: Stefan Wiehler <stefan.wiehler@nokia.com> Fixes: d1db275dd3f6 ("ipv6: ip6mr: support multiple tables") --- net/ipv6/ip6mr.c | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-)