wlan: prima: 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: I84dd1b706bbf3946b91f282c23d966776e342f82
CRs-fixed: 622766
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index b137ab3..34c930d 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -1901,51 +1901,26 @@
#endif/*End of FEATURE_WLAN_BATCH_SCAN*/
-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;
- long ret = 0;
+ int ret = 0;
- if (NULL == pAdapter)
- {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
- "%s: pAdapter is Null", __func__);
- ret = -ENODEV;
- goto exit;
- }
+ /*
+ * Note that valid pointers are provided by caller
+ */
- if ((!ifr) || (!ifr->ifr_data))
- {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- "%s: invalid data", __func__);
- ret = -EINVAL;
- goto exit;
- }
+ /* copy to local struct to avoid numerous changes to legacy code */
+ priv_data = *ppriv_data;
- if ((WLAN_HDD_GET_CTX(pAdapter))->isLogpInProgress)
+ if (priv_data.total_len <= 0 ||
+ priv_data.total_len > WLAN_PRIV_DATA_MAX_LEN)
{
- VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
- "%s:LOGP in Progress. Ignore!!!", __func__);
- ret = -EBUSY;
- goto exit;
- }
-
- if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(hdd_priv_data_t)))
- {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- FL("failed to get data from user buffer"));
- ret = -EFAULT;
- goto exit;
- }
-
- 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;
}
@@ -1954,24 +1929,24 @@
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", __func__);
+ 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))
{
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- FL("failed to get data from user buffer"));
ret = -EFAULT;
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;
@@ -2006,10 +1981,10 @@
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);
/* Change band request received */
- ret = hdd_setBand_helper(dev, ptr);
+ ret = hdd_setBand_helper(pAdapter->dev, ptr);
if(ret != 0)
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
- "%s: failed to set band ret=%ld",__func__, ret);
+ "%s: failed to set band ret=%d", __func__, ret);
}
else if(strncmp(command, "SETWMMPS", 8) == 0)
{
@@ -2038,14 +2013,14 @@
msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
if (0 >= ret)
{
- hddLog(VOS_TRACE_LEVEL_ERROR, "%s: SME while setting country code timed out %ld",
+ hddLog(VOS_TRACE_LEVEL_ERROR, "%s: SME while setting country code timed out %d",
__func__, ret);
}
}
else
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
- "%s: SME Change Country code fail ret=%ld", __func__, ret);
+ "%s: SME Change Country code fail ret=%d", __func__, ret);
ret = -EINVAL;
}
@@ -2745,7 +2720,7 @@
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
&(pAdapter->wdev),
#else
- dev,
+ pAdapter->dev,
#endif
&chan, 0,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
@@ -3828,7 +3803,108 @@
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)
/**---------------------------------------------------------------------------