Commit df6d02300f7c2fbd0fbe626d819c8e5237d72c62

Authored by Johannes Berg
Committed by John W. Linville
1 parent 7acc7c683a

wext: fix potential private ioctl memory content leak

When a driver doesn't fill the entire buffer, old
heap contents may remain, and if it also doesn't
update the length properly, this old heap content
will be copied back to userspace.

It is very unlikely that this happens in any of
the drivers using private ioctls since it would
show up as junk being reported by iwpriv, but it
seems better to be safe here, so use kzalloc.

Reported-by: Jeff Mahoney <jeffm@suse.com>
Cc: stable@kernel.org
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

Showing 1 changed file with 1 additions and 1 deletions Inline Diff

net/wireless/wext-priv.c
1 /* 1 /*
2 * This file implement the Wireless Extensions priv API. 2 * This file implement the Wireless Extensions priv API.
3 * 3 *
4 * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> 4 * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
5 * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. 5 * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
6 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 6 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
7 * 7 *
8 * (As all part of the Linux kernel, this file is GPL) 8 * (As all part of the Linux kernel, this file is GPL)
9 */ 9 */
10 #include <linux/slab.h> 10 #include <linux/slab.h>
11 #include <linux/wireless.h> 11 #include <linux/wireless.h>
12 #include <linux/netdevice.h> 12 #include <linux/netdevice.h>
13 #include <net/iw_handler.h> 13 #include <net/iw_handler.h>
14 #include <net/wext.h> 14 #include <net/wext.h>
15 15
16 int iw_handler_get_private(struct net_device * dev, 16 int iw_handler_get_private(struct net_device * dev,
17 struct iw_request_info * info, 17 struct iw_request_info * info,
18 union iwreq_data * wrqu, 18 union iwreq_data * wrqu,
19 char * extra) 19 char * extra)
20 { 20 {
21 /* Check if the driver has something to export */ 21 /* Check if the driver has something to export */
22 if ((dev->wireless_handlers->num_private_args == 0) || 22 if ((dev->wireless_handlers->num_private_args == 0) ||
23 (dev->wireless_handlers->private_args == NULL)) 23 (dev->wireless_handlers->private_args == NULL))
24 return -EOPNOTSUPP; 24 return -EOPNOTSUPP;
25 25
26 /* Check if there is enough buffer up there */ 26 /* Check if there is enough buffer up there */
27 if (wrqu->data.length < dev->wireless_handlers->num_private_args) { 27 if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
28 /* User space can't know in advance how large the buffer 28 /* User space can't know in advance how large the buffer
29 * needs to be. Give it a hint, so that we can support 29 * needs to be. Give it a hint, so that we can support
30 * any size buffer we want somewhat efficiently... */ 30 * any size buffer we want somewhat efficiently... */
31 wrqu->data.length = dev->wireless_handlers->num_private_args; 31 wrqu->data.length = dev->wireless_handlers->num_private_args;
32 return -E2BIG; 32 return -E2BIG;
33 } 33 }
34 34
35 /* Set the number of available ioctls. */ 35 /* Set the number of available ioctls. */
36 wrqu->data.length = dev->wireless_handlers->num_private_args; 36 wrqu->data.length = dev->wireless_handlers->num_private_args;
37 37
38 /* Copy structure to the user buffer. */ 38 /* Copy structure to the user buffer. */
39 memcpy(extra, dev->wireless_handlers->private_args, 39 memcpy(extra, dev->wireless_handlers->private_args,
40 sizeof(struct iw_priv_args) * wrqu->data.length); 40 sizeof(struct iw_priv_args) * wrqu->data.length);
41 41
42 return 0; 42 return 0;
43 } 43 }
44 44
45 /* Size (in bytes) of the various private data types */ 45 /* Size (in bytes) of the various private data types */
46 static const char iw_priv_type_size[] = { 46 static const char iw_priv_type_size[] = {
47 0, /* IW_PRIV_TYPE_NONE */ 47 0, /* IW_PRIV_TYPE_NONE */
48 1, /* IW_PRIV_TYPE_BYTE */ 48 1, /* IW_PRIV_TYPE_BYTE */
49 1, /* IW_PRIV_TYPE_CHAR */ 49 1, /* IW_PRIV_TYPE_CHAR */
50 0, /* Not defined */ 50 0, /* Not defined */
51 sizeof(__u32), /* IW_PRIV_TYPE_INT */ 51 sizeof(__u32), /* IW_PRIV_TYPE_INT */
52 sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ 52 sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
53 sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ 53 sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
54 0, /* Not defined */ 54 0, /* Not defined */
55 }; 55 };
56 56
57 static int get_priv_size(__u16 args) 57 static int get_priv_size(__u16 args)
58 { 58 {
59 int num = args & IW_PRIV_SIZE_MASK; 59 int num = args & IW_PRIV_SIZE_MASK;
60 int type = (args & IW_PRIV_TYPE_MASK) >> 12; 60 int type = (args & IW_PRIV_TYPE_MASK) >> 12;
61 61
62 return num * iw_priv_type_size[type]; 62 return num * iw_priv_type_size[type];
63 } 63 }
64 64
65 static int adjust_priv_size(__u16 args, struct iw_point *iwp) 65 static int adjust_priv_size(__u16 args, struct iw_point *iwp)
66 { 66 {
67 int num = iwp->length; 67 int num = iwp->length;
68 int max = args & IW_PRIV_SIZE_MASK; 68 int max = args & IW_PRIV_SIZE_MASK;
69 int type = (args & IW_PRIV_TYPE_MASK) >> 12; 69 int type = (args & IW_PRIV_TYPE_MASK) >> 12;
70 70
71 /* Make sure the driver doesn't goof up */ 71 /* Make sure the driver doesn't goof up */
72 if (max < num) 72 if (max < num)
73 num = max; 73 num = max;
74 74
75 return num * iw_priv_type_size[type]; 75 return num * iw_priv_type_size[type];
76 } 76 }
77 77
78 /* 78 /*
79 * Wrapper to call a private Wireless Extension handler. 79 * Wrapper to call a private Wireless Extension handler.
80 * We do various checks and also take care of moving data between 80 * We do various checks and also take care of moving data between
81 * user space and kernel space. 81 * user space and kernel space.
82 * It's not as nice and slimline as the standard wrapper. The cause 82 * It's not as nice and slimline as the standard wrapper. The cause
83 * is struct iw_priv_args, which was not really designed for the 83 * is struct iw_priv_args, which was not really designed for the
84 * job we are going here. 84 * job we are going here.
85 * 85 *
86 * IMPORTANT : This function prevent to set and get data on the same 86 * IMPORTANT : This function prevent to set and get data on the same
87 * IOCTL and enforce the SET/GET convention. Not doing it would be 87 * IOCTL and enforce the SET/GET convention. Not doing it would be
88 * far too hairy... 88 * far too hairy...
89 * If you need to set and get data at the same time, please don't use 89 * If you need to set and get data at the same time, please don't use
90 * a iw_handler but process it in your ioctl handler (i.e. use the 90 * a iw_handler but process it in your ioctl handler (i.e. use the
91 * old driver API). 91 * old driver API).
92 */ 92 */
93 static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, 93 static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
94 const struct iw_priv_args **descrp) 94 const struct iw_priv_args **descrp)
95 { 95 {
96 const struct iw_priv_args *descr; 96 const struct iw_priv_args *descr;
97 int i, extra_size; 97 int i, extra_size;
98 98
99 descr = NULL; 99 descr = NULL;
100 for (i = 0; i < dev->wireless_handlers->num_private_args; i++) { 100 for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
101 if (cmd == dev->wireless_handlers->private_args[i].cmd) { 101 if (cmd == dev->wireless_handlers->private_args[i].cmd) {
102 descr = &dev->wireless_handlers->private_args[i]; 102 descr = &dev->wireless_handlers->private_args[i];
103 break; 103 break;
104 } 104 }
105 } 105 }
106 106
107 extra_size = 0; 107 extra_size = 0;
108 if (descr) { 108 if (descr) {
109 if (IW_IS_SET(cmd)) { 109 if (IW_IS_SET(cmd)) {
110 int offset = 0; /* For sub-ioctls */ 110 int offset = 0; /* For sub-ioctls */
111 /* Check for sub-ioctl handler */ 111 /* Check for sub-ioctl handler */
112 if (descr->name[0] == '\0') 112 if (descr->name[0] == '\0')
113 /* Reserve one int for sub-ioctl index */ 113 /* Reserve one int for sub-ioctl index */
114 offset = sizeof(__u32); 114 offset = sizeof(__u32);
115 115
116 /* Size of set arguments */ 116 /* Size of set arguments */
117 extra_size = get_priv_size(descr->set_args); 117 extra_size = get_priv_size(descr->set_args);
118 118
119 /* Does it fits in iwr ? */ 119 /* Does it fits in iwr ? */
120 if ((descr->set_args & IW_PRIV_SIZE_FIXED) && 120 if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
121 ((extra_size + offset) <= IFNAMSIZ)) 121 ((extra_size + offset) <= IFNAMSIZ))
122 extra_size = 0; 122 extra_size = 0;
123 } else { 123 } else {
124 /* Size of get arguments */ 124 /* Size of get arguments */
125 extra_size = get_priv_size(descr->get_args); 125 extra_size = get_priv_size(descr->get_args);
126 126
127 /* Does it fits in iwr ? */ 127 /* Does it fits in iwr ? */
128 if ((descr->get_args & IW_PRIV_SIZE_FIXED) && 128 if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
129 (extra_size <= IFNAMSIZ)) 129 (extra_size <= IFNAMSIZ))
130 extra_size = 0; 130 extra_size = 0;
131 } 131 }
132 } 132 }
133 *descrp = descr; 133 *descrp = descr;
134 return extra_size; 134 return extra_size;
135 } 135 }
136 136
137 static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, 137 static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
138 const struct iw_priv_args *descr, 138 const struct iw_priv_args *descr,
139 iw_handler handler, struct net_device *dev, 139 iw_handler handler, struct net_device *dev,
140 struct iw_request_info *info, int extra_size) 140 struct iw_request_info *info, int extra_size)
141 { 141 {
142 char *extra; 142 char *extra;
143 int err; 143 int err;
144 144
145 /* Check what user space is giving us */ 145 /* Check what user space is giving us */
146 if (IW_IS_SET(cmd)) { 146 if (IW_IS_SET(cmd)) {
147 if (!iwp->pointer && iwp->length != 0) 147 if (!iwp->pointer && iwp->length != 0)
148 return -EFAULT; 148 return -EFAULT;
149 149
150 if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) 150 if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
151 return -E2BIG; 151 return -E2BIG;
152 } else if (!iwp->pointer) 152 } else if (!iwp->pointer)
153 return -EFAULT; 153 return -EFAULT;
154 154
155 extra = kmalloc(extra_size, GFP_KERNEL); 155 extra = kzalloc(extra_size, GFP_KERNEL);
156 if (!extra) 156 if (!extra)
157 return -ENOMEM; 157 return -ENOMEM;
158 158
159 /* If it is a SET, get all the extra data in here */ 159 /* If it is a SET, get all the extra data in here */
160 if (IW_IS_SET(cmd) && (iwp->length != 0)) { 160 if (IW_IS_SET(cmd) && (iwp->length != 0)) {
161 if (copy_from_user(extra, iwp->pointer, extra_size)) { 161 if (copy_from_user(extra, iwp->pointer, extra_size)) {
162 err = -EFAULT; 162 err = -EFAULT;
163 goto out; 163 goto out;
164 } 164 }
165 } 165 }
166 166
167 /* Call the handler */ 167 /* Call the handler */
168 err = handler(dev, info, (union iwreq_data *) iwp, extra); 168 err = handler(dev, info, (union iwreq_data *) iwp, extra);
169 169
170 /* If we have something to return to the user */ 170 /* If we have something to return to the user */
171 if (!err && IW_IS_GET(cmd)) { 171 if (!err && IW_IS_GET(cmd)) {
172 /* Adjust for the actual length if it's variable, 172 /* Adjust for the actual length if it's variable,
173 * avoid leaking kernel bits outside. 173 * avoid leaking kernel bits outside.
174 */ 174 */
175 if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) 175 if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
176 extra_size = adjust_priv_size(descr->get_args, iwp); 176 extra_size = adjust_priv_size(descr->get_args, iwp);
177 177
178 if (copy_to_user(iwp->pointer, extra, extra_size)) 178 if (copy_to_user(iwp->pointer, extra, extra_size))
179 err = -EFAULT; 179 err = -EFAULT;
180 } 180 }
181 181
182 out: 182 out:
183 kfree(extra); 183 kfree(extra);
184 return err; 184 return err;
185 } 185 }
186 186
187 int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, 187 int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
188 unsigned int cmd, struct iw_request_info *info, 188 unsigned int cmd, struct iw_request_info *info,
189 iw_handler handler) 189 iw_handler handler)
190 { 190 {
191 int extra_size = 0, ret = -EINVAL; 191 int extra_size = 0, ret = -EINVAL;
192 const struct iw_priv_args *descr; 192 const struct iw_priv_args *descr;
193 193
194 extra_size = get_priv_descr_and_size(dev, cmd, &descr); 194 extra_size = get_priv_descr_and_size(dev, cmd, &descr);
195 195
196 /* Check if we have a pointer to user space data or not. */ 196 /* Check if we have a pointer to user space data or not. */
197 if (extra_size == 0) { 197 if (extra_size == 0) {
198 /* No extra arguments. Trivial to handle */ 198 /* No extra arguments. Trivial to handle */
199 ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); 199 ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
200 } else { 200 } else {
201 ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, 201 ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
202 handler, dev, info, extra_size); 202 handler, dev, info, extra_size);
203 } 203 }
204 204
205 /* Call commit handler if needed and defined */ 205 /* Call commit handler if needed and defined */
206 if (ret == -EIWCOMMIT) 206 if (ret == -EIWCOMMIT)
207 ret = call_commit_handler(dev); 207 ret = call_commit_handler(dev);
208 208
209 return ret; 209 return ret;
210 } 210 }
211 211
212 #ifdef CONFIG_COMPAT 212 #ifdef CONFIG_COMPAT
213 int compat_private_call(struct net_device *dev, struct iwreq *iwr, 213 int compat_private_call(struct net_device *dev, struct iwreq *iwr,
214 unsigned int cmd, struct iw_request_info *info, 214 unsigned int cmd, struct iw_request_info *info,
215 iw_handler handler) 215 iw_handler handler)
216 { 216 {
217 const struct iw_priv_args *descr; 217 const struct iw_priv_args *descr;
218 int ret, extra_size; 218 int ret, extra_size;
219 219
220 extra_size = get_priv_descr_and_size(dev, cmd, &descr); 220 extra_size = get_priv_descr_and_size(dev, cmd, &descr);
221 221
222 /* Check if we have a pointer to user space data or not. */ 222 /* Check if we have a pointer to user space data or not. */
223 if (extra_size == 0) { 223 if (extra_size == 0) {
224 /* No extra arguments. Trivial to handle */ 224 /* No extra arguments. Trivial to handle */
225 ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); 225 ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
226 } else { 226 } else {
227 struct compat_iw_point *iwp_compat; 227 struct compat_iw_point *iwp_compat;
228 struct iw_point iwp; 228 struct iw_point iwp;
229 229
230 iwp_compat = (struct compat_iw_point *) &iwr->u.data; 230 iwp_compat = (struct compat_iw_point *) &iwr->u.data;
231 iwp.pointer = compat_ptr(iwp_compat->pointer); 231 iwp.pointer = compat_ptr(iwp_compat->pointer);
232 iwp.length = iwp_compat->length; 232 iwp.length = iwp_compat->length;
233 iwp.flags = iwp_compat->flags; 233 iwp.flags = iwp_compat->flags;
234 234
235 ret = ioctl_private_iw_point(&iwp, cmd, descr, 235 ret = ioctl_private_iw_point(&iwp, cmd, descr,
236 handler, dev, info, extra_size); 236 handler, dev, info, extra_size);
237 237
238 iwp_compat->pointer = ptr_to_compat(iwp.pointer); 238 iwp_compat->pointer = ptr_to_compat(iwp.pointer);
239 iwp_compat->length = iwp.length; 239 iwp_compat->length = iwp.length;
240 iwp_compat->flags = iwp.flags; 240 iwp_compat->flags = iwp.flags;
241 } 241 }
242 242
243 /* Call commit handler if needed and defined */ 243 /* Call commit handler if needed and defined */
244 if (ret == -EIWCOMMIT) 244 if (ret == -EIWCOMMIT)
245 ret = call_commit_handler(dev); 245 ret = call_commit_handler(dev);
246 246
247 return ret; 247 return ret;
248 } 248 }
249 #endif 249 #endif
250 250