Skip to content

Commit

Permalink
[PATCH] WE-22 : prevent information leak on 64 bit
Browse files Browse the repository at this point in the history
 	Johannes Berg discovered that kernel space was leaking to
userspace on 64 bit platform. He made a first patch to fix that. This
is an improved version of his patch.

Signed-off-by: Jean Tourrilhes <jt@hpl.hp.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Jean Tourrilhes authored and John W. Linville committed Mar 27, 2007
1 parent ed4bb10 commit c2805fb
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 45 deletions.
21 changes: 18 additions & 3 deletions include/linux/wireless.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* This file define a set of standard wireless extensions
*
* Version : 21 14.3.06
* Version : 22 16.3.07
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
*/

#ifndef _LINUX_WIRELESS_H
Expand Down Expand Up @@ -85,7 +85,7 @@
* (there is some stuff that will be added in the future...)
* I just plan to increment with each new version.
*/
#define WIRELESS_EXT 21
#define WIRELESS_EXT 22

/*
* Changes :
Expand Down Expand Up @@ -221,6 +221,10 @@
* - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers
* - Power/Retry relative values no longer * 100000
* - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI
*
* V21 to V22
* ----------
* - Prevent leaking of kernel space in stream on 64 bits.
*/

/**************************** CONSTANTS ****************************/
Expand Down Expand Up @@ -1085,4 +1089,15 @@ struct iw_event
#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \
IW_EV_POINT_OFF)

/* Size of the Event prefix when packed in stream */
#define IW_EV_LCP_PK_LEN (4)
/* Size of the various events when packed in stream */
#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ)
#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32))
#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq))
#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param))
#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr))
#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality))
#define IW_EV_POINT_PK_LEN (IW_EV_LCP_LEN + 4)

#endif /* _LINUX_WIRELESS_H */
30 changes: 21 additions & 9 deletions include/net/iw_handler.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* This file define the new driver API for Wireless Extensions
*
* Version : 7 18.3.05
* Version : 8 16.3.07
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 2001-2006 Jean Tourrilhes, All Rights Reserved.
* Copyright (c) 2001-2007 Jean Tourrilhes, All Rights Reserved.
*/

#ifndef _IW_HANDLER_H
Expand Down Expand Up @@ -207,7 +207,7 @@
* will be needed...
* I just plan to increment with each new version.
*/
#define IW_HANDLER_VERSION 7
#define IW_HANDLER_VERSION 8

/*
* Changes :
Expand Down Expand Up @@ -239,6 +239,10 @@
* - Remove (struct iw_point *)->pointer from events and streams
* - Remove spy_offset from struct iw_handler_def
* - Add "check" version of event macros for ieee802.11 stack
*
* V7 to V8
* ----------
* - Prevent leaking of kernel space in stream on 64 bits.
*/

/**************************** CONSTANTS ****************************/
Expand Down Expand Up @@ -500,7 +504,11 @@ iwe_stream_add_event(char * stream, /* Stream of events */
/* Check if it's possible */
if(likely((stream + event_len) < ends)) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, event_len);
/* Beware of alignement issues on 64 bits */
memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
memcpy(stream + IW_EV_LCP_LEN,
((char *) iwe) + IW_EV_LCP_LEN,
event_len - IW_EV_LCP_LEN);
stream += event_len;
}
return stream;
Expand All @@ -521,10 +529,10 @@ iwe_stream_add_point(char * stream, /* Stream of events */
/* Check if it's possible */
if(likely((stream + event_len) < ends)) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, IW_EV_LCP_LEN);
memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
memcpy(stream + IW_EV_LCP_LEN,
((char *) iwe) + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
IW_EV_POINT_LEN - IW_EV_LCP_LEN);
IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length);
stream += event_len;
}
Expand Down Expand Up @@ -574,7 +582,11 @@ iwe_stream_check_add_event(char * stream, /* Stream of events */
/* Check if it's possible, set error if not */
if(likely((stream + event_len) < ends)) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, event_len);
/* Beware of alignement issues on 64 bits */
memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
memcpy(stream + IW_EV_LCP_LEN,
((char *) iwe) + IW_EV_LCP_LEN,
event_len - IW_EV_LCP_LEN);
stream += event_len;
} else
*perr = -E2BIG;
Expand All @@ -598,10 +610,10 @@ iwe_stream_check_add_point(char * stream, /* Stream of events */
/* Check if it's possible */
if(likely((stream + event_len) < ends)) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, IW_EV_LCP_LEN);
memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
memcpy(stream + IW_EV_LCP_LEN,
((char *) iwe) + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
IW_EV_POINT_LEN - IW_EV_LCP_LEN);
IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length);
stream += event_len;
} else
Expand Down
3 changes: 2 additions & 1 deletion net/core/rtnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,8 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
if (err < 0)
goto errout;

