1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * 32bit Socket syscall emulation. Based on arch/sparc64/kernel/sys_sparc32.c. |
4 | * |
5 | * Copyright (C) 2000 VA Linux Co |
6 | * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> |
7 | * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> |
8 | * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) |
9 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) |
10 | * Copyright (C) 2000 Hewlett-Packard Co. |
11 | * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> |
12 | * Copyright (C) 2000,2001 Andi Kleen, SuSE Labs |
13 | */ |
14 | |
15 | #include <linux/kernel.h> |
16 | #include <linux/gfp.h> |
17 | #include <linux/fs.h> |
18 | #include <linux/types.h> |
19 | #include <linux/file.h> |
20 | #include <linux/icmpv6.h> |
21 | #include <linux/socket.h> |
22 | #include <linux/syscalls.h> |
23 | #include <linux/filter.h> |
24 | #include <linux/compat.h> |
25 | #include <linux/security.h> |
26 | #include <linux/audit.h> |
27 | #include <linux/export.h> |
28 | |
29 | #include <net/scm.h> |
30 | #include <net/sock.h> |
31 | #include <net/ip.h> |
32 | #include <net/ipv6.h> |
33 | #include <linux/uaccess.h> |
34 | #include <net/compat.h> |
35 | |
36 | int __get_compat_msghdr(struct msghdr *kmsg, |
37 | struct compat_msghdr *msg, |
38 | struct sockaddr __user **save_addr) |
39 | { |
40 | ssize_t err; |
41 | |
42 | kmsg->msg_flags = msg->msg_flags; |
43 | kmsg->msg_namelen = msg->msg_namelen; |
44 | |
45 | if (!msg->msg_name) |
46 | kmsg->msg_namelen = 0; |
47 | |
48 | if (kmsg->msg_namelen < 0) |
49 | return -EINVAL; |
50 | |
51 | if (kmsg->msg_namelen > sizeof(struct sockaddr_storage)) |
52 | kmsg->msg_namelen = sizeof(struct sockaddr_storage); |
53 | |
54 | kmsg->msg_control_is_user = true; |
55 | kmsg->msg_get_inq = 0; |
56 | kmsg->msg_control_user = compat_ptr(uptr: msg->msg_control); |
57 | kmsg->msg_controllen = msg->msg_controllen; |
58 | |
59 | if (save_addr) |
60 | *save_addr = compat_ptr(uptr: msg->msg_name); |
61 | |
62 | if (msg->msg_name && kmsg->msg_namelen) { |
63 | if (!save_addr) { |
64 | err = move_addr_to_kernel(uaddr: compat_ptr(uptr: msg->msg_name), |
65 | ulen: kmsg->msg_namelen, |
66 | kaddr: kmsg->msg_name); |
67 | if (err < 0) |
68 | return err; |
69 | } |
70 | } else { |
71 | kmsg->msg_name = NULL; |
72 | kmsg->msg_namelen = 0; |
73 | } |
74 | |
75 | if (msg->msg_iovlen > UIO_MAXIOV) |
76 | return -EMSGSIZE; |
77 | |
78 | kmsg->msg_iocb = NULL; |
79 | kmsg->msg_ubuf = NULL; |
80 | return 0; |
81 | } |
82 | |
83 | int get_compat_msghdr(struct msghdr *kmsg, |
84 | struct compat_msghdr __user *umsg, |
85 | struct sockaddr __user **save_addr, |
86 | struct iovec **iov) |
87 | { |
88 | struct compat_msghdr msg; |
89 | ssize_t err; |
90 | |
91 | if (copy_from_user(to: &msg, from: umsg, n: sizeof(*umsg))) |
92 | return -EFAULT; |
93 | |
94 | err = __get_compat_msghdr(kmsg, msg: &msg, save_addr); |
95 | if (err) |
96 | return err; |
97 | |
98 | err = import_iovec(type: save_addr ? ITER_DEST : ITER_SOURCE, |
99 | uvec: compat_ptr(uptr: msg.msg_iov), nr_segs: msg.msg_iovlen, |
100 | UIO_FASTIOV, iovp: iov, i: &kmsg->msg_iter); |
101 | return err < 0 ? err : 0; |
102 | } |
103 | |
104 | /* Bleech... */ |
105 | #define CMSG_COMPAT_ALIGN(len) ALIGN((len), sizeof(s32)) |
106 | |
107 | #define CMSG_COMPAT_DATA(cmsg) \ |
108 | ((void __user *)((char __user *)(cmsg) + sizeof(struct compat_cmsghdr))) |
109 | #define CMSG_COMPAT_SPACE(len) \ |
110 | (sizeof(struct compat_cmsghdr) + CMSG_COMPAT_ALIGN(len)) |
111 | #define CMSG_COMPAT_LEN(len) \ |
112 | (sizeof(struct compat_cmsghdr) + (len)) |
113 | |
114 | #define CMSG_COMPAT_FIRSTHDR(msg) \ |
115 | (((msg)->msg_controllen) >= sizeof(struct compat_cmsghdr) ? \ |
116 | (struct compat_cmsghdr __user *)((msg)->msg_control_user) : \ |
117 | (struct compat_cmsghdr __user *)NULL) |
118 | |
119 | #define CMSG_COMPAT_OK(ucmlen, ucmsg, mhdr) \ |
120 | ((ucmlen) >= sizeof(struct compat_cmsghdr) && \ |
121 | (ucmlen) <= (unsigned long) \ |
122 | ((mhdr)->msg_controllen - \ |
123 | ((char __user *)(ucmsg) - (char __user *)(mhdr)->msg_control_user))) |
124 | |
125 | static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *msg, |
126 | struct compat_cmsghdr __user *cmsg, int cmsg_len) |
127 | { |
128 | char __user *ptr = (char __user *)cmsg + CMSG_COMPAT_ALIGN(cmsg_len); |
129 | if ((unsigned long)(ptr + 1 - (char __user *)msg->msg_control_user) > |
130 | msg->msg_controllen) |
131 | return NULL; |
132 | return (struct compat_cmsghdr __user *)ptr; |
133 | } |
134 | |
135 | /* There is a lot of hair here because the alignment rules (and |
136 | * thus placement) of cmsg headers and length are different for |
137 | * 32-bit apps. -DaveM |
138 | */ |
139 | int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk, |
140 | unsigned char *stackbuf, int stackbuf_size) |
141 | { |
142 | struct compat_cmsghdr __user *ucmsg; |
143 | struct cmsghdr *kcmsg, *kcmsg_base; |
144 | compat_size_t ucmlen; |
145 | __kernel_size_t kcmlen, tmp; |
146 | int err = -EFAULT; |
147 | |
148 | BUILD_BUG_ON(sizeof(struct compat_cmsghdr) != |
149 | CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr))); |
150 | |
151 | kcmlen = 0; |
152 | kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; |
153 | ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); |
154 | while (ucmsg != NULL) { |
155 | if (get_user(ucmlen, &ucmsg->cmsg_len)) |
156 | return -EFAULT; |
157 | |
158 | /* Catch bogons. */ |
159 | if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg)) |
160 | return -EINVAL; |
161 | |
162 | tmp = ((ucmlen - sizeof(*ucmsg)) + sizeof(struct cmsghdr)); |
163 | tmp = CMSG_ALIGN(tmp); |
164 | kcmlen += tmp; |
165 | ucmsg = cmsg_compat_nxthdr(msg: kmsg, cmsg: ucmsg, cmsg_len: ucmlen); |
166 | } |
167 | if (kcmlen == 0) |
168 | return -EINVAL; |
169 | |
170 | /* The kcmlen holds the 64-bit version of the control length. |
171 | * It may not be modified as we do not stick it into the kmsg |
172 | * until we have successfully copied over all of the data |
173 | * from the user. |
174 | */ |
175 | if (kcmlen > stackbuf_size) |
176 | kcmsg_base = kcmsg = sock_kmalloc(sk, size: kcmlen, GFP_KERNEL); |
177 | if (kcmsg == NULL) |
178 | return -ENOMEM; |
179 | |
180 | /* Now copy them over neatly. */ |
181 | memset(kcmsg, 0, kcmlen); |
182 | ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); |
183 | while (ucmsg != NULL) { |
184 | struct compat_cmsghdr cmsg; |
185 | if (copy_from_user(to: &cmsg, from: ucmsg, n: sizeof(cmsg))) |
186 | goto Efault; |
187 | if (!CMSG_COMPAT_OK(cmsg.cmsg_len, ucmsg, kmsg)) |
188 | goto Einval; |
189 | tmp = ((cmsg.cmsg_len - sizeof(*ucmsg)) + sizeof(struct cmsghdr)); |
190 | if ((char *)kcmsg_base + kcmlen - (char *)kcmsg < CMSG_ALIGN(tmp)) |
191 | goto Einval; |
192 | kcmsg->cmsg_len = tmp; |
193 | kcmsg->cmsg_level = cmsg.cmsg_level; |
194 | kcmsg->cmsg_type = cmsg.cmsg_type; |
195 | tmp = CMSG_ALIGN(tmp); |
196 | if (copy_from_user(CMSG_DATA(kcmsg), |
197 | CMSG_COMPAT_DATA(ucmsg), |
198 | n: (cmsg.cmsg_len - sizeof(*ucmsg)))) |
199 | goto Efault; |
200 | |
201 | /* Advance. */ |
202 | kcmsg = (struct cmsghdr *)((char *)kcmsg + tmp); |
203 | ucmsg = cmsg_compat_nxthdr(msg: kmsg, cmsg: ucmsg, cmsg_len: cmsg.cmsg_len); |
204 | } |
205 | |
206 | /* |
207 | * check the length of messages copied in is the same as the |
208 | * what we get from the first loop |
209 | */ |
210 | if ((char *)kcmsg - (char *)kcmsg_base != kcmlen) |
211 | goto Einval; |
212 | |
213 | /* Ok, looks like we made it. Hook it up and return success. */ |
214 | kmsg->msg_control_is_user = false; |
215 | kmsg->msg_control = kcmsg_base; |
216 | kmsg->msg_controllen = kcmlen; |
217 | return 0; |
218 | |
219 | Einval: |
220 | err = -EINVAL; |
221 | Efault: |
222 | if (kcmsg_base != (struct cmsghdr *)stackbuf) |
223 | sock_kfree_s(sk, mem: kcmsg_base, size: kcmlen); |
224 | return err; |
225 | } |
226 | |
227 | int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) |
228 | { |
229 | struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control_user; |
230 | struct compat_cmsghdr cmhdr; |
231 | struct old_timeval32 ctv; |
232 | struct old_timespec32 cts[3]; |
233 | int cmlen; |
234 | |
235 | if (cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { |
236 | kmsg->msg_flags |= MSG_CTRUNC; |
237 | return 0; /* XXX: return error? check spec. */ |
238 | } |
239 | |
240 | if (!COMPAT_USE_64BIT_TIME) { |
241 | if (level == SOL_SOCKET && type == SO_TIMESTAMP_OLD) { |
242 | struct __kernel_old_timeval *tv = (struct __kernel_old_timeval *)data; |
243 | ctv.tv_sec = tv->tv_sec; |
244 | ctv.tv_usec = tv->tv_usec; |
245 | data = &ctv; |
246 | len = sizeof(ctv); |
247 | } |
248 | if (level == SOL_SOCKET && |
249 | (type == SO_TIMESTAMPNS_OLD || type == SO_TIMESTAMPING_OLD)) { |
250 | int count = type == SO_TIMESTAMPNS_OLD ? 1 : 3; |
251 | int i; |
252 | struct __kernel_old_timespec *ts = data; |
253 | for (i = 0; i < count; i++) { |
254 | cts[i].tv_sec = ts[i].tv_sec; |
255 | cts[i].tv_nsec = ts[i].tv_nsec; |
256 | } |
257 | data = &cts; |
258 | len = sizeof(cts[0]) * count; |
259 | } |
260 | } |
261 | |
262 | cmlen = CMSG_COMPAT_LEN(len); |
263 | if (kmsg->msg_controllen < cmlen) { |
264 | kmsg->msg_flags |= MSG_CTRUNC; |
265 | cmlen = kmsg->msg_controllen; |
266 | } |
267 | cmhdr.cmsg_level = level; |
268 | cmhdr.cmsg_type = type; |
269 | cmhdr.cmsg_len = cmlen; |
270 | |
271 | if (copy_to_user(to: cm, from: &cmhdr, n: sizeof cmhdr)) |
272 | return -EFAULT; |
273 | if (copy_to_user(CMSG_COMPAT_DATA(cm), from: data, n: cmlen - sizeof(struct compat_cmsghdr))) |
274 | return -EFAULT; |
275 | cmlen = CMSG_COMPAT_SPACE(len); |
276 | if (kmsg->msg_controllen < cmlen) |
277 | cmlen = kmsg->msg_controllen; |
278 | kmsg->msg_control_user += cmlen; |
279 | kmsg->msg_controllen -= cmlen; |
280 | return 0; |
281 | } |
282 | |
283 | static int scm_max_fds_compat(struct msghdr *msg) |
284 | { |
285 | if (msg->msg_controllen <= sizeof(struct compat_cmsghdr)) |
286 | return 0; |
287 | return (msg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int); |
288 | } |
289 | |
290 | void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm) |
291 | { |
292 | struct compat_cmsghdr __user *cm = |
293 | (struct compat_cmsghdr __user *)msg->msg_control_user; |
294 | unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; |
295 | int fdmax = min_t(int, scm_max_fds_compat(msg), scm->fp->count); |
296 | int __user *cmsg_data = CMSG_COMPAT_DATA(cm); |
297 | int err = 0, i; |
298 | |
299 | for (i = 0; i < fdmax; i++) { |
300 | err = receive_fd_user(file: scm->fp->fp[i], ufd: cmsg_data + i, o_flags); |
301 | if (err < 0) |
302 | break; |
303 | } |
304 | |
305 | if (i > 0) { |
306 | int cmlen = CMSG_COMPAT_LEN(i * sizeof(int)); |
307 | |
308 | err = put_user(SOL_SOCKET, &cm->cmsg_level); |
309 | if (!err) |
310 | err = put_user(SCM_RIGHTS, &cm->cmsg_type); |
311 | if (!err) |
312 | err = put_user(cmlen, &cm->cmsg_len); |
313 | if (!err) { |
314 | cmlen = CMSG_COMPAT_SPACE(i * sizeof(int)); |
315 | if (msg->msg_controllen < cmlen) |
316 | cmlen = msg->msg_controllen; |
317 | msg->msg_control_user += cmlen; |
318 | msg->msg_controllen -= cmlen; |
319 | } |
320 | } |
321 | |
322 | if (i < scm->fp->count || (scm->fp->count && fdmax <= 0)) |
323 | msg->msg_flags |= MSG_CTRUNC; |
324 | |
325 | /* |
326 | * All of the files that fit in the message have had their usage counts |
327 | * incremented, so we just free the list. |
328 | */ |
329 | __scm_destroy(scm); |
330 | } |
331 | |
332 | /* Argument list sizes for compat_sys_socketcall */ |
333 | #define AL(x) ((x) * sizeof(u32)) |
334 | static unsigned char nas[21] = { |
335 | AL(0), AL(3), AL(3), AL(3), AL(2), AL(3), |
336 | AL(3), AL(3), AL(4), AL(4), AL(4), AL(6), |
337 | AL(6), AL(2), AL(5), AL(5), AL(3), AL(3), |
338 | AL(4), AL(5), AL(4) |
339 | }; |
340 | #undef AL |
341 | |
342 | static inline long __compat_sys_sendmsg(int fd, |
343 | struct compat_msghdr __user *msg, |
344 | unsigned int flags) |
345 | { |
346 | return __sys_sendmsg(fd, msg: (struct user_msghdr __user *)msg, |
347 | flags: flags | MSG_CMSG_COMPAT, forbid_cmsg_compat: false); |
348 | } |
349 | |
350 | COMPAT_SYSCALL_DEFINE3(sendmsg, int, fd, struct compat_msghdr __user *, msg, |
351 | unsigned int, flags) |
352 | { |
353 | return __compat_sys_sendmsg(fd, msg, flags); |
354 | } |
355 | |
356 | static inline long __compat_sys_sendmmsg(int fd, |
357 | struct compat_mmsghdr __user *mmsg, |
358 | unsigned int vlen, unsigned int flags) |
359 | { |
360 | return __sys_sendmmsg(fd, mmsg: (struct mmsghdr __user *)mmsg, vlen, |
361 | flags: flags | MSG_CMSG_COMPAT, forbid_cmsg_compat: false); |
362 | } |
363 | |
364 | COMPAT_SYSCALL_DEFINE4(sendmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, |
365 | unsigned int, vlen, unsigned int, flags) |
366 | { |
367 | return __compat_sys_sendmmsg(fd, mmsg, vlen, flags); |
368 | } |
369 | |
370 | static inline long __compat_sys_recvmsg(int fd, |
371 | struct compat_msghdr __user *msg, |
372 | unsigned int flags) |
373 | { |
374 | return __sys_recvmsg(fd, msg: (struct user_msghdr __user *)msg, |
375 | flags: flags | MSG_CMSG_COMPAT, forbid_cmsg_compat: false); |
376 | } |
377 | |
378 | COMPAT_SYSCALL_DEFINE3(recvmsg, int, fd, struct compat_msghdr __user *, msg, |
379 | unsigned int, flags) |
380 | { |
381 | return __compat_sys_recvmsg(fd, msg, flags); |
382 | } |
383 | |
384 | static inline long __compat_sys_recvfrom(int fd, void __user *buf, |
385 | compat_size_t len, unsigned int flags, |
386 | struct sockaddr __user *addr, |
387 | int __user *addrlen) |
388 | { |
389 | return __sys_recvfrom(fd, ubuf: buf, size: len, flags: flags | MSG_CMSG_COMPAT, addr, |
390 | addr_len: addrlen); |
391 | } |
392 | |
393 | COMPAT_SYSCALL_DEFINE4(recv, int, fd, void __user *, buf, compat_size_t, len, unsigned int, flags) |
394 | { |
395 | return __compat_sys_recvfrom(fd, buf, len, flags, NULL, NULL); |
396 | } |
397 | |
398 | COMPAT_SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, buf, compat_size_t, len, |
399 | unsigned int, flags, struct sockaddr __user *, addr, |
400 | int __user *, addrlen) |
401 | { |
402 | return __compat_sys_recvfrom(fd, buf, len, flags, addr, addrlen); |
403 | } |
404 | |
405 | COMPAT_SYSCALL_DEFINE5(recvmmsg_time64, int, fd, struct compat_mmsghdr __user *, mmsg, |
406 | unsigned int, vlen, unsigned int, flags, |
407 | struct __kernel_timespec __user *, timeout) |
408 | { |
409 | return __sys_recvmmsg(fd, mmsg: (struct mmsghdr __user *)mmsg, vlen, |
410 | flags: flags | MSG_CMSG_COMPAT, timeout, NULL); |
411 | } |
412 | |
413 | #ifdef CONFIG_COMPAT_32BIT_TIME |
414 | COMPAT_SYSCALL_DEFINE5(recvmmsg_time32, int, fd, struct compat_mmsghdr __user *, mmsg, |
415 | unsigned int, vlen, unsigned int, flags, |
416 | struct old_timespec32 __user *, timeout) |
417 | { |
418 | return __sys_recvmmsg(fd, mmsg: (struct mmsghdr __user *)mmsg, vlen, |
419 | flags: flags | MSG_CMSG_COMPAT, NULL, timeout32: timeout); |
420 | } |
421 | #endif |
422 | |
423 | COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) |
424 | { |
425 | u32 a[AUDITSC_ARGS]; |
426 | unsigned int len; |
427 | u32 a0, a1; |
428 | int ret; |
429 | |
430 | if (call < SYS_SOCKET || call > SYS_SENDMMSG) |
431 | return -EINVAL; |
432 | len = nas[call]; |
433 | if (len > sizeof(a)) |
434 | return -EINVAL; |
435 | |
436 | if (copy_from_user(to: a, from: args, n: len)) |
437 | return -EFAULT; |
438 | |
439 | ret = audit_socketcall_compat(nargs: len / sizeof(a[0]), args: a); |
440 | if (ret) |
441 | return ret; |
442 | |
443 | a0 = a[0]; |
444 | a1 = a[1]; |
445 | |
446 | switch (call) { |
447 | case SYS_SOCKET: |
448 | ret = __sys_socket(family: a0, type: a1, protocol: a[2]); |
449 | break; |
450 | case SYS_BIND: |
451 | ret = __sys_bind(fd: a0, umyaddr: compat_ptr(uptr: a1), addrlen: a[2]); |
452 | break; |
453 | case SYS_CONNECT: |
454 | ret = __sys_connect(fd: a0, uservaddr: compat_ptr(uptr: a1), addrlen: a[2]); |
455 | break; |
456 | case SYS_LISTEN: |
457 | ret = __sys_listen(fd: a0, backlog: a1); |
458 | break; |
459 | case SYS_ACCEPT: |
460 | ret = __sys_accept4(fd: a0, upeer_sockaddr: compat_ptr(uptr: a1), upeer_addrlen: compat_ptr(uptr: a[2]), flags: 0); |
461 | break; |
462 | case SYS_GETSOCKNAME: |
463 | ret = __sys_getsockname(fd: a0, usockaddr: compat_ptr(uptr: a1), usockaddr_len: compat_ptr(uptr: a[2])); |
464 | break; |
465 | case SYS_GETPEERNAME: |
466 | ret = __sys_getpeername(fd: a0, usockaddr: compat_ptr(uptr: a1), usockaddr_len: compat_ptr(uptr: a[2])); |
467 | break; |
468 | case SYS_SOCKETPAIR: |
469 | ret = __sys_socketpair(family: a0, type: a1, protocol: a[2], usockvec: compat_ptr(uptr: a[3])); |
470 | break; |
471 | case SYS_SEND: |
472 | ret = __sys_sendto(fd: a0, buff: compat_ptr(uptr: a1), len: a[2], flags: a[3], NULL, addr_len: 0); |
473 | break; |
474 | case SYS_SENDTO: |
475 | ret = __sys_sendto(fd: a0, buff: compat_ptr(uptr: a1), len: a[2], flags: a[3], |
476 | addr: compat_ptr(uptr: a[4]), addr_len: a[5]); |
477 | break; |
478 | case SYS_RECV: |
479 | ret = __compat_sys_recvfrom(fd: a0, buf: compat_ptr(uptr: a1), len: a[2], flags: a[3], |
480 | NULL, NULL); |
481 | break; |
482 | case SYS_RECVFROM: |
483 | ret = __compat_sys_recvfrom(fd: a0, buf: compat_ptr(uptr: a1), len: a[2], flags: a[3], |
484 | addr: compat_ptr(uptr: a[4]), |
485 | addrlen: compat_ptr(uptr: a[5])); |
486 | break; |
487 | case SYS_SHUTDOWN: |
488 | ret = __sys_shutdown(fd: a0, how: a1); |
489 | break; |
490 | case SYS_SETSOCKOPT: |
491 | ret = __sys_setsockopt(fd: a0, level: a1, optname: a[2], optval: compat_ptr(uptr: a[3]), optlen: a[4]); |
492 | break; |
493 | case SYS_GETSOCKOPT: |
494 | ret = __sys_getsockopt(fd: a0, level: a1, optname: a[2], optval: compat_ptr(uptr: a[3]), |
495 | optlen: compat_ptr(uptr: a[4])); |
496 | break; |
497 | case SYS_SENDMSG: |
498 | ret = __compat_sys_sendmsg(fd: a0, msg: compat_ptr(uptr: a1), flags: a[2]); |
499 | break; |
500 | case SYS_SENDMMSG: |
501 | ret = __compat_sys_sendmmsg(fd: a0, mmsg: compat_ptr(uptr: a1), vlen: a[2], flags: a[3]); |
502 | break; |
503 | case SYS_RECVMSG: |
504 | ret = __compat_sys_recvmsg(fd: a0, msg: compat_ptr(uptr: a1), flags: a[2]); |
505 | break; |
506 | case SYS_RECVMMSG: |
507 | ret = __sys_recvmmsg(fd: a0, mmsg: compat_ptr(uptr: a1), vlen: a[2], |
508 | flags: a[3] | MSG_CMSG_COMPAT, NULL, |
509 | timeout32: compat_ptr(uptr: a[4])); |
510 | break; |
511 | case SYS_ACCEPT4: |
512 | ret = __sys_accept4(fd: a0, upeer_sockaddr: compat_ptr(uptr: a1), upeer_addrlen: compat_ptr(uptr: a[2]), flags: a[3]); |
513 | break; |
514 | default: |
515 | ret = -EINVAL; |
516 | break; |
517 | } |
518 | return ret; |
519 | } |
520 | |