aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhil Blundell <philb@gnu.org>2010-11-24 11:51:47 -0800
committerWilly Tarreau <w@1wt.eu>2010-12-12 22:22:31 +0100
commit6830831bea184af5b9cad25a1adc700eb13c7aed (patch)
treee3e4c5b931baa8185993eb559cd5366abb6c5c2f
parenta7d7e94124970e0361ecad496de235fc0dd6517d (diff)
downloadlinux-2.4-6830831bea184af5b9cad25a1adc700eb13c7aed.tar.gz
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> Signed-off-by: Willy Tarreau <w@1wt.eu> (cherry picked from 2.6 commit a27e13d370415add3487949c60810e36069a23a6)
-rw-r--r--net/econet/af_econet.c39
1 files changed, 25 insertions, 14 deletions
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index f76685f87e894e..20c1523daa2f36 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -31,6 +31,7 @@
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
#include <net/sock.h>
#include <net/inet_common.h>
#include <linux/stat.h>
@@ -245,12 +246,12 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
#endif
#ifdef CONFIG_ECONET_AUNUDP
struct msghdr udpmsg;
- struct iovec iov[msg->msg_iovlen+1];
+ struct iovec iov[2];
struct aunhdr ah;
struct sockaddr_in udpdest;
__kernel_size_t size;
- int i;
mm_segment_t oldfs;
+ char *userbuf;
#endif
/*
@@ -294,6 +295,10 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
{
/* Real hardware Econet. We're not worthy etc. */
#ifdef CONFIG_ECONET_NATIVE
+
+ if (len + 15 > dev->mtu)
+ return -EMSGSIZE;
+
atomic_inc(&dev->refcnt);
skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15,
@@ -365,6 +370,9 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
if (udpsock == NULL)
return -ENETDOWN; /* No socket - can't send */
+
+ if (len > 32768)
+ return -E2BIG;
/* Make up a UDP datagram and hand it off to some higher intellect. */
@@ -398,21 +406,21 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
size = sizeof(struct aunhdr);
iov[0].iov_base = (void *)&ah;
iov[0].iov_len = size;
- for (i = 0; i < msg->msg_iovlen; i++) {
- void *base = msg->msg_iov[i].iov_base;
- size_t len = msg->msg_iov[i].iov_len;
- /* Check it now since we switch to KERNEL_DS later. */
- if ((err = verify_area(VERIFY_READ, base, len)) < 0)
- return err;
- iov[i+1].iov_base = base;
- iov[i+1].iov_len = len;
- size += len;
- }
+
+ userbuf = vmalloc(len);
+ if (userbuf == NULL)
+ return -ENOMEM;
+
+ iov[1].iov_base = userbuf;
+ iov[1].iov_len = len;
+ err = memcpy_fromiovec(userbuf, msg->msg_iov, len);
+ if (err)
+ goto error_free_buf;
/* Get a skbuff (no data, just holds our cb information) */
if ((skb = sock_alloc_send_skb(sk, 0,
msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
- return err;
+ goto error_free_buf;
eb = (struct ec_cb *)&skb->cb;
@@ -428,7 +436,7 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
udpmsg.msg_name = (void *)&udpdest;
udpmsg.msg_namelen = sizeof(udpdest);
udpmsg.msg_iov = &iov[0];
- udpmsg.msg_iovlen = msg->msg_iovlen + 1;
+ udpmsg.msg_iovlen = 2;
udpmsg.msg_control = NULL;
udpmsg.msg_controllen = 0;
udpmsg.msg_flags=0;
@@ -436,6 +444,9 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
oldfs = get_fs(); set_fs(KERNEL_DS); /* More privs :-) */
err = sock_sendmsg(udpsock, &udpmsg, size);
set_fs(oldfs);
+
+ error_free_buf:
+ vfree(userbuf);
#else
err = -EPROTOTYPE;
#endif