diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/compat.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/compat.c')
-rw-r--r-- | net/compat.c | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/net/compat.c b/net/compat.c new file mode 100644 index 000000000000..be5d936dc423 --- /dev/null +++ b/net/compat.c | |||
@@ -0,0 +1,605 @@ | |||
1 | /* | ||
2 | * 32bit Socket syscall emulation. Based on arch/sparc64/kernel/sys_sparc32.c. | ||
3 | * | ||
4 | * Copyright (C) 2000 VA Linux Co | ||
5 | * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> | ||
6 | * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> | ||
7 | * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | ||
8 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) | ||
9 | * Copyright (C) 2000 Hewlett-Packard Co. | ||
10 | * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> | ||
11 | * Copyright (C) 2000,2001 Andi Kleen, SuSE Labs | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/file.h> | ||
19 | #include <linux/icmpv6.h> | ||
20 | #include <linux/socket.h> | ||
21 | #include <linux/syscalls.h> | ||
22 | #include <linux/filter.h> | ||
23 | #include <linux/compat.h> | ||
24 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
25 | #include <linux/security.h> | ||
26 | |||
27 | #include <net/scm.h> | ||
28 | #include <net/sock.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | #include <net/compat.h> | ||
31 | |||
32 | static inline int iov_from_user_compat_to_kern(struct iovec *kiov, | ||
33 | struct compat_iovec __user *uiov32, | ||
34 | int niov) | ||
35 | { | ||
36 | int tot_len = 0; | ||
37 | |||
38 | while(niov > 0) { | ||
39 | compat_uptr_t buf; | ||
40 | compat_size_t len; | ||
41 | |||
42 | if(get_user(len, &uiov32->iov_len) || | ||
43 | get_user(buf, &uiov32->iov_base)) { | ||
44 | tot_len = -EFAULT; | ||
45 | break; | ||
46 | } | ||
47 | tot_len += len; | ||
48 | kiov->iov_base = compat_ptr(buf); | ||
49 | kiov->iov_len = (__kernel_size_t) len; | ||
50 | uiov32++; | ||
51 | kiov++; | ||
52 | niov--; | ||
53 | } | ||
54 | return tot_len; | ||
55 | } | ||
56 | |||
57 | int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) | ||
58 | { | ||
59 | compat_uptr_t tmp1, tmp2, tmp3; | ||
60 | |||
61 | if (!access_ok(VERIFY_READ, umsg, sizeof(*umsg)) || | ||
62 | __get_user(tmp1, &umsg->msg_name) || | ||
63 | __get_user(kmsg->msg_namelen, &umsg->msg_namelen) || | ||
64 | __get_user(tmp2, &umsg->msg_iov) || | ||
65 | __get_user(kmsg->msg_iovlen, &umsg->msg_iovlen) || | ||
66 | __get_user(tmp3, &umsg->msg_control) || | ||
67 | __get_user(kmsg->msg_controllen, &umsg->msg_controllen) || | ||
68 | __get_user(kmsg->msg_flags, &umsg->msg_flags)) | ||
69 | return -EFAULT; | ||
70 | kmsg->msg_name = compat_ptr(tmp1); | ||
71 | kmsg->msg_iov = compat_ptr(tmp2); | ||
72 | kmsg->msg_control = compat_ptr(tmp3); | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | /* I've named the args so it is easy to tell whose space the pointers are in. */ | ||
77 | int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, | ||
78 | char *kern_address, int mode) | ||
79 | { | ||
80 | int tot_len; | ||
81 | |||
82 | if(kern_msg->msg_namelen) { | ||
83 | if(mode==VERIFY_READ) { | ||
84 | int err = move_addr_to_kernel(kern_msg->msg_name, | ||
85 | kern_msg->msg_namelen, | ||
86 | kern_address); | ||
87 | if(err < 0) | ||
88 | return err; | ||
89 | } | ||
90 | kern_msg->msg_name = kern_address; | ||
91 | } else | ||
92 | kern_msg->msg_name = NULL; | ||
93 | |||
94 | if(kern_msg->msg_iovlen > UIO_FASTIOV) { | ||
95 | kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), | ||
96 | GFP_KERNEL); | ||
97 | if(!kern_iov) | ||
98 | return -ENOMEM; | ||
99 | } | ||
100 | |||
101 | tot_len = iov_from_user_compat_to_kern(kern_iov, | ||
102 | (struct compat_iovec __user *)kern_msg->msg_iov, | ||
103 | kern_msg->msg_iovlen); | ||
104 | if(tot_len >= 0) | ||
105 | kern_msg->msg_iov = kern_iov; | ||
106 | else if(kern_msg->msg_iovlen > UIO_FASTIOV) | ||
107 | kfree(kern_iov); | ||
108 | |||
109 | return tot_len; | ||
110 | } | ||
111 | |||
112 | /* Bleech... */ | ||
113 | #define CMSG_COMPAT_ALIGN(len) ALIGN((len), sizeof(s32)) | ||
114 | |||
115 | #define CMSG_COMPAT_DATA(cmsg) \ | ||
116 | ((void __user *)((char __user *)(cmsg) + CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)))) | ||
117 | #define CMSG_COMPAT_SPACE(len) \ | ||
118 | (CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)) + CMSG_COMPAT_ALIGN(len)) | ||
119 | #define CMSG_COMPAT_LEN(len) \ | ||
120 | (CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)) + (len)) | ||
121 | |||
122 | #define CMSG_COMPAT_FIRSTHDR(msg) \ | ||
123 | (((msg)->msg_controllen) >= sizeof(struct compat_cmsghdr) ? \ | ||
124 | (struct compat_cmsghdr __user *)((msg)->msg_control) : \ | ||
125 | (struct compat_cmsghdr __user *)NULL) | ||
126 | |||
127 | #define CMSG_COMPAT_OK(ucmlen, ucmsg, mhdr) \ | ||
128 | ((ucmlen) >= sizeof(struct compat_cmsghdr) && \ | ||
129 | (ucmlen) <= (unsigned long) \ | ||
130 | ((mhdr)->msg_controllen - \ | ||
131 | ((char *)(ucmsg) - (char *)(mhdr)->msg_control))) | ||
132 | |||
133 | static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *msg, | ||
134 | struct compat_cmsghdr __user *cmsg, int cmsg_len) | ||
135 | { | ||
136 | char __user *ptr = (char __user *)cmsg + CMSG_COMPAT_ALIGN(cmsg_len); | ||
137 | if ((unsigned long)(ptr + 1 - (char __user *)msg->msg_control) > | ||
138 | msg->msg_controllen) | ||
139 | return NULL; | ||
140 | return (struct compat_cmsghdr __user *)ptr; | ||
141 | } | ||
142 | |||
143 | /* There is a lot of hair here because the alignment rules (and | ||
144 | * thus placement) of cmsg headers and length are different for | ||
145 | * 32-bit apps. -DaveM | ||
146 | */ | ||
147 | int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, | ||
148 | unsigned char *stackbuf, int stackbuf_size) | ||
149 | { | ||
150 | struct compat_cmsghdr __user *ucmsg; | ||
151 | struct cmsghdr *kcmsg, *kcmsg_base; | ||
152 | compat_size_t ucmlen; | ||
153 | __kernel_size_t kcmlen, tmp; | ||
154 | |||
155 | kcmlen = 0; | ||
156 | kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; | ||
157 | ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); | ||
158 | while(ucmsg != NULL) { | ||
159 | if(get_user(ucmlen, &ucmsg->cmsg_len)) | ||
160 | return -EFAULT; | ||
161 | |||
162 | /* Catch bogons. */ | ||
163 | if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg)) | ||
164 | return -EINVAL; | ||
165 | |||
166 | tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + | ||
167 | CMSG_ALIGN(sizeof(struct cmsghdr))); | ||
168 | kcmlen += tmp; | ||
169 | ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); | ||
170 | } | ||
171 | if(kcmlen == 0) | ||
172 | return -EINVAL; | ||
173 | |||
174 | /* The kcmlen holds the 64-bit version of the control length. | ||
175 | * It may not be modified as we do not stick it into the kmsg | ||
176 | * until we have successfully copied over all of the data | ||
177 | * from the user. | ||
178 | */ | ||
179 | if(kcmlen > stackbuf_size) | ||
180 | kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); | ||
181 | if(kcmsg == NULL) | ||
182 | return -ENOBUFS; | ||
183 | |||
184 | /* Now copy them over neatly. */ | ||
185 | memset(kcmsg, 0, kcmlen); | ||
186 | ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); | ||
187 | while(ucmsg != NULL) { | ||
188 | __get_user(ucmlen, &ucmsg->cmsg_len); | ||
189 | tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + | ||
190 | CMSG_ALIGN(sizeof(struct cmsghdr))); | ||
191 | kcmsg->cmsg_len = tmp; | ||
192 | __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); | ||
193 | __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); | ||
194 | |||
195 | /* Copy over the data. */ | ||
196 | if(copy_from_user(CMSG_DATA(kcmsg), | ||
197 | CMSG_COMPAT_DATA(ucmsg), | ||
198 | (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) | ||
199 | goto out_free_efault; | ||
200 | |||
201 | /* Advance. */ | ||
202 | kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); | ||
203 | ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); | ||
204 | } | ||
205 | |||
206 | /* Ok, looks like we made it. Hook it up and return success. */ | ||
207 | kmsg->msg_control = kcmsg_base; | ||
208 | kmsg->msg_controllen = kcmlen; | ||
209 | return 0; | ||
210 | |||
211 | out_free_efault: | ||
212 | if(kcmsg_base != (struct cmsghdr *)stackbuf) | ||
213 | kfree(kcmsg_base); | ||
214 | return -EFAULT; | ||
215 | } | ||
216 | |||
217 | int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) | ||
218 | { | ||
219 | struct compat_timeval ctv; | ||
220 | struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; | ||
221 | struct compat_cmsghdr cmhdr; | ||
222 | int cmlen; | ||
223 | |||
224 | if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { | ||
225 | kmsg->msg_flags |= MSG_CTRUNC; | ||
226 | return 0; /* XXX: return error? check spec. */ | ||
227 | } | ||
228 | |||
229 | if (level == SOL_SOCKET && type == SO_TIMESTAMP) { | ||
230 | struct timeval *tv = (struct timeval *)data; | ||
231 | ctv.tv_sec = tv->tv_sec; | ||
232 | ctv.tv_usec = tv->tv_usec; | ||
233 | data = &ctv; | ||
234 | len = sizeof(struct compat_timeval); | ||
235 | } | ||
236 | |||
237 | cmlen = CMSG_COMPAT_LEN(len); | ||
238 | if(kmsg->msg_controllen < cmlen) { | ||
239 | kmsg->msg_flags |= MSG_CTRUNC; | ||
240 | cmlen = kmsg->msg_controllen; | ||
241 | } | ||
242 | cmhdr.cmsg_level = level; | ||
243 | cmhdr.cmsg_type = type; | ||
244 | cmhdr.cmsg_len = cmlen; | ||
245 | |||
246 | if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) | ||
247 | return -EFAULT; | ||
248 | if(copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr))) | ||
249 | return -EFAULT; | ||
250 | cmlen = CMSG_COMPAT_SPACE(len); | ||
251 | kmsg->msg_control += cmlen; | ||
252 | kmsg->msg_controllen -= cmlen; | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) | ||
257 | { | ||
258 | struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; | ||
259 | int fdmax = (kmsg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int); | ||
260 | int fdnum = scm->fp->count; | ||
261 | struct file **fp = scm->fp->fp; | ||
262 | int __user *cmfptr; | ||
263 | int err = 0, i; | ||
264 | |||
265 | if (fdnum < fdmax) | ||
266 | fdmax = fdnum; | ||
267 | |||
268 | for (i = 0, cmfptr = (int __user *) CMSG_COMPAT_DATA(cm); i < fdmax; i++, cmfptr++) { | ||
269 | int new_fd; | ||
270 | err = security_file_receive(fp[i]); | ||
271 | if (err) | ||
272 | break; | ||
273 | err = get_unused_fd(); | ||
274 | if (err < 0) | ||
275 | break; | ||
276 | new_fd = err; | ||
277 | err = put_user(new_fd, cmfptr); | ||
278 | if (err) { | ||
279 | put_unused_fd(new_fd); | ||
280 | break; | ||
281 | } | ||
282 | /* Bump the usage count and install the file. */ | ||
283 | get_file(fp[i]); | ||
284 | fd_install(new_fd, fp[i]); | ||
285 | } | ||
286 | |||
287 | if (i > 0) { | ||
288 | int cmlen = CMSG_COMPAT_LEN(i * sizeof(int)); | ||
289 | if (!err) | ||
290 | err = put_user(SOL_SOCKET, &cm->cmsg_level); | ||
291 | if (!err) | ||
292 | err = put_user(SCM_RIGHTS, &cm->cmsg_type); | ||
293 | if (!err) | ||
294 | err = put_user(cmlen, &cm->cmsg_len); | ||
295 | if (!err) { | ||
296 | cmlen = CMSG_COMPAT_SPACE(i * sizeof(int)); | ||
297 | kmsg->msg_control += cmlen; | ||
298 | kmsg->msg_controllen -= cmlen; | ||
299 | } | ||
300 | } | ||
301 | if (i < fdnum) | ||
302 | kmsg->msg_flags |= MSG_CTRUNC; | ||
303 | |||
304 | /* | ||
305 | * All of the files that fit in the message have had their | ||
306 | * usage counts incremented, so we just free the list. | ||
307 | */ | ||
308 | __scm_destroy(scm); | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * For now, we assume that the compatibility and native version | ||
313 | * of struct ipt_entry are the same - sfr. FIXME | ||
314 | */ | ||
315 | struct compat_ipt_replace { | ||
316 | char name[IPT_TABLE_MAXNAMELEN]; | ||
317 | u32 valid_hooks; | ||
318 | u32 num_entries; | ||
319 | u32 size; | ||
320 | u32 hook_entry[NF_IP_NUMHOOKS]; | ||
321 | u32 underflow[NF_IP_NUMHOOKS]; | ||
322 | u32 num_counters; | ||
323 | compat_uptr_t counters; /* struct ipt_counters * */ | ||
324 | struct ipt_entry entries[0]; | ||
325 | }; | ||
326 | |||
327 | static int do_netfilter_replace(int fd, int level, int optname, | ||
328 | char __user *optval, int optlen) | ||
329 | { | ||
330 | struct compat_ipt_replace __user *urepl; | ||
331 | struct ipt_replace __user *repl_nat; | ||
332 | char name[IPT_TABLE_MAXNAMELEN]; | ||
333 | u32 origsize, tmp32, num_counters; | ||
334 | unsigned int repl_nat_size; | ||
335 | int ret; | ||
336 | int i; | ||
337 | compat_uptr_t ucntrs; | ||
338 | |||
339 | urepl = (struct compat_ipt_replace __user *)optval; | ||
340 | if (get_user(origsize, &urepl->size)) | ||
341 | return -EFAULT; | ||
342 | |||
343 | /* Hack: Causes ipchains to give correct error msg --RR */ | ||
344 | if (optlen != sizeof(*urepl) + origsize) | ||
345 | return -ENOPROTOOPT; | ||
346 | |||
347 | /* XXX Assumes that size of ipt_entry is the same both in | ||
348 | * native and compat environments. | ||
349 | */ | ||
350 | repl_nat_size = sizeof(*repl_nat) + origsize; | ||
351 | repl_nat = compat_alloc_user_space(repl_nat_size); | ||
352 | |||
353 | ret = -EFAULT; | ||
354 | if (put_user(origsize, &repl_nat->size)) | ||
355 | goto out; | ||
356 | |||
357 | if (!access_ok(VERIFY_READ, urepl, optlen) || | ||
358 | !access_ok(VERIFY_WRITE, repl_nat, optlen)) | ||
359 | goto out; | ||
360 | |||
361 | if (__copy_from_user(name, urepl->name, sizeof(urepl->name)) || | ||
362 | __copy_to_user(repl_nat->name, name, sizeof(repl_nat->name))) | ||
363 | goto out; | ||
364 | |||
365 | if (__get_user(tmp32, &urepl->valid_hooks) || | ||
366 | __put_user(tmp32, &repl_nat->valid_hooks)) | ||
367 | goto out; | ||
368 | |||
369 | if (__get_user(tmp32, &urepl->num_entries) || | ||
370 | __put_user(tmp32, &repl_nat->num_entries)) | ||
371 | goto out; | ||
372 | |||
373 | if (__get_user(num_counters, &urepl->num_counters) || | ||
374 | __put_user(num_counters, &repl_nat->num_counters)) | ||
375 | goto out; | ||
376 | |||
377 | if (__get_user(ucntrs, &urepl->counters) || | ||
378 | __put_user(compat_ptr(ucntrs), &repl_nat->counters)) | ||
379 | goto out; | ||
380 | |||
381 | if (__copy_in_user(&repl_nat->entries[0], | ||
382 | &urepl->entries[0], | ||
383 | origsize)) | ||
384 | goto out; | ||
385 | |||
386 | for (i = 0; i < NF_IP_NUMHOOKS; i++) { | ||
387 | if (__get_user(tmp32, &urepl->hook_entry[i]) || | ||
388 | __put_user(tmp32, &repl_nat->hook_entry[i]) || | ||
389 | __get_user(tmp32, &urepl->underflow[i]) || | ||
390 | __put_user(tmp32, &repl_nat->underflow[i])) | ||
391 | goto out; | ||
392 | } | ||
393 | |||
394 | /* | ||
395 | * Since struct ipt_counters just contains two u_int64_t members | ||
396 | * we can just do the access_ok check here and pass the (converted) | ||
397 | * pointer into the standard syscall. We hope that the pointer is | ||
398 | * not misaligned ... | ||
399 | */ | ||
400 | if (!access_ok(VERIFY_WRITE, compat_ptr(ucntrs), | ||
401 | num_counters * sizeof(struct ipt_counters))) | ||
402 | goto out; | ||
403 | |||
404 | |||
405 | ret = sys_setsockopt(fd, level, optname, | ||
406 | (char __user *)repl_nat, repl_nat_size); | ||
407 | |||
408 | out: | ||
409 | return ret; | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * A struct sock_filter is architecture independent. | ||
414 | */ | ||
415 | struct compat_sock_fprog { | ||
416 | u16 len; | ||
417 | compat_uptr_t filter; /* struct sock_filter * */ | ||
418 | }; | ||
419 | |||
420 | static int do_set_attach_filter(int fd, int level, int optname, | ||
421 | char __user *optval, int optlen) | ||
422 | { | ||
423 | struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; | ||
424 | struct sock_fprog __user *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog)); | ||
425 | compat_uptr_t ptr; | ||
426 | u16 len; | ||
427 | |||
428 | if (!access_ok(VERIFY_READ, fprog32, sizeof(*fprog32)) || | ||
429 | !access_ok(VERIFY_WRITE, kfprog, sizeof(struct sock_fprog)) || | ||
430 | __get_user(len, &fprog32->len) || | ||
431 | __get_user(ptr, &fprog32->filter) || | ||
432 | __put_user(len, &kfprog->len) || | ||
433 | __put_user(compat_ptr(ptr), &kfprog->filter)) | ||
434 | return -EFAULT; | ||
435 | |||
436 | return sys_setsockopt(fd, level, optname, (char __user *)kfprog, | ||
437 | sizeof(struct sock_fprog)); | ||
438 | } | ||
439 | |||
440 | static int do_set_sock_timeout(int fd, int level, int optname, char __user *optval, int optlen) | ||
441 | { | ||
442 | struct compat_timeval __user *up = (struct compat_timeval __user *) optval; | ||
443 | struct timeval ktime; | ||
444 | mm_segment_t old_fs; | ||
445 | int err; | ||
446 | |||
447 | if (optlen < sizeof(*up)) | ||
448 | return -EINVAL; | ||
449 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || | ||
450 | __get_user(ktime.tv_sec, &up->tv_sec) || | ||
451 | __get_user(ktime.tv_usec, &up->tv_usec)) | ||
452 | return -EFAULT; | ||
453 | old_fs = get_fs(); | ||
454 | set_fs(KERNEL_DS); | ||
455 | err = sys_setsockopt(fd, level, optname, (char *) &ktime, sizeof(ktime)); | ||
456 | set_fs(old_fs); | ||
457 | |||
458 | return err; | ||
459 | } | ||
460 | |||
461 | asmlinkage long compat_sys_setsockopt(int fd, int level, int optname, | ||
462 | char __user *optval, int optlen) | ||
463 | { | ||
464 | /* SO_SET_REPLACE seems to be the same in all levels */ | ||
465 | if (optname == IPT_SO_SET_REPLACE) | ||
466 | return do_netfilter_replace(fd, level, optname, | ||
467 | optval, optlen); | ||
468 | if (level == SOL_SOCKET && optname == SO_ATTACH_FILTER) | ||
469 | return do_set_attach_filter(fd, level, optname, | ||
470 | optval, optlen); | ||
471 | if (level == SOL_SOCKET && | ||
472 | (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) | ||
473 | return do_set_sock_timeout(fd, level, optname, optval, optlen); | ||
474 | |||
475 | return sys_setsockopt(fd, level, optname, optval, optlen); | ||
476 | } | ||
477 | |||
478 | static int do_get_sock_timeout(int fd, int level, int optname, | ||
479 | char __user *optval, int __user *optlen) | ||
480 | { | ||
481 | struct compat_timeval __user *up; | ||
482 | struct timeval ktime; | ||
483 | mm_segment_t old_fs; | ||
484 | int len, err; | ||
485 | |||
486 | up = (struct compat_timeval __user *) optval; | ||
487 | if (get_user(len, optlen)) | ||
488 | return -EFAULT; | ||
489 | if (len < sizeof(*up)) | ||
490 | return -EINVAL; | ||
491 | len = sizeof(ktime); | ||
492 | old_fs = get_fs(); | ||
493 | set_fs(KERNEL_DS); | ||
494 | err = sys_getsockopt(fd, level, optname, (char *) &ktime, &len); | ||
495 | set_fs(old_fs); | ||
496 | |||
497 | if (!err) { | ||
498 | if (put_user(sizeof(*up), optlen) || | ||
499 | !access_ok(VERIFY_WRITE, up, sizeof(*up)) || | ||
500 | __put_user(ktime.tv_sec, &up->tv_sec) || | ||
501 | __put_user(ktime.tv_usec, &up->tv_usec)) | ||
502 | err = -EFAULT; | ||
503 | } | ||
504 | return err; | ||
505 | } | ||
506 | |||
507 | asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, | ||
508 | char __user *optval, int __user *optlen) | ||
509 | { | ||
510 | if (level == SOL_SOCKET && | ||
511 | (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) | ||
512 | return do_get_sock_timeout(fd, level, optname, optval, optlen); | ||
513 | return sys_getsockopt(fd, level, optname, optval, optlen); | ||
514 | } | ||
515 | |||
516 | /* Argument list sizes for compat_sys_socketcall */ | ||
517 | #define AL(x) ((x) * sizeof(u32)) | ||
518 | static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), | ||
519 | AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), | ||
520 | AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; | ||
521 | #undef AL | ||
522 | |||
523 | asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags) | ||
524 | { | ||
525 | return sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); | ||
526 | } | ||
527 | |||
528 | asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, unsigned int flags) | ||
529 | { | ||
530 | return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); | ||
531 | } | ||
532 | |||
533 | asmlinkage long compat_sys_socketcall(int call, u32 __user *args) | ||
534 | { | ||
535 | int ret; | ||
536 | u32 a[6]; | ||
537 | u32 a0, a1; | ||
538 | |||
539 | if (call < SYS_SOCKET || call > SYS_RECVMSG) | ||
540 | return -EINVAL; | ||
541 | if (copy_from_user(a, args, nas[call])) | ||
542 | return -EFAULT; | ||
543 | a0 = a[0]; | ||
544 | a1 = a[1]; | ||
545 | |||
546 | switch(call) { | ||
547 | case SYS_SOCKET: | ||
548 | ret = sys_socket(a0, a1, a[2]); | ||
549 | break; | ||
550 | case SYS_BIND: | ||
551 | ret = sys_bind(a0, compat_ptr(a1), a[2]); | ||
552 | break; | ||
553 | case SYS_CONNECT: | ||
554 | ret = sys_connect(a0, compat_ptr(a1), a[2]); | ||
555 | break; | ||
556 | case SYS_LISTEN: | ||
557 | ret = sys_listen(a0, a1); | ||
558 | break; | ||
559 | case SYS_ACCEPT: | ||
560 | ret = sys_accept(a0, compat_ptr(a1), compat_ptr(a[2])); | ||
561 | break; | ||
562 | case SYS_GETSOCKNAME: | ||
563 | ret = sys_getsockname(a0, compat_ptr(a1), compat_ptr(a[2])); | ||
564 | break; | ||
565 | case SYS_GETPEERNAME: | ||
566 | ret = sys_getpeername(a0, compat_ptr(a1), compat_ptr(a[2])); | ||
567 | break; | ||
568 | case SYS_SOCKETPAIR: | ||
569 | ret = sys_socketpair(a0, a1, a[2], compat_ptr(a[3])); | ||
570 | break; | ||
571 | case SYS_SEND: | ||
572 | ret = sys_send(a0, compat_ptr(a1), a[2], a[3]); | ||
573 | break; | ||
574 | case SYS_SENDTO: | ||
575 | ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]); | ||
576 | break; | ||
577 | case SYS_RECV: | ||
578 | ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]); | ||
579 | break; | ||
580 | case SYS_RECVFROM: | ||
581 | ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5])); | ||
582 | break; | ||
583 | case SYS_SHUTDOWN: | ||
584 | ret = sys_shutdown(a0,a1); | ||
585 | break; | ||
586 | case SYS_SETSOCKOPT: | ||
587 | ret = compat_sys_setsockopt(a0, a1, a[2], | ||
588 | compat_ptr(a[3]), a[4]); | ||
589 | break; | ||
590 | case SYS_GETSOCKOPT: | ||
591 | ret = compat_sys_getsockopt(a0, a1, a[2], | ||
592 | compat_ptr(a[3]), compat_ptr(a[4])); | ||
593 | break; | ||
594 | case SYS_SENDMSG: | ||
595 | ret = compat_sys_sendmsg(a0, compat_ptr(a1), a[2]); | ||
596 | break; | ||
597 | case SYS_RECVMSG: | ||
598 | ret = compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); | ||
599 | break; | ||
600 | default: | ||
601 | ret = -EINVAL; | ||
602 | break; | ||
603 | } | ||
604 | return ret; | ||
605 | } | ||