ipv6: check sk sk_type and protocol early in ip_mroute_set/getsockopt
authorXin Long <lucien.xin@gmail.com>
Fri, 24 Feb 2017 08:29:06 +0000 (16:29 +0800)
committerDanny Wood <danwood76@gmail.com>
Tue, 28 Sep 2021 12:06:42 +0000 (13:06 +0100)
[ Upstream commit 99253eb750fda6a644d5188fb26c43bad8d5a745 ]

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 <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Change-Id: I8d48f4611a2f2d0cb7ad5146036f571f12ecb1fc
CVE-2017-18509
Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org>
net/ipv6/ip6mr.c

index 13d47e177665c6875785009b906a02d47365de8a..d9811e4079aef90e4ae4863ada2cb93b65e0c988 100644 (file)
@@ -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;
 
@@ -1811,6 +1812,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;