summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Johnson <jjohnson@qca.qualcomm.com>2014-03-17 09:48:20 -0700
committerAkash Patel <c_akashp@qca.qualcomm.com>2014-03-27 09:22:13 -0700
commitc37d171e07b9ba2c3eb8bb346227eadbac8bbbf4 (patch)
treefc068950c4098ba0e597b8e396baef32c5add7eb
parent321f24b5f2698014094dd7869767f97630eab4e5 (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.c245
-rw-r--r--CORE/HDD/src/wlan_hdd_main.c182
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)
/**---------------------------------------------------------------------------