iw += IW_EV_POINT_OFF;
/* Payload is at an offset in buffer */
iw = iw_buf + IW_EV_POINT_OFF;
}
#endif /* CONFIG_NET_WIRELESS_RTNETLINK */

Expand Down
82 changes: 50 additions & 32 deletions net/core/wireless.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* This file implement the Wireless Extensions APIs.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
*
* (As all part of the Linux kernel, this file is GPL)
*/
Expand Down Expand Up @@ -76,6 +76,9 @@
* o Change length in ESSID and NICK to strlen() instead of strlen()+1
* o Make standard_ioctl_num and standard_event_num unsigned
* o Remove (struct net_device *)->get_wireless_stats()
*
* v10 - 16.3.07 - Jean II
* o Prevent leaking of kernel space in stream on 64 bits.
*/

/***************************** INCLUDES *****************************/
Expand Down Expand Up @@ -427,6 +430,21 @@ static const int event_type_size[] = {
IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
};

/* Size (in bytes) of various events, as packed */
static const int event_type_pk_size[] = {
IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */
0,
IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */
0,
IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */
IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */
IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */
0,
IW_EV_POINT_PK_LEN, /* Without variable payload */
IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */
IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */
};

/************************ COMMON SUBROUTINES ************************/
/*
* Stuff that may be used in various place or doesn't fit in one
Expand Down Expand Up @@ -1217,7 +1235,7 @@ static int rtnetlink_standard_get(struct net_device * dev,
memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
/* Use our own copy of wrqu */
wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+ IW_EV_LCP_LEN);
+ IW_EV_LCP_PK_LEN);

/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, NULL);
Expand All @@ -1229,8 +1247,8 @@ static int rtnetlink_standard_get(struct net_device * dev,

/* Get a temp copy of wrqu (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
((char *) request) + IW_EV_LCP_LEN,
IW_EV_POINT_LEN - IW_EV_LCP_LEN);
((char *) request) + IW_EV_LCP_PK_LEN,
IW_EV_POINT_LEN - IW_EV_LCP_PK_LEN);

/* Calculate space needed by arguments. Always allocate
* for max space. Easier, and won't last long... */
Expand All @@ -1240,7 +1258,7 @@ static int rtnetlink_standard_get(struct net_device * dev,
(wrqu_point.data.length > descr->max_tokens))
extra_size = (wrqu_point.data.length
* descr->token_size);
buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF;
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
dev->name, extra_size, buffer_size);
Expand All @@ -1254,27 +1272,27 @@ static int rtnetlink_standard_get(struct net_device * dev,

/* Put wrqu in the right place (just before extra).
* Leave space for IWE header and dummy pointer...
* Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
* Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned.
*/
memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF,
((char *) &wrqu_point) + IW_EV_POINT_OFF,
IW_EV_POINT_LEN - IW_EV_LCP_LEN);
wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN);

/* Extra comes logically after that. Offset +12 bytes. */
extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN;

/* Call the handler */
ret = handler(dev, &info, wrqu, extra);

/* Calculate real returned length */
extra_size = (wrqu->data.length * descr->token_size);
/* Re-adjust reply size */
request->len = extra_size + IW_EV_POINT_LEN;
request->len = extra_size + IW_EV_POINT_PK_LEN;

/* Put the iwe header where it should, i.e. scrap the
* dummy pointer. */
memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN);

