[PATCH 2.6.X] 32->64 bit conversion for wireless private ioctl
From: Jean Tourrilhes <hidden>
Date: 2004-06-24 23:43:07
Hi all, (Sorry for the spam) The Wireless Extensions use a bunch of driver specific ioctl. Jeff told me that those can't be safe when running a 32 bit userspace on a 64 bit kernel. So, I did a patch... Patch is for kernel 2.6.5. Treat this patch as a proof of concept. I don't have any 64 bit machine, so no way to compile it (without talking of actually testing it). But this is where I hope you guys will help me ;-) Waiting for your feedback, Have fun... Jean ---------------------------------------------------------------- diff -u -p linux/include/net/iw_handler.p1.h linux/include/net/iw_handler.h
--- linux/include/net/iw_handler.p1.h Thu Jun 24 16:27:53 2004
+++ linux/include/net/iw_handler.h Thu Jun 24 16:18:29 2004@@ -412,6 +412,9 @@ struct iw_public_data { * Those may be called only within the kernel. */ +/* Data needed by fs/compat_ioctl.c for 32->64 bit conversion */ +extern const char iw_priv_type_size[]; + /* First : function strictly used inside the kernel */ /* Handle /proc/net/wireless, called in net/code/dev.c */
diff -u -p linux/net/core/wireless.p1.c linux/net/core/wireless.c
--- linux/net/core/wireless.p1.c Thu Jun 24 16:15:07 2004
+++ linux/net/core/wireless.c Thu Jun 24 16:17:07 2004@@ -301,7 +301,7 @@ static const int standard_event_num = (s sizeof(struct iw_ioctl_description)); /* Size (in bytes) of the various private data types */ -static const char priv_type_size[] = { +const char iw_priv_type_size[] = { 0, /* IW_PRIV_TYPE_NONE */ 1, /* IW_PRIV_TYPE_BYTE */ 1, /* IW_PRIV_TYPE_CHAR */
@@ -415,7 +415,7 @@ static inline int get_priv_size(__u16 ar int num = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; - return num * priv_type_size[type]; + return num * iw_priv_type_size[type]; }
diff -u -p linux/fs/compat_ioctl.p1.c linux/fs/compat_ioctl.c
--- linux/fs/compat_ioctl.p1.c Thu Jun 24 16:15:18 2004
+++ linux/fs/compat_ioctl.c Thu Jun 24 16:24:34 2004@@ -67,6 +67,7 @@ #include <linux/i2c.h> #include <linux/i2c-dev.h> #include <linux/wireless.h> +#include <net/iw_handler.h> #include <net/sock.h> /* siocdevprivate_ioctl */ #include <net/bluetooth/bluetooth.h>
@@ -3070,6 +3071,79 @@ static int do_wireless_ioctl(unsigned in return sys_ioctl(fd, cmd, (unsigned long) iwr); } +static int do_wireless_private_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct iwreq *iwr, *iwr_u; + struct net_device *dev; + struct iw_priv_args *descr = NULL; + __u16 iw_args; + int offset = 0; + int extra_size = 0; + + iwr_u = (struct iwreq *) compat_ptr(arg); + iwr = compat_alloc_user_space(sizeof(*iwr)); + if (iwr == NULL) + return -ENOMEM; + + if (verify_area(VERIFY_WRITE, iwr, sizeof(*iwr))) + return -EFAULT; + + if (__copy_in_user(&iwr->ifr_ifrn.ifrn_name[0], + &iwr_u->ifr_ifrn.ifrn_name[0], + sizeof(struct iwreq))) + return -EFAULT; + + /* Wireless Private ioctls are different from regular Wireless + * ioctls, in that they are driver specific. Therefore, we have + * to ask the driver how the ioctl should be handled. + * This is a bit messy, see wireless.c for details. Jean II + */ + + //dev_load(ifr.ifr_name); + /* Lock */ + rtnl_lock(); + + /* Make sure the device exist */ + if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) + return -ENODEV; + + /* Get the description of the IOCTL */ + for(i = 0; i < dev->wireless_handlers->num_private_args; i++) + if(cmd == dev->wireless_handlers->private_args[i].cmd) { + descr = &(dev->wireless_handlers->private_args[i]); + break; + } + if(!descr) + return -EFAULT; + + /* Compute size of arguments */ + if(IW_IS_SET(cmd)) { + /* Check for sub-ioctl handler */ + if(descr->name[0] == '\0') + offset = sizeof(__u32); + iw_args = descr->set_args; + } else + iw_args = descr->get_args; + extra_size = ((iw_args & IW_PRIV_SIZE_MASK) * + iw_priv_type_size[(args & IW_PRIV_TYPE_MASK) >> 12]); + + /* Are the args passed inline ? */ + if((iw_args & IW_PRIV_SIZE_FIXED) && + ((extra_size + offset) <= IFNAMSIZ)) + extra_size = 0; + + /* Unlock */ + rtnl_unlock(); + + /* Done */ + if(extra_size) + /* Convert struct iw_point appropriately */ + return do_wireless_ioctl(fd, cmd, (unsigned long) iwr); + else + /* No conversion needed */ + return sys_ioctl(fd, cmd, (unsigned long) iwr); +} + /* Emulate old style bridge ioctls */ static int do_bridge_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) {
@@ -3261,6 +3335,40 @@ HANDLE_IOCTL(SIOCSIWNICKN, do_wireless_i HANDLE_IOCTL(SIOCGIWNICKN, do_wireless_ioctl) HANDLE_IOCTL(SIOCSIWENCODE, do_wireless_ioctl) HANDLE_IOCTL(SIOCGIWENCODE, do_wireless_ioctl) +/* wireless private */ +HANDLE_IOCTL(SIOCIWFIRSTPRIV, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 1, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 2, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 3, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 4, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 5, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 6, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 7, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 8, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 9, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 10, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 11, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 12, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 13, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 14, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 15, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 16, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 17, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 18, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 19, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 20, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 21, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 22, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 23, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 24, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 25, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 26, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 27, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 28, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 29, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 30, do_wireless_private_ioctl) +HANDLE_IOCTL(SIOCIWFIRSTPRIV + 31, do_wireless_private_ioctl) +/* bridge */ HANDLE_IOCTL(SIOCSIFBR, do_bridge_ioctl) HANDLE_IOCTL(SIOCGIFBR, do_bridge_ioctl)