aboutsummaryrefslogtreecommitdiffstats
path: root/net/compat.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2005-09-07 21:28:51 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-09-08 11:14:11 -0400
commit8920e8f94c44e31a73bdf923b04721e26e88cadd (patch)
tree7a0195643c37c63335224358256fab8cd445a671 /net/compat.c
parent5aa3b610a7330c3cd6f0cb264d2189a3a1dcf534 (diff)
[PATCH] Fix 32bit sendmsg() flaw
When we copy 32bit ->msg_control contents to kernel, we walk the same userland data twice without sanity checks on the second pass. Second version of this patch: the original broke with 64-bit arches running 32-bit-compat-mode executables doing sendmsg() syscalls with unaligned CMSG data areas Another thing is that we use kmalloc() to allocate and sock_kfree_s() to free afterwards; less serious, but also needs fixing. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Chris Wright <chrisw@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'net/compat.c')
-rw-r--r--net/compat.c44
1 files changed, 26 insertions, 18 deletions
diff --git a/net/compat.c b/net/compat.c
index d99ab9695893..e593dace2fdb 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -135,13 +135,14 @@ static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *ms
135 * thus placement) of cmsg headers and length are different for 135 * thus placement) of cmsg headers and length are different for
136 * 32-bit apps. -DaveM 136 * 32-bit apps. -DaveM
137 */ 137 */
138int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, 138int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk,
139 unsigned char *stackbuf, int stackbuf_size) 139 unsigned char *stackbuf, int stackbuf_size)
140{ 140{
141 struct compat_cmsghdr __user *ucmsg; 141 struct compat_cmsghdr __user *ucmsg;
142 struct cmsghdr *kcmsg, *kcmsg_base; 142 struct cmsghdr *kcmsg, *kcmsg_base;
143 compat_size_t ucmlen; 143 compat_size_t ucmlen;
144 __kernel_size_t kcmlen, tmp; 144 __kernel_size_t kcmlen, tmp;
145 int err = -EFAULT;
145 146
146 kcmlen = 0; 147 kcmlen = 0;
147 kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; 148 kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf;
@@ -156,6 +157,7 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg,
156 157
157 tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + 158 tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) +
158 CMSG_ALIGN(sizeof(struct cmsghdr))); 159 CMSG_ALIGN(sizeof(struct cmsghdr)));
160 tmp = CMSG_ALIGN(tmp);
159 kcmlen += tmp; 161 kcmlen += tmp;
160 ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); 162 ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen);
161 } 163 }
@@ -167,30 +169,34 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg,
167 * until we have successfully copied over all of the data 169 * until we have successfully copied over all of the data
168 * from the user. 170 * from the user.
169 */ 171 */
170 if(kcmlen > stackbuf_size) 172 if (kcmlen > stackbuf_size)
171 kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); 173 kcmsg_base = kcmsg = sock_kmalloc(sk, kcmlen, GFP_KERNEL);
172 if(kcmsg == NULL) 174 if (kcmsg == NULL)
173 return -ENOBUFS; 175 return -ENOBUFS;
174 176
175 /* Now copy them over neatly. */ 177 /* Now copy them over neatly. */
176 memset(kcmsg, 0, kcmlen); 178 memset(kcmsg, 0, kcmlen);
177 ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); 179 ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg);
178 while(ucmsg != NULL) { 180 while(ucmsg != NULL) {
179 __get_user(ucmlen, &ucmsg->cmsg_len); 181 if (__get_user(ucmlen, &ucmsg->cmsg_len))
182 goto Efault;
183 if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg))
184 goto Einval;
180 tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + 185 tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) +
181 CMSG_ALIGN(sizeof(struct cmsghdr))); 186 CMSG_ALIGN(sizeof(struct cmsghdr)));
187 if ((char *)kcmsg_base + kcmlen - (char *)kcmsg < CMSG_ALIGN(tmp))
188 goto Einval;
182 kcmsg->cmsg_len = tmp; 189 kcmsg->cmsg_len = tmp;
183 __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); 190 tmp = CMSG_ALIGN(tmp);
184 __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); 191 if (__get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level) ||
185 192 __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type) ||
186 /* Copy over the data. */ 193 copy_from_user(CMSG_DATA(kcmsg),
187 if(copy_from_user(CMSG_DATA(kcmsg), 194 CMSG_COMPAT_DATA(ucmsg),
188 CMSG_COMPAT_DATA(ucmsg), 195 (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg)))))
189 (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) 196 goto Efault;
190 goto out_free_efault;
191 197
192 /* Advance. */ 198 /* Advance. */
193 kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); 199 kcmsg = (struct cmsghdr *)((char *)kcmsg + tmp);
194 ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); 200 ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen);
195 } 201 }
196 202
@@ -199,10 +205,12 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg,
199 kmsg->msg_controllen = kcmlen; 205 kmsg->msg_controllen = kcmlen;
200 return 0; 206 return 0;
201 207
202out_free_efault: 208Einval:
203 if(kcmsg_base != (struct cmsghdr *)stackbuf) 209 err = -EINVAL;
204 kfree(kcmsg_base); 210Efault:
205 return -EFAULT; 211 if (kcmsg_base != (struct cmsghdr *)stackbuf)
212 sock_kfree_s(sk, kcmsg_base, kcmlen);
213 return err;
206} 214}
207 215
208int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) 216int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data)