wireless extensions: fix kernel heap content leak
[linux-2.6.git] / net / wireless / wext-core.c
index 58dfb95..8f5116f 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/rtnetlink.h>
+#include <linux/slab.h>
 #include <linux/wireless.h>
 #include <linux/uaccess.h>
 #include <net/cfg80211.h>
@@ -28,226 +29,226 @@ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
  * know about.
  */
 static const struct iw_ioctl_description standard_ioctl[] = {
-       [SIOCSIWCOMMIT  - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = {
                .header_type    = IW_HEADER_TYPE_NULL,
        },
-       [SIOCGIWNAME    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWNAME)] = {
                .header_type    = IW_HEADER_TYPE_CHAR,
                .flags          = IW_DESCR_FLAG_DUMP,
        },
-       [SIOCSIWNWID    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWNWID)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
                .flags          = IW_DESCR_FLAG_EVENT,
        },
-       [SIOCGIWNWID    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWNWID)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
                .flags          = IW_DESCR_FLAG_DUMP,
        },
-       [SIOCSIWFREQ    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWFREQ)] = {
                .header_type    = IW_HEADER_TYPE_FREQ,
                .flags          = IW_DESCR_FLAG_EVENT,
        },
-       [SIOCGIWFREQ    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWFREQ)] = {
                .header_type    = IW_HEADER_TYPE_FREQ,
                .flags          = IW_DESCR_FLAG_DUMP,
        },
-       [SIOCSIWMODE    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWMODE)] = {
                .header_type    = IW_HEADER_TYPE_UINT,
                .flags          = IW_DESCR_FLAG_EVENT,
        },
-       [SIOCGIWMODE    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWMODE)] = {
                .header_type    = IW_HEADER_TYPE_UINT,
                .flags          = IW_DESCR_FLAG_DUMP,
        },
-       [SIOCSIWSENS    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWSENS)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCGIWSENS    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWSENS)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCSIWRANGE   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWRANGE)] = {
                .header_type    = IW_HEADER_TYPE_NULL,
        },
-       [SIOCGIWRANGE   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWRANGE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = sizeof(struct iw_range),
                .flags          = IW_DESCR_FLAG_DUMP,
        },
-       [SIOCSIWPRIV    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWPRIV)] = {
                .header_type    = IW_HEADER_TYPE_NULL,
        },
-       [SIOCGIWPRIV    - SIOCIWFIRST] = { /* (handled directly by us) */
+       [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = sizeof(struct iw_priv_args),
                .max_tokens     = 16,
                .flags          = IW_DESCR_FLAG_NOMAX,
        },
-       [SIOCSIWSTATS   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWSTATS)] = {
                .header_type    = IW_HEADER_TYPE_NULL,
        },
-       [SIOCGIWSTATS   - SIOCIWFIRST] = { /* (handled directly by us) */
+       [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = sizeof(struct iw_statistics),
                .flags          = IW_DESCR_FLAG_DUMP,
        },
-       [SIOCSIWSPY     - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWSPY)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = sizeof(struct sockaddr),
                .max_tokens     = IW_MAX_SPY,
        },
-       [SIOCGIWSPY     - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWSPY)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = sizeof(struct sockaddr) +
                                  sizeof(struct iw_quality),
                .max_tokens     = IW_MAX_SPY,
        },
-       [SIOCSIWTHRSPY  - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = sizeof(struct iw_thrspy),
                .min_tokens     = 1,
                .max_tokens     = 1,
        },
-       [SIOCGIWTHRSPY  - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = sizeof(struct iw_thrspy),
                .min_tokens     = 1,
                .max_tokens     = 1,
        },
-       [SIOCSIWAP      - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWAP)] = {
                .header_type    = IW_HEADER_TYPE_ADDR,
        },
-       [SIOCGIWAP      - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWAP)] = {
                .header_type    = IW_HEADER_TYPE_ADDR,
                .flags          = IW_DESCR_FLAG_DUMP,
        },
-       [SIOCSIWMLME    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWMLME)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .min_tokens     = sizeof(struct iw_mlme),
                .max_tokens     = sizeof(struct iw_mlme),
        },
-       [SIOCGIWAPLIST  - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWAPLIST)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = sizeof(struct sockaddr) +
                                  sizeof(struct iw_quality),
                .max_tokens     = IW_MAX_AP,
                .flags          = IW_DESCR_FLAG_NOMAX,
        },
-       [SIOCSIWSCAN    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWSCAN)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .min_tokens     = 0,
                .max_tokens     = sizeof(struct iw_scan_req),
        },
-       [SIOCGIWSCAN    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWSCAN)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_SCAN_MAX_DATA,
                .flags          = IW_DESCR_FLAG_NOMAX,
        },
-       [SIOCSIWESSID   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWESSID)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_ESSID_MAX_SIZE,
                .flags          = IW_DESCR_FLAG_EVENT,
        },
-       [SIOCGIWESSID   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWESSID)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_ESSID_MAX_SIZE,
                .flags          = IW_DESCR_FLAG_DUMP,
        },
-       [SIOCSIWNICKN   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWNICKN)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_ESSID_MAX_SIZE,
        },
-       [SIOCGIWNICKN   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWNICKN)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_ESSID_MAX_SIZE,
        },
-       [SIOCSIWRATE    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWRATE)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCGIWRATE    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWRATE)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCSIWRTS     - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWRTS)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCGIWRTS     - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWRTS)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCSIWFRAG    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWFRAG)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCGIWFRAG    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWFRAG)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCSIWTXPOW   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWTXPOW)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCGIWTXPOW   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWTXPOW)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCSIWRETRY   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWRETRY)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCGIWRETRY   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWRETRY)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCSIWENCODE  - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWENCODE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_ENCODING_TOKEN_MAX,
                .flags          = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
        },
