ipv6: check sk sk_type and protocol early in ip_mroute_set/getsockopt [Linux 3.16.72]

This Linux kernel change "ipv6: check sk sk_type and protocol early in ip_mroute_set/getsockopt" is included in the Linux 3.16.72 release. This change is authored by Xin Long <lucien.xin [at] gmail.com> on Fri Feb 24 16:29:06 2017 +0800. The commit for this change in Linux stable tree is 2b8d63b (patch) which is from upstream commit 99253eb. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 99253eb.

ipv6: check sk sk_type and protocol early in ip_mroute_set/getsockopt

commit 99253eb750fda6a644d5188fb26c43bad8d5a745 upstream.

Commit 5e1859fbcc3c ("ipv4: ipmr: various fixes and cleanups") fixed
the issue for ipv4 ipmr:

  ip_mroute_setsockopt() & ip_mroute_getsockopt() should not
  access/set raw_sk(sk)->ipmr_table before making sure the socket
  is a raw socket, and protocol is IGMP

The same fix should be done for ipv6 ipmr as well.

This patch can fix the panic caused by overwriting the same offset
as ipmr_table as in raw_sk(sk) when accessing other type's socket
by ip_mroute_setsockopt().

Signed-off-by: Xin Long <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Ben Hutchings <[email protected]>

There are 11 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 net/ipv6/ip6mr.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 42978998..5733b05 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1662,6 +1662,10 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
    struct net *net = sock_net(sk);
    struct mr6_table *mrt;

+   if (sk->sk_type != SOCK_RAW ||
+       inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
+       return -EOPNOTSUPP;
+
    mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
    if (mrt == NULL)
        return -ENOENT;
@@ -1673,9 +1677,6 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns

    switch (optname) {
    case MRT6_INIT:
-       if (sk->sk_type != SOCK_RAW ||
-           inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
-           return -EOPNOTSUPP;
        if (optlen < sizeof(int))
            return -EINVAL;

@@ -1812,6 +1813,10 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval,
    struct net *net = sock_net(sk);
    struct mr6_table *mrt;

+   if (sk->sk_type != SOCK_RAW ||
+       inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
+       return -EOPNOTSUPP;
+
    mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
    if (mrt == NULL)
        return -ENOENT;

Leave a Reply

Your email address will not be published. Required fields are marked *