dev_ioctl: copy only the smaller struct iwreq for wext
authorJohannes Berg <johannes.berg@intel.com>
Wed, 14 Jun 2017 07:28:11 +0000 (09:28 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 14 Jun 2017 11:52:44 +0000 (13:52 +0200)
Unfortunately, struct iwreq isn't a proper subset of struct ifreq,
but is still handled by the same code path. Robert reported that
then applications may (randomly) fault if the struct iwreq they
pass happens to land within 8 bytes of the end of a mapping (the
struct is only 32 bytes, vs. struct ifreq's 40 bytes).

To fix this, pull out the code handling wireless extension ioctls
and copy only the smaller structure in this case.

This bug goes back a long time, I tracked that it was introduced
into mainline in 2.1.15, over 20 years ago!

This fixes https://bugzilla.kernel.org/show_bug.cgi?id=195869

Reported-by: Robert O'Callahan <robert@ocallahan.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/wext.h
net/core/dev_ioctl.c
net/wireless/wext-core.c

index 345911965dbb8f53289d63535828969454ae3333..454ff763eeba9b829051862d7cce278be7d77b42 100644 (file)
@@ -6,7 +6,7 @@
 struct net;
 
 #ifdef CONFIG_WEXT_CORE
-int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
+int wext_handle_ioctl(struct net *net, struct iwreq *iwr, unsigned int cmd,
                      void __user *arg);
 int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
                             unsigned long arg);
@@ -14,7 +14,7 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
 struct iw_statistics *get_wireless_stats(struct net_device *dev);
 int call_commit_handler(struct net_device *dev);
 #else
-static inline int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
+static inline int wext_handle_ioctl(struct net *net, struct iwreq *iwr, unsigned int cmd,
                                    void __user *arg)
 {
        return -EINVAL;
index b94b1d29350603e19c3db8e6fd740d3f89440771..27fad31784a83861f942f3b4f82f44985a946645 100644 (file)
@@ -410,6 +410,22 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
        if (cmd == SIOCGIFNAME)
                return dev_ifname(net, (struct ifreq __user *)arg);
 
+       /*
+        * Take care of Wireless Extensions. Unfortunately struct iwreq
+        * isn't a proper subset of struct ifreq (it's 8 byte shorter)
+        * so we need to treat it specially, otherwise applications may
+        * fault if the struct they're passing happens to land at the
+        * end of a mapped page.
+        */
+       if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+               struct iwreq iwr;
+
+               if (copy_from_user(&iwr, arg, sizeof(iwr)))
+                       return -EFAULT;
+
+               return wext_handle_ioctl(net, &iwr, cmd, arg);
+       }
+
        if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
                return -EFAULT;
 
@@ -559,9 +575,6 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
                                ret = -EFAULT;
                        return ret;
                }
-               /* Take care of Wireless Extensions */
-               if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
-                       return wext_handle_ioctl(net, &ifr, cmd, arg);
                return -ENOTTY;
        }
 }
index 12949c8d3e5f9bd8c7bce05ba2a50ab2c2e9de4e..6cdb054484d66d40e4523965457a619bd1c9154f 100644 (file)
@@ -1035,18 +1035,18 @@ static int ioctl_standard_call(struct net_device *      dev,
 }
 
 
-int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
+int wext_handle_ioctl(struct net *net, struct iwreq *iwr, unsigned int cmd,
                      void __user *arg)
 {
        struct iw_request_info info = { .cmd = cmd, .flags = 0 };
        int ret;
 
-       ret = wext_ioctl_dispatch(net, (void *)ifr, cmd, &info,
+       ret = wext_ioctl_dispatch(net, iwr, cmd, &info,
                                  ioctl_standard_call,
                                  ioctl_private_call);
        if (ret >= 0 &&
            IW_IS_GET(cmd) &&
-           copy_to_user(arg, ifr, sizeof(struct iwreq)))
+           copy_to_user(arg, iwr, sizeof(struct iwreq)))
                return -EFAULT;
 
        return ret;