diff options
| author | Jeff Johnson <jjohnson@qca.qualcomm.com> | 2014-03-17 09:48:20 -0700 |
|---|---|---|
| committer | Akash Patel <c_akashp@qca.qualcomm.com> | 2014-03-27 09:22:13 -0700 |
| commit | c37d171e07b9ba2c3eb8bb346227eadbac8bbbf4 (patch) | |
| tree | fc068950c4098ba0e597b8e396baef32c5add7eb | |
| parent | 321f24b5f2698014094dd7869767f97630eab4e5 (diff) | |
wlan: qcacld: Add "compat" support for device ioctls
The ioctls used for Android "DRIVER" commands are not safe when used
in a 32U/64K environment. Add "compat" support so that when the
driver is part of a 64-bit kernel, the ioctls will work with 32-bit
userspace applications.
Change-Id: I8d4d9d31a5e6ab2f89f84a4e6056acb95343cf73
CRs-fixed: 622765
| -rw-r--r-- | CORE/HDD/src/wlan_hdd_hostapd.c | 245 | ||||
| -rw-r--r-- | CORE/HDD/src/wlan_hdd_main.c | 182 |
2 files changed, 300 insertions, 127 deletions
diff --git a/CORE/HDD/src/wlan_hdd_hostapd.c b/CORE/HDD/src/wlan_hdd_hostapd.c index 255041a86b9b..24ba24e632aa 100644 --- a/CORE/HDD/src/wlan_hdd_hostapd.c +++ b/CORE/HDD/src/wlan_hdd_hostapd.c @@ -56,6 +56,7 @@ #include <linux/init.h> #include <linux/wireless.h> #include <linux/semaphore.h> +#include <linux/compat.h> #include <vos_api.h> #include <vos_sched.h> #include <linux/etherdevice.h> @@ -207,100 +208,186 @@ int hdd_hostapd_change_mtu(struct net_device *dev, int new_mtu) return 0; } -int hdd_hostapd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +static int hdd_hostapd_driver_command(hdd_adapter_t *pAdapter, + hdd_priv_data_t *priv_data) { - hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); - hdd_priv_data_t priv_data; - tANI_U8 *command = NULL; - int ret = 0; - - if (NULL == pAdapter) - { - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, - "%s: HDD adapter context is Null", __func__); - ret = -ENODEV; - goto exit; - } + tANI_U8 *command = NULL; + int ret = 0; - if ((!ifr) || (!ifr->ifr_data)) - { - ret = -EINVAL; - goto exit; - } - - if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(hdd_priv_data_t))) - { - ret = -EFAULT; - goto exit; - } + /* + * Note that valid pointers are provided by caller + */ - if (priv_data.total_len <= 0 || - priv_data.total_len > HOSTAPD_IOCTL_COMMAND_STRLEN_MAX) - { - /* below we allocate one more byte for command buffer. - * To avoid addition overflow total_len should be - * smaller than INT_MAX. */ - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, - "%s: integer out of range len %d", __func__, priv_data.total_len); - ret = -EFAULT; - goto exit; - } + if (priv_data->total_len <= 0 || + priv_data->total_len > HOSTAPD_IOCTL_COMMAND_STRLEN_MAX) + { + /* below we allocate one more byte for command buffer. + * To avoid addition overflow total_len should be + * smaller than INT_MAX. */ + hddLog(VOS_TRACE_LEVEL_ERROR, "%s: integer out of range len %d", + __func__, priv_data->total_len); + ret = -EFAULT; + goto exit; + } - command = kmalloc((priv_data.total_len + 1), GFP_KERNEL); - if (!command) - { - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, - "%s: failed to allocate memory\n", __func__); - ret = -ENOMEM; - goto exit; - } + /* Allocate +1 for '\0' */ + command = kmalloc((priv_data->total_len + 1), GFP_KERNEL); + if (!command) + { + hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to allocate memory", __func__); + ret = -ENOMEM; + goto exit; + } - if (copy_from_user(command, priv_data.buf, priv_data.total_len)) - { - ret = -EFAULT; - goto exit; - } + if (copy_from_user(command, priv_data->buf, priv_data->total_len)) + { + ret = -EFAULT; + goto exit; + } - command[priv_data.total_len] = '\0'; + /* Make sure the command is NUL-terminated */ + command[priv_data->total_len] = '\0'; - if ((SIOCDEVPRIVATE + 1) == cmd) - { - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, - "***HOSTAPD*** : Received %s cmd from Wi-Fi GUI***", command); + hddLog(VOS_TRACE_LEVEL_INFO, + "***HOSTAPD*** : Received %s cmd from Wi-Fi GUI***", command); - if(strncmp(command, "P2P_SET_NOA", 11) == 0 ) - { - hdd_setP2pNoa(dev, command); - } - else if( strncmp(command, "P2P_SET_PS", 10) == 0 ) - { - hdd_setP2pOpps(dev, command); - } + if (strncmp(command, "P2P_SET_NOA", 11) == 0) + { + hdd_setP2pNoa(pAdapter->dev, command); + } + else if (strncmp(command, "P2P_SET_PS", 10) == 0) + { + hdd_setP2pOpps(pAdapter->dev, command); + } #ifdef FEATURE_WLAN_BATCH_SCAN - else if( strncmp(command, "WLS_BATCHING", 12) == 0 ) - { - ret = hdd_handle_batch_scan_ioctl(pAdapter, &priv_data, command); - } + else if (strncmp(command, "WLS_BATCHING", 12) == 0) + { + ret = hdd_handle_batch_scan_ioctl(pAdapter, priv_data, command); + } #endif + else if (strncmp(command, "SET_SAP_CHANNEL_LIST", 20) == 0) + { + /* + * command should be a string having format + * SET_SAP_CHANNEL_LIST <num channels> <channels seperated by spaces> + */ + hddLog(VOS_TRACE_LEVEL_INFO, + "%s: Received Command to Set Preferred Channels for SAP", + __func__); + + ret = sapSetPreferredChannel(command); + } - /* - command should be a string having format - SET_SAP_CHANNEL_LIST <num of channels> <the channels seperated by spaces> - */ - if(strncmp(command, "SET_SAP_CHANNEL_LIST", 20) == 0) - { - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, - " Received Command to Set Preferred Channels for SAP in %s", __func__); - - ret = sapSetPreferredChannel(command); - } - - } exit: if (command) { - kfree(command); + kfree(command); + } + return ret; +} + +#ifdef CONFIG_COMPAT +static int hdd_hostapd_driver_compat_ioctl(hdd_adapter_t *pAdapter, + struct ifreq *ifr) +{ + struct { + compat_uptr_t buf; + int used_len; + int total_len; + } compat_priv_data; + hdd_priv_data_t priv_data; + int ret = 0; + + /* + * Note that pAdapter and ifr have already been verified by caller, + * and HDD context has also been validated + */ + if (copy_from_user(&compat_priv_data, ifr->ifr_data, + sizeof(compat_priv_data))) { + ret = -EFAULT; + goto exit; + } + priv_data.buf = compat_ptr(compat_priv_data.buf); + priv_data.used_len = compat_priv_data.used_len; + priv_data.total_len = compat_priv_data.total_len; + ret = hdd_hostapd_driver_command(pAdapter, &priv_data); + exit: + return ret; +} +#else /* CONFIG_COMPAT */ +static int hdd_hostapd_driver_compat_ioctl(hdd_adapter_t *pAdapter, + struct ifreq *ifr) +{ + /* will never be invoked */ + return 0; +} +#endif /* CONFIG_COMPAT */ + +static int hdd_hostapd_driver_ioctl(hdd_adapter_t *pAdapter, struct ifreq *ifr) +{ + hdd_priv_data_t priv_data; + int ret = 0; + + /* + * Note that pAdapter and ifr have already been verified by caller, + * and HDD context has also been validated + */ + if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data))) { + ret = -EFAULT; + } else { + ret = hdd_hostapd_driver_command(pAdapter, &priv_data); + } + return ret; +} + +static int hdd_hostapd_ioctl(struct net_device *dev, + struct ifreq *ifr, int cmd) +{ + hdd_adapter_t *pAdapter; + hdd_context_t *pHddCtx; + int ret; + + pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); + if (NULL == pAdapter) { + hddLog(VOS_TRACE_LEVEL_ERROR, + "%s: HDD adapter context is Null", __func__); + ret = -ENODEV; + goto exit; + } + if (dev != pAdapter->dev) { + hddLog(VOS_TRACE_LEVEL_ERROR, + "%s: HDD adapter/dev inconsistency", __func__); + ret = -ENODEV; + goto exit; + } + + if ((!ifr) || (!ifr->ifr_data)) { + ret = -EINVAL; + goto exit; + } + + pHddCtx = WLAN_HDD_GET_CTX(pAdapter); + ret = wlan_hdd_validate_context(pHddCtx); + if (ret) { + hddLog(VOS_TRACE_LEVEL_ERROR, "%s: invalid context", __func__); + ret = -EBUSY; + goto exit; + } + + switch (cmd) { + case (SIOCDEVPRIVATE + 1): + if (is_compat_task()) + ret = hdd_hostapd_driver_compat_ioctl(pAdapter, ifr); + else + ret = hdd_hostapd_driver_ioctl(pAdapter, ifr); + break; + default: + hddLog(VOS_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d", + __func__, cmd); + ret = -EINVAL; + break; } + exit: return ret; } diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c index accb04674934..4a5a2169662c 100644 --- a/CORE/HDD/src/wlan_hdd_main.c +++ b/CORE/HDD/src/wlan_hdd_main.c @@ -89,6 +89,7 @@ int wlan_hdd_ftm_start(hdd_context_t *pAdapter); #include "sapApi.h" #include <linux/semaphore.h> #include <linux/ctype.h> +#include <linux/compat.h> #ifdef MSM_PLATFORM #include <soc/qcom/subsystem_restart.h> #endif @@ -2312,47 +2313,26 @@ int wlan_hdd_set_mc_rate(hdd_adapter_t *pAdapter, int targetRate) return 0; } -int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +static int hdd_driver_command(hdd_adapter_t *pAdapter, + hdd_priv_data_t *ppriv_data) { - hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); hdd_priv_data_t priv_data; tANI_U8 *command = NULL; int ret = 0; - if (NULL == pAdapter) - { - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, - "%s: HDD adapter context is Null", __func__); - ret = -ENODEV; - goto exit; - } - - if ((!ifr) || (!ifr->ifr_data)) - { - ret = -EINVAL; - goto exit; - } - - if ((WLAN_HDD_GET_CTX(pAdapter))->isLogpInProgress) - { - VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, - "%s:LOGP in Progress. Ignore!!!", __func__); - ret = -EBUSY; - goto exit; - } + /* + * Note that valid pointers are provided by caller + */ - if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(hdd_priv_data_t))) - { - ret = -EFAULT; - goto exit; - } + /* copy to local struct to avoid numerous changes to legacy code */ + priv_data = *ppriv_data; if (priv_data.total_len <= 0 || priv_data.total_len > WLAN_PRIV_DATA_MAX_LEN) { - VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, - "%s:invalid priv_data.total_len(%d)!!!", __func__, - priv_data.total_len); + hddLog(VOS_TRACE_LEVEL_WARN, + "%s:invalid priv_data.total_len(%d)!!!", __func__, + priv_data.total_len); ret = -EINVAL; goto exit; } @@ -2361,8 +2341,8 @@ int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) command = kmalloc(priv_data.total_len + 1, GFP_KERNEL); if (!command) { - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, - "%s: failed to allocate memory\n", __func__); + hddLog(VOS_TRACE_LEVEL_ERROR, + "%s: failed to allocate memory", __func__); ret = -ENOMEM; goto exit; } @@ -2373,27 +2353,29 @@ int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) goto exit; } - /* Making sure the command is NUL-terminated */ + /* Make sure the command is NUL-terminated */ command[priv_data.total_len] = '\0'; - if ((SIOCDEVPRIVATE + 1) == cmd) + /* at one time the following block of code was conditional. braces + * have been retained to avoid re-indenting the legacy code + */ { hdd_context_t *pHddCtx = (hdd_context_t*)pAdapter->pHddCtx; - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, - "%s: Received %s cmd from Wi-Fi GUI***", __func__, command); + hddLog(VOS_TRACE_LEVEL_INFO, + "%s: Received %s cmd from Wi-Fi GUI***", __func__, command); if (strncmp(command, "P2P_DEV_ADDR", 12) == 0 ) { if (copy_to_user(priv_data.buf, pHddCtx->p2pDeviceAddress.bytes, sizeof(tSirMacAddr))) { - VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, - "%s: failed to copy data to user buffer\n", __func__); + hddLog(VOS_TRACE_LEVEL_ERROR, + "%s: failed to copy data to user buffer", __func__); ret = -EFAULT; } } - else if(strncmp(command, "SETBAND", 7) == 0) + else if (strncmp(command, "SETBAND", 7) == 0) { tANI_U8 *ptr = command ; int ret = 0 ; @@ -2402,17 +2384,18 @@ int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* First 8 bytes will have "SETBAND " and * 9 byte will have band setting value */ - VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, - "%s: SetBandCommand Info comm %s UL %d, TL %d", __func__, command, priv_data.used_len, priv_data.total_len); + hddLog(VOS_TRACE_LEVEL_INFO, + "%s: SetBandCommand Info comm %s UL %d, TL %d", + __func__, command, priv_data.used_len, priv_data.total_len); /* Change band request received */ - ret = hdd_setBand_helper(dev, ptr); + ret = hdd_setBand_helper(pAdapter->dev, ptr); } - else if(strncmp(command, "SETWMMPS", 8) == 0) + else if (strncmp(command, "SETWMMPS", 8) == 0) { tANI_U8 *ptr = command; ret = hdd_wmmps_helper(pAdapter, ptr); } - else if ( strncasecmp(command, "COUNTRY", 7) == 0 ) + else if (strncasecmp(command, "COUNTRY", 7) == 0) { eHalStatus status; long rc; @@ -2457,7 +2440,7 @@ int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) command should be a string having format SET_SAP_CHANNEL_LIST <num of channels> <the channels seperated by spaces> */ - else if(strncmp(command, "SET_SAP_CHANNEL_LIST", 20) == 0) + else if (strncmp(command, "SET_SAP_CHANNEL_LIST", 20) == 0) { tANI_U8 *ptr = command; @@ -2466,7 +2449,7 @@ int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ret = sapSetPreferredChannel(ptr); } - else if(strncmp(command, "SETSUSPENDMODE", 14) == 0) + else if (strncmp(command, "SETSUSPENDMODE", 14) == 0) { #ifndef QCA_WIFI_2_0 int suspend = 0; @@ -3465,7 +3448,7 @@ int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } /* Check channel number is a valid channel number */ - if(VOS_STATUS_SUCCESS != + if (VOS_STATUS_SUCCESS != wlan_hdd_validate_operation_channel(pAdapter, channel)) { hddLog(VOS_TRACE_LEVEL_ERROR, @@ -4349,6 +4332,109 @@ exit: return ret; } +#ifdef CONFIG_COMPAT +static int hdd_driver_compat_ioctl(hdd_adapter_t *pAdapter, struct ifreq *ifr) +{ + struct { + compat_uptr_t buf; + int used_len; + int total_len; + } compat_priv_data; + hdd_priv_data_t priv_data; + int ret = 0; + + /* + * Note that pAdapter and ifr have already been verified by caller, + * and HDD context has also been validated + */ + if (copy_from_user(&compat_priv_data, ifr->ifr_data, + sizeof(compat_priv_data))) { + ret = -EFAULT; + goto exit; + } + priv_data.buf = compat_ptr(compat_priv_data.buf); + priv_data.used_len = compat_priv_data.used_len; + priv_data.total_len = compat_priv_data.total_len; + ret = hdd_driver_command(pAdapter, &priv_data); + exit: + return ret; +} +#else /* CONFIG_COMPAT */ +static int hdd_driver_compat_ioctl(hdd_adapter_t *pAdapter, struct ifreq *ifr) +{ + /* will never be invoked */ + return 0; +} +#endif /* CONFIG_COMPAT */ + +static int hdd_driver_ioctl(hdd_adapter_t *pAdapter, struct ifreq *ifr) +{ + hdd_priv_data_t priv_data; + int ret = 0; + + /* + * Note that pAdapter and ifr have already been verified by caller, + * and HDD context has also been validated + */ + if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data))) { + ret = -EFAULT; + } else { + ret = hdd_driver_command(pAdapter, &priv_data); + } + return ret; +} + +int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + hdd_adapter_t *pAdapter; + hdd_context_t *pHddCtx; + int ret; + + pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); + if (NULL == pAdapter) { + VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, + "%s: HDD adapter context is Null", __func__); + ret = -ENODEV; + goto exit; + } + if (dev != pAdapter->dev) { + VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, + "%s: HDD adapter/dev inconsistency", __func__); + ret = -ENODEV; + goto exit; + } + + if ((!ifr) || (!ifr->ifr_data)) { + ret = -EINVAL; + goto exit; + } + + pHddCtx = WLAN_HDD_GET_CTX(pAdapter); + ret = wlan_hdd_validate_context(pHddCtx); + if (ret) { + VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, + "%s: invalid context", __func__); + ret = -EBUSY; + goto exit; + } + + switch (cmd) { + case (SIOCDEVPRIVATE + 1): + if (is_compat_task()) + ret = hdd_driver_compat_ioctl(pAdapter, ifr); + else + ret = hdd_driver_ioctl(pAdapter, ifr); + break; + default: + hddLog(VOS_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d", + __func__, cmd); + ret = -EINVAL; + break; + } + exit: + return ret; +} + #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) /**--------------------------------------------------------------------------- |