#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
Expand Down Expand Up @@ -1331,10 +1349,10 @@ static inline int rtnetlink_standard_set(struct net_device * dev,
#endif /* WE_RTNETLINK_DEBUG */

/* Extract fixed header from request. This is properly aligned. */
wrqu = &request->u;
wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN);

/* Check if wrqu is complete */
hdr_len = event_type_size[descr->header_type];
hdr_len = event_type_pk_size[descr->header_type];
if(request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
Expand All @@ -1359,7 +1377,7 @@ static inline int rtnetlink_standard_set(struct net_device * dev,

/* Put wrqu in the right place (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
/* Don't forget about the event code... */
wrqu = &wrqu_point;

Expand Down Expand Up @@ -1483,7 +1501,7 @@ static inline int rtnetlink_private_get(struct net_device * dev,
hdr_len = extra_size;
extra_size = 0;
} else {
hdr_len = IW_EV_POINT_LEN;
hdr_len = IW_EV_POINT_PK_LEN;
}

/* Check if wrqu is complete */
Expand Down Expand Up @@ -1514,7 +1532,7 @@ static inline int rtnetlink_private_get(struct net_device * dev,
memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
/* Use our own copy of wrqu */
wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+ IW_EV_LCP_LEN);
+ IW_EV_LCP_PK_LEN);

/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, (char *) wrqu);
Expand All @@ -1523,7 +1541,7 @@ static inline int rtnetlink_private_get(struct net_device * dev,
char * extra;

/* Buffer for full reply */
buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF;

#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
Expand All @@ -1538,15 +1556,15 @@ static inline int rtnetlink_private_get(struct net_device * dev,

/* Put wrqu in the right place (just before extra).
* Leave space for IWE header and dummy pointer...
* Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
* Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned.
*/
memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
((char *) request) + IW_EV_LCP_LEN,
IW_EV_POINT_LEN - IW_EV_LCP_LEN);
wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF,
((char *) request) + IW_EV_LCP_PK_LEN,
IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN);

/* Extra comes logically after that. Offset +12 bytes. */
extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN;

/* Call the handler */
ret = handler(dev, &info, wrqu, extra);
Expand All @@ -1556,11 +1574,11 @@ static inline int rtnetlink_private_get(struct net_device * dev,
if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
extra_size = adjust_priv_size(descr->get_args, wrqu);
/* Re-adjust reply size */
request->len = extra_size + IW_EV_POINT_LEN;
request->len = extra_size + IW_EV_POINT_PK_LEN;

/* Put the iwe header where it should, i.e. scrap the
* dummy pointer. */
memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN);

#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
Expand Down Expand Up @@ -1641,14 +1659,14 @@ static inline int rtnetlink_private_set(struct net_device * dev,
/* Does it fits in wrqu ? */
if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
(extra_size <= IFNAMSIZ)) {
hdr_len = IW_EV_LCP_LEN + extra_size;
hdr_len = IW_EV_LCP_PK_LEN + extra_size;
extra_size = 0;
} else {
hdr_len = IW_EV_POINT_LEN;
hdr_len = IW_EV_POINT_PK_LEN;
}

/* Extract fixed header from request. This is properly aligned. */
wrqu = &request->u;
wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN);

/* Check if wrqu is complete */
if(request_len < hdr_len) {
Expand All @@ -1675,7 +1693,7 @@ static inline int rtnetlink_private_set(struct net_device * dev,

/* Put wrqu in the right place (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);

/* Does it fits within bounds ? */
if(wrqu_point.data.length > (descr->set_args &
Expand Down Expand Up @@ -1738,7 +1756,7 @@ int wireless_rtnetlink_get(struct net_device * dev,
iw_handler handler;

/* Check length */
if(len < IW_EV_LCP_LEN) {
if(len < IW_EV_LCP_PK_LEN) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
dev->name, len);
return -EINVAL;
Expand Down Expand Up @@ -1822,7 +1840,7 @@ int wireless_rtnetlink_set(struct net_device * dev,
iw_handler handler;

/* Check length */
if(len < IW_EV_LCP_LEN) {
if(len < IW_EV_LCP_PK_LEN) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
dev->name, len);
return -EINVAL;
Expand Down

0 comments on commit c2805fb

Please sign in to comment.