-       [SIOCGIWENCODE  - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWENCODE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_ENCODING_TOKEN_MAX,
                .flags          = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
        },
-       [SIOCSIWPOWER   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWPOWER)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCGIWPOWER   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWPOWER)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCSIWGENIE   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWGENIE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_GENERIC_IE_MAX,
        },
-       [SIOCGIWGENIE   - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWGENIE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_GENERIC_IE_MAX,
        },
-       [SIOCSIWAUTH    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWAUTH)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCGIWAUTH    - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWAUTH)] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
-       [SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .min_tokens     = sizeof(struct iw_encode_ext),
                .max_tokens     = sizeof(struct iw_encode_ext) +
                                  IW_ENCODING_TOKEN_MAX,
        },
-       [SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .min_tokens     = sizeof(struct iw_encode_ext),
                .max_tokens     = sizeof(struct iw_encode_ext) +
                                  IW_ENCODING_TOKEN_MAX,
        },
-       [SIOCSIWPMKSA - SIOCIWFIRST] = {
+       [IW_IOCTL_IDX(SIOCSIWPMKSA)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .min_tokens     = sizeof(struct iw_pmksa),
@@ -261,44 +262,44 @@ static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
  * we know about.
  */
 static const struct iw_ioctl_description standard_event[] = {
-       [IWEVTXDROP     - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVTXDROP)] = {
                .header_type    = IW_HEADER_TYPE_ADDR,
        },
-       [IWEVQUAL       - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVQUAL)] = {
                .header_type    = IW_HEADER_TYPE_QUAL,
        },
-       [IWEVCUSTOM     - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVCUSTOM)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_CUSTOM_MAX,
        },
-       [IWEVREGISTERED - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVREGISTERED)] = {
                .header_type    = IW_HEADER_TYPE_ADDR,
        },
-       [IWEVEXPIRED    - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVEXPIRED)] = {
                .header_type    = IW_HEADER_TYPE_ADDR,
        },
-       [IWEVGENIE      - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVGENIE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_GENERIC_IE_MAX,
        },
-       [IWEVMICHAELMICFAILURE  - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = sizeof(struct iw_michaelmicfailure),
        },
-       [IWEVASSOCREQIE - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVASSOCREQIE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_GENERIC_IE_MAX,
        },
-       [IWEVASSOCRESPIE        - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVASSOCRESPIE)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = IW_GENERIC_IE_MAX,
        },
-       [IWEVPMKIDCAND  - IWEVFIRST] = {
+       [IW_EVENT_IDX(IWEVPMKIDCAND)] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
                .max_tokens     = sizeof(struct iw_pmkid_cand),
@@ -449,11 +450,11 @@ void wireless_send_event(struct net_device *      dev,
 
        /* Get the description of the Event */
        if (cmd <= SIOCIWLAST) {
-               cmd_index = cmd - SIOCIWFIRST;
+               cmd_index = IW_IOCTL_IDX(cmd);
                if (cmd_index < standard_ioctl_num)
                        descr = &(standard_ioctl[cmd_index]);
        } else {
-               cmd_index = cmd - IWEVFIRST;
+               cmd_index = IW_EVENT_IDX(cmd);
                if (cmd_index < standard_event_num)
                        descr = &(standard_event[cmd_index]);
        }
@@ -662,7 +663,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
                return NULL;
 
        /* Try as a standard command */
-       index = cmd - SIOCIWFIRST;
+       index = IW_IOCTL_IDX(cmd);
        if (index < handlers->num_standard)
                return handlers->standard[index];
 
@@ -781,6 +782,22 @@ static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd,
                }
        }
 
+       if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) {
+               /*
+                * If this is a GET, but not NOMAX, it means that the extra
+                * data is not bounded by userspace, but by max_tokens. Thus
+                * set the length to max_tokens. This matches the extra data
+                * allocation.
+                * The driver should fill it with the number of tokens it
+                * provided, and it may check iwp->length rather than having
+                * knowledge of max_tokens. If the driver doesn't change the
+                * iwp->length, this ioctl just copies back max_token tokens
+                * filled with zeroes. Hopefully the driver isn't claiming
+                * them to be valid data.
+                */
+               iwp->length = descr->max_tokens;
+       }
+
        err = handler(dev, info, (union iwreq_data *) iwp, extra);
 
        iwp->length += essid_compat;
@@ -802,7 +819,8 @@ static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd,
        }
 
        /* Generate an event to notify listeners of the change */
-       if ((descr->flags & IW_DESCR_FLAG_EVENT) && err == -EIWCOMMIT) {
+       if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+           ((err == 0) || (err == -EIWCOMMIT))) {
                union iwreq_data *data = (union iwreq_data *) iwp;
 
                if (descr->flags & IW_DESCR_FLAG_RESTRICT)
@@ -953,9 +971,9 @@ static int ioctl_standard_call(struct net_device *  dev,
        int                                     ret = -EINVAL;
 
        /* Get the description of the IOCTL */
-       if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+       if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num)
                return -EOPNOTSUPP;
-       descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+       descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]);
 
        /* Check if we have a pointer to user space data or not */
        if (descr->header_type != IW_HEADER_TYPE_POINT) {
@@ -1011,7 +1029,7 @@ static int compat_standard_call(struct net_device *dev,
        struct iw_point iwp;
        int err;
 
-       descr = standard_ioctl + (cmd - SIOCIWFIRST);
+       descr = standard_ioctl + IW_IOCTL_IDX(cmd);
 
        if (descr->header_type != IW_HEADER_TYPE_POINT)
                return ioctl_standard_call(dev, iwr, cmd, info, handler);