Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c2805fbb authored by Jean Tourrilhes's avatar Jean Tourrilhes Committed by John W. Linville
Browse files

[PATCH] WE-22 : prevent information leak on 64 bit



 	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: default avatarJean Tourrilhes <jt@hpl.hp.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent ed4bb106
Loading
Loading
Loading
Loading
+18 −3
Original line number Diff line number Diff line
/*
 * 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
@@ -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 :
@@ -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 ****************************/
@@ -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 */
+21 −9
Original line number Diff line number Diff line
/*
 * 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
@@ -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 :
@@ -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 ****************************/
@@ -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;
@@ -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;
	}
@@ -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;
@@ -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
+2 −1
Original line number Diff line number Diff line
@@ -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 */

+50 −32
Original line number Diff line number Diff line
@@ -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)
 */
@@ -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 *****************************/
@@ -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
@@ -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);
@@ -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... */
@@ -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);
@@ -1254,15 +1272,15 @@ 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);
@@ -1270,11 +1288,11 @@ static int rtnetlink_standard_get(struct net_device * dev,
		/* 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);
@@ -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
@@ -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;

@@ -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 */
@@ -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);
@@ -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",
@@ -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);
@@ -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);
@@ -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) {
@@ -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 &
@@ -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;
@@ -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;