aboutsummaryrefslogtreecommitdiffstats
path: root/net/econet
diff options
context:
space:
mode:
authorPhil Blundell <philb@gnu.org>2010-11-24 14:51:47 -0500
committerDavid S. Miller <davem@davemloft.net>2010-11-24 14:51:47 -0500
commita27e13d370415add3487949c60810e36069a23a6 (patch)
tree072e0ba8e2d629c55be4ef6fa5ae318e2a351e2f /net/econet
parent16c41745c7b92a243d0874f534c1655196c64b74 (diff)
econet: fix CVE-2010-3848
Don't declare variable sized array of iovecs on the stack since this could cause stack overflow if msg->msgiovlen is large. Instead, coalesce the user-supplied data into a new buffer and use a single iovec for it. Signed-off-by: Phil Blundell <philb@gnu.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/econet')
-rw-r--r--net/econet/af_econet.c62
1 files changed, 31 insertions, 31 deletions
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index d41ba8e56c10..13992e1d2726 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -31,6 +31,7 @@
31#include <linux/skbuff.h> 31#include <linux/skbuff.h>
32#include <linux/udp.h> 32#include <linux/udp.h>
33#include <linux/slab.h> 33#include <linux/slab.h>
34#include <linux/vmalloc.h>
34#include <net/sock.h> 35#include <net/sock.h>
35#include <net/inet_common.h> 36#include <net/inet_common.h>
36#include <linux/stat.h> 37#include <linux/stat.h>
@@ -276,12 +277,12 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
276#endif 277#endif
277#ifdef CONFIG_ECONET_AUNUDP 278#ifdef CONFIG_ECONET_AUNUDP
278 struct msghdr udpmsg; 279 struct msghdr udpmsg;
279 struct iovec iov[msg->msg_iovlen+1]; 280 struct iovec iov[2];
280 struct aunhdr ah; 281 struct aunhdr ah;
281 struct sockaddr_in udpdest; 282 struct sockaddr_in udpdest;
282 __kernel_size_t size; 283 __kernel_size_t size;
283 int i;
284 mm_segment_t oldfs; 284 mm_segment_t oldfs;
285 char *userbuf;
285#endif 286#endif
286 287
287 /* 288 /*
@@ -319,17 +320,17 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
319 } 320 }
320 } 321 }
321 322
322 if (len + 15 > dev->mtu) {
323 mutex_unlock(&econet_mutex);
324 return -EMSGSIZE;
325 }
326
327 if (dev->type == ARPHRD_ECONET) { 323 if (dev->type == ARPHRD_ECONET) {
328 /* Real hardware Econet. We're not worthy etc. */ 324 /* Real hardware Econet. We're not worthy etc. */
329#ifdef CONFIG_ECONET_NATIVE 325#ifdef CONFIG_ECONET_NATIVE
330 unsigned short proto = 0; 326 unsigned short proto = 0;
331 int res; 327 int res;
332 328
329 if (len + 15 > dev->mtu) {
330 mutex_unlock(&econet_mutex);
331 return -EMSGSIZE;
332 }
333
333 dev_hold(dev); 334 dev_hold(dev);
334 335
335 skb = sock_alloc_send_skb(sk, len+LL_ALLOCATED_SPACE(dev), 336 skb = sock_alloc_send_skb(sk, len+LL_ALLOCATED_SPACE(dev),
@@ -405,6 +406,11 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
405 return -ENETDOWN; /* No socket - can't send */ 406 return -ENETDOWN; /* No socket - can't send */
406 } 407 }
407 408
409 if (len > 32768) {
410 err = -E2BIG;
411 goto error;
412 }
413
408 /* Make up a UDP datagram and hand it off to some higher intellect. */ 414 /* Make up a UDP datagram and hand it off to some higher intellect. */
409 415
410 memset(&udpdest, 0, sizeof(udpdest)); 416 memset(&udpdest, 0, sizeof(udpdest));
@@ -436,36 +442,26 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
436 442
437 /* tack our header on the front of the iovec */ 443 /* tack our header on the front of the iovec */
438 size = sizeof(struct aunhdr); 444 size = sizeof(struct aunhdr);
439 /*
440 * XXX: that is b0rken. We can't mix userland and kernel pointers
441 * in iovec, since on a lot of platforms copy_from_user() will
442 * *not* work with the kernel and userland ones at the same time,
443 * regardless of what we do with set_fs(). And we are talking about
444 * econet-over-ethernet here, so "it's only ARM anyway" doesn't
445 * apply. Any suggestions on fixing that code? -- AV
446 */
447 iov[0].iov_base = (void *)&ah; 445 iov[0].iov_base = (void *)&ah;
448 iov[0].iov_len = size; 446 iov[0].iov_len = size;
449 for (i = 0; i < msg->msg_iovlen; i++) { 447
450 void __user *base = msg->msg_iov[i].iov_base; 448 userbuf = vmalloc(len);
451 size_t iov_len = msg->msg_iov[i].iov_len; 449 if (userbuf == NULL) {
452 /* Check it now since we switch to KERNEL_DS later. */ 450 err = -ENOMEM;
453 if (!access_ok(VERIFY_READ, base, iov_len)) { 451 goto error;
454 mutex_unlock(&econet_mutex);
455 return -EFAULT;
456 }
457 iov[i+1].iov_base = base;
458 iov[i+1].iov_len = iov_len;
459 size += iov_len;
460 } 452 }
461 453
454 iov[1].iov_base = userbuf;
455 iov[1].iov_len = len;
456 err = memcpy_fromiovec(userbuf, msg->msg_iov, len);
457 if (err)
458 goto error_free_buf;
459
462 /* Get a skbuff (no data, just holds our cb information) */ 460 /* Get a skbuff (no data, just holds our cb information) */
463 if ((skb = sock_alloc_send_skb(sk, 0, 461 if ((skb = sock_alloc_send_skb(sk, 0,
464 msg->msg_flags & MSG_DONTWAIT, 462 msg->msg_flags & MSG_DONTWAIT,
465 &err)) == NULL) { 463 &err)) == NULL)
466 mutex_unlock(&econet_mutex); 464 goto error_free_buf;
467 return err;
468 }
469 465
470 eb = (struct ec_cb *)&skb->cb; 466 eb = (struct ec_cb *)&skb->cb;
471 467
@@ -481,7 +477,7 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
481 udpmsg.msg_name = (void *)&udpdest; 477 udpmsg.msg_name = (void *)&udpdest;
482 udpmsg.msg_namelen = sizeof(udpdest); 478 udpmsg.msg_namelen = sizeof(udpdest);
483 udpmsg.msg_iov = &iov[0]; 479 udpmsg.msg_iov = &iov[0];
484 udpmsg.msg_iovlen = msg->msg_iovlen + 1; 480 udpmsg.msg_iovlen = 2;
485 udpmsg.msg_control = NULL; 481 udpmsg.msg_control = NULL;
486 udpmsg.msg_controllen = 0; 482 udpmsg.msg_controllen = 0;
487 udpmsg.msg_flags=0; 483 udpmsg.msg_flags=0;
@@ -489,9 +485,13 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
489 oldfs = get_fs(); set_fs(KERNEL_DS); /* More privs :-) */ 485 oldfs = get_fs(); set_fs(KERNEL_DS); /* More privs :-) */
490 err = sock_sendmsg(udpsock, &udpmsg, size); 486 err = sock_sendmsg(udpsock, &udpmsg, size);
491 set_fs(oldfs); 487 set_fs(oldfs);
488
489error_free_buf:
490 vfree(userbuf);
492#else 491#else
493 err = -EPROTOTYPE; 492 err = -EPROTOTYPE;
494#endif 493#endif
494 error:
495 mutex_unlock(&econet_mutex); 495 mutex_unlock(&econet_mutex);
496 496
497 return err; 497 return err;