diff options
author | Manikanta <mmaddireddy@nvidia.com> | 2014-02-26 15:44:23 +0530 |
---|---|---|
committer | Venu Byravarasu <vbyravarasu@nvidia.com> | 2014-04-07 22:10:20 -0700 |
commit | 8e9e49fa9a96116f44a86cdba2e084d09827641a (patch) | |
tree | 18f07bdef044c107a5e5991bc10f4c71aa02abb4 /drivers/net/wireless | |
parent | 170259ffaad85f0fe8c51cbbdacea3b48dbfaaf1 (diff) |
net: wireless: bcmdhd: fix 64-bit safeness issues
If a structure has pointer as a field and used in copy_from_user,
it causes issues in case of 32-bit userspace and 64-bit kernel
as structure size varies in kernel and userspace. To handle this case
define new structure without a pointer and use in case of compat task.
bug 1466409
Change-Id: I1dfb511b2683d93ba66626877885a4ad0ea37470
Signed-off-by: Manikanta <mmaddireddy@nvidia.com>
Reviewed-on: http://git-master/r/374795
Reviewed-by: Nagarjuna Kristam <nkristam@nvidia.com>
Tested-by: Nagarjuna Kristam <nkristam@nvidia.com>
Reviewed-by: Venu Byravarasu <vbyravarasu@nvidia.com>
Diffstat (limited to 'drivers/net/wireless')
-rwxr-xr-x | drivers/net/wireless/bcmdhd/dhd_linux.c | 35 | ||||
-rwxr-xr-x | drivers/net/wireless/bcmdhd/include/dhdioctl.h | 12 | ||||
-rwxr-xr-x | drivers/net/wireless/bcmdhd/wl_android.c | 33 |
3 files changed, 76 insertions, 4 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 534c272cdd53..4f8dde7453c8 100755 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -43,6 +43,7 @@ #include <linux/fcntl.h> #include <linux/fs.h> #include <linux/ip.h> +#include <linux/compat.h> #include <net/addrconf.h> #include <linux/cpufreq.h> @@ -2768,6 +2769,9 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); dhd_ioctl_t ioc; +#ifdef CONFIG_COMPAT + dhd_ioctl_compat_t ioc_compat; +#endif int bcmerror = 0; int ifidx; int ret; @@ -2824,7 +2828,37 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) } memset(&ioc, 0, sizeof(ioc)); +#ifdef CONFIG_COMPAT + memset(&ioc_compat, 0, sizeof(ioc_compat)); + if (is_compat_task()) { + /* Copy the ioc control structure part of ioctl request */ + if (copy_from_user(&ioc_compat, ifr->ifr_data, sizeof(dhd_ioctl_compat_t))) { + bcmerror = BCME_BADADDR; + goto done; + } + ioc.cmd = ioc_compat.cmd; + ioc.buf = (void *)(uintptr_t) ioc_compat.buf; + ioc.len = ioc_compat.len; + ioc.set = ioc_compat.set; + ioc.used = ioc_compat.used; + ioc.needed = ioc_compat.needed; + ioc.driver = ioc_compat.driver; + } else { + /* Copy the ioc control structure part of ioctl request */ + if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) { + bcmerror = BCME_BADADDR; + goto done; + } + + /* To differentiate between wl and dhd read 4 more byes */ + if ((copy_from_user(&ioc.driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t), + sizeof(uint)) != 0)) { + bcmerror = BCME_BADADDR; + goto done; + } + } +#else /* Copy the ioc control structure part of ioctl request */ if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) { bcmerror = BCME_BADADDR; @@ -2837,6 +2871,7 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) bcmerror = BCME_BADADDR; goto done; } +#endif if (!capable(CAP_NET_ADMIN)) { bcmerror = BCME_EPERM; diff --git a/drivers/net/wireless/bcmdhd/include/dhdioctl.h b/drivers/net/wireless/bcmdhd/include/dhdioctl.h index 187fd07b0e61..efca0146b156 100755 --- a/drivers/net/wireless/bcmdhd/include/dhdioctl.h +++ b/drivers/net/wireless/bcmdhd/include/dhdioctl.h @@ -40,6 +40,18 @@ /* Linux network driver ioctl encoding */ +#ifdef CONFIG_COMPAT +typedef struct dhd_ioctl_compat { + uint cmd; /* common ioctl definition */ + u32 buf; /* pointer to user buffer */ + uint len; /* length of user buffer */ + bool set; /* get or set request (optional) */ + uint used; /* bytes read or written (optional) */ + uint needed; /* bytes needed (optional) */ + uint driver; /* to identify target driver */ +} dhd_ioctl_compat_t; +#endif + typedef struct dhd_ioctl { uint cmd; /* common ioctl definition */ void *buf; /* pointer to user buffer */ diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 14e81efa8ded..a94e6ef13622 100755 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/netdevice.h> +#include <linux/compat.h> #include <net/netlink.h> #include <wl_android.h> @@ -150,12 +151,16 @@ struct io_cfg { struct list_head list; }; +#ifdef CONFIG_COMPAT +typedef struct android_wifi_priv_cmd_compat { + u32 bufaddr; + int used_len; + int total_len; +} android_wifi_priv_cmd_compat; +#endif + typedef struct android_wifi_priv_cmd { -#ifdef CONFIG_64BIT - u64 bufaddr; -#else char *bufaddr; -#endif int used_len; int total_len; } android_wifi_priv_cmd; @@ -1278,6 +1283,9 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) char *buf = NULL; int bytes_written = 0; android_wifi_priv_cmd priv_cmd; +#ifdef CONFIG_COMPAT + android_wifi_priv_cmd_compat priv_cmd_compat; +#endif net_os_wake_lock(net); @@ -1285,10 +1293,27 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) ret = -EINVAL; goto exit; } +#ifdef CONFIG_COMPAT + if (is_compat_task()) { + if (copy_from_user(&priv_cmd_compat, ifr->ifr_data, sizeof(android_wifi_priv_cmd_compat))) { + ret = -EFAULT; + goto exit; + } + priv_cmd.bufaddr = (char *)(uintptr_t) priv_cmd_compat.bufaddr; + priv_cmd.used_len = priv_cmd_compat.used_len; + priv_cmd.total_len = priv_cmd_compat.total_len; + } else { + if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { + ret = -EFAULT; + goto exit; + } + } +#else if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { ret = -EFAULT; goto exit; } +#endif if (priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) { DHD_ERROR(("%s: too long priavte command\n", __FUNCTION__)); |