]>
Commit | Line | Data |
---|---|---|
6dcefc2a GKH |
1 | From a27e13d370415add3487949c60810e36069a23a6 Mon Sep 17 00:00:00 2001 |
2 | From: Phil Blundell <philb@gnu.org> | |
3 | Date: Wed, 24 Nov 2010 11:51:47 -0800 | |
4 | Subject: econet: fix CVE-2010-3848 | |
5 | ||
6 | From: Phil Blundell <philb@gnu.org> | |
7 | ||
8 | commit a27e13d370415add3487949c60810e36069a23a6 upstream. | |
9 | ||
10 | Don't declare variable sized array of iovecs on the stack since this | |
11 | could cause stack overflow if msg->msgiovlen is large. Instead, coalesce | |
12 | the user-supplied data into a new buffer and use a single iovec for it. | |
13 | ||
14 | Signed-off-by: Phil Blundell <philb@gnu.org> | |
15 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
16 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
17 | ||
18 | --- | |
19 | net/econet/af_econet.c | 62 ++++++++++++++++++++++++------------------------- | |
20 | 1 file changed, 31 insertions(+), 31 deletions(-) | |
21 | ||
22 | --- a/net/econet/af_econet.c | |
23 | +++ b/net/econet/af_econet.c | |
24 | @@ -31,6 +31,7 @@ | |
25 | #include <linux/skbuff.h> | |
26 | #include <linux/udp.h> | |
27 | #include <linux/slab.h> | |
28 | +#include <linux/vmalloc.h> | |
29 | #include <net/sock.h> | |
30 | #include <net/inet_common.h> | |
31 | #include <linux/stat.h> | |
32 | @@ -276,12 +277,12 @@ static int econet_sendmsg(struct kiocb * | |
33 | #endif | |
34 | #ifdef CONFIG_ECONET_AUNUDP | |
35 | struct msghdr udpmsg; | |
36 | - struct iovec iov[msg->msg_iovlen+1]; | |
37 | + struct iovec iov[2]; | |
38 | struct aunhdr ah; | |
39 | struct sockaddr_in udpdest; | |
40 | __kernel_size_t size; | |
41 | - int i; | |
42 | mm_segment_t oldfs; | |
43 | + char *userbuf; | |
44 | #endif | |
45 | ||
46 | /* | |
47 | @@ -319,17 +320,17 @@ static int econet_sendmsg(struct kiocb * | |
48 | } | |
49 | } | |
50 | ||
51 | - if (len + 15 > dev->mtu) { | |
52 | - mutex_unlock(&econet_mutex); | |
53 | - return -EMSGSIZE; | |
54 | - } | |
55 | - | |
56 | if (dev->type == ARPHRD_ECONET) { | |
57 | /* Real hardware Econet. We're not worthy etc. */ | |
58 | #ifdef CONFIG_ECONET_NATIVE | |
59 | unsigned short proto = 0; | |
60 | int res; | |
61 | ||
62 | + if (len + 15 > dev->mtu) { | |
63 | + mutex_unlock(&econet_mutex); | |
64 | + return -EMSGSIZE; | |
65 | + } | |
66 | + | |
67 | dev_hold(dev); | |
68 | ||
69 | skb = sock_alloc_send_skb(sk, len+LL_ALLOCATED_SPACE(dev), | |
70 | @@ -405,6 +406,11 @@ static int econet_sendmsg(struct kiocb * | |
71 | return -ENETDOWN; /* No socket - can't send */ | |
72 | } | |
73 | ||
74 | + if (len > 32768) { | |
75 | + err = -E2BIG; | |
76 | + goto error; | |
77 | + } | |
78 | + | |
79 | /* Make up a UDP datagram and hand it off to some higher intellect. */ | |
80 | ||
81 | memset(&udpdest, 0, sizeof(udpdest)); | |
82 | @@ -436,36 +442,26 @@ static int econet_sendmsg(struct kiocb * | |
83 | ||
84 | /* tack our header on the front of the iovec */ | |
85 | size = sizeof(struct aunhdr); | |
86 | - /* | |
87 | - * XXX: that is b0rken. We can't mix userland and kernel pointers | |
88 | - * in iovec, since on a lot of platforms copy_from_user() will | |
89 | - * *not* work with the kernel and userland ones at the same time, | |
90 | - * regardless of what we do with set_fs(). And we are talking about | |
91 | - * econet-over-ethernet here, so "it's only ARM anyway" doesn't | |
92 | - * apply. Any suggestions on fixing that code? -- AV | |
93 | - */ | |
94 | iov[0].iov_base = (void *)&ah; | |
95 | iov[0].iov_len = size; | |
96 | - for (i = 0; i < msg->msg_iovlen; i++) { | |
97 | - void __user *base = msg->msg_iov[i].iov_base; | |
98 | - size_t iov_len = msg->msg_iov[i].iov_len; | |
99 | - /* Check it now since we switch to KERNEL_DS later. */ | |
100 | - if (!access_ok(VERIFY_READ, base, iov_len)) { | |
101 | - mutex_unlock(&econet_mutex); | |
102 | - return -EFAULT; | |
103 | - } | |
104 | - iov[i+1].iov_base = base; | |
105 | - iov[i+1].iov_len = iov_len; | |
106 | - size += iov_len; | |
107 | + | |
108 | + userbuf = vmalloc(len); | |
109 | + if (userbuf == NULL) { | |
110 | + err = -ENOMEM; | |
111 | + goto error; | |
112 | } | |
113 | ||
114 | + iov[1].iov_base = userbuf; | |
115 | + iov[1].iov_len = len; | |
116 | + err = memcpy_fromiovec(userbuf, msg->msg_iov, len); | |
117 | + if (err) | |
118 | + goto error_free_buf; | |
119 | + | |
120 | /* Get a skbuff (no data, just holds our cb information) */ | |
121 | if ((skb = sock_alloc_send_skb(sk, 0, | |
122 | msg->msg_flags & MSG_DONTWAIT, | |
123 | - &err)) == NULL) { | |
124 | - mutex_unlock(&econet_mutex); | |
125 | - return err; | |
126 | - } | |
127 | + &err)) == NULL) | |
128 | + goto error_free_buf; | |
129 | ||
130 | eb = (struct ec_cb *)&skb->cb; | |
131 | ||
132 | @@ -481,7 +477,7 @@ static int econet_sendmsg(struct kiocb * | |
133 | udpmsg.msg_name = (void *)&udpdest; | |
134 | udpmsg.msg_namelen = sizeof(udpdest); | |
135 | udpmsg.msg_iov = &iov[0]; | |
136 | - udpmsg.msg_iovlen = msg->msg_iovlen + 1; | |
137 | + udpmsg.msg_iovlen = 2; | |
138 | udpmsg.msg_control = NULL; | |
139 | udpmsg.msg_controllen = 0; | |
140 | udpmsg.msg_flags=0; | |
141 | @@ -489,9 +485,13 @@ static int econet_sendmsg(struct kiocb * | |
142 | oldfs = get_fs(); set_fs(KERNEL_DS); /* More privs :-) */ | |
143 | err = sock_sendmsg(udpsock, &udpmsg, size); | |
144 | set_fs(oldfs); | |
145 | + | |
146 | +error_free_buf: | |
147 | + vfree(userbuf); | |
148 | #else | |
149 | err = -EPROTOTYPE; | |
150 | #endif | |
151 | + error: | |
152 | mutex_unlock(&econet_mutex); | |
153 | ||
154 | return err; |