xref: /linux/net/compat.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * 32bit Socket syscall emulation. Based on arch/sparc64/kernel/sys_sparc32.c.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2000		VA Linux Co
61da177e4SLinus Torvalds  * Copyright (C) 2000		Don Dugger <n0ano@valinux.com>
71da177e4SLinus Torvalds  * Copyright (C) 1999 		Arun Sharma <arun.sharma@intel.com>
81da177e4SLinus Torvalds  * Copyright (C) 1997,1998 	Jakub Jelinek (jj@sunsite.mff.cuni.cz)
91da177e4SLinus Torvalds  * Copyright (C) 1997 		David S. Miller (davem@caip.rutgers.edu)
101da177e4SLinus Torvalds  * Copyright (C) 2000		Hewlett-Packard Co.
111da177e4SLinus Torvalds  * Copyright (C) 2000		David Mosberger-Tang <davidm@hpl.hp.com>
121da177e4SLinus Torvalds  * Copyright (C) 2000,2001	Andi Kleen, SuSE Labs
131da177e4SLinus Torvalds  */
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #include <linux/kernel.h>
165a0e3ad6STejun Heo #include <linux/gfp.h>
171da177e4SLinus Torvalds #include <linux/fs.h>
181da177e4SLinus Torvalds #include <linux/types.h>
191da177e4SLinus Torvalds #include <linux/file.h>
201da177e4SLinus Torvalds #include <linux/icmpv6.h>
211da177e4SLinus Torvalds #include <linux/socket.h>
221da177e4SLinus Torvalds #include <linux/syscalls.h>
231da177e4SLinus Torvalds #include <linux/filter.h>
241da177e4SLinus Torvalds #include <linux/compat.h>
251da177e4SLinus Torvalds #include <linux/security.h>
2662bc306eSRichard Guy Briggs #include <linux/audit.h>
27bc3b2d7fSPaul Gortmaker #include <linux/export.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include <net/scm.h>
301da177e4SLinus Torvalds #include <net/sock.h>
31dae50295SDavid L Stevens #include <net/ip.h>
32dae50295SDavid L Stevens #include <net/ipv6.h>
337c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
341da177e4SLinus Torvalds #include <net/compat.h>
351da177e4SLinus Torvalds 
__get_compat_msghdr(struct msghdr * kmsg,struct compat_msghdr * msg,struct sockaddr __user ** save_addr)360a384abfSJens Axboe int __get_compat_msghdr(struct msghdr *kmsg,
3772c531f8SDylan Yudaken 			struct compat_msghdr *msg,
3872c531f8SDylan Yudaken 			struct sockaddr __user **save_addr)
391da177e4SLinus Torvalds {
4008adb7daSAl Viro 	ssize_t err;
411da177e4SLinus Torvalds 
4272c531f8SDylan Yudaken 	kmsg->msg_flags = msg->msg_flags;
4372c531f8SDylan Yudaken 	kmsg->msg_namelen = msg->msg_namelen;
4491edd096SCatalin Marinas 
4572c531f8SDylan Yudaken 	if (!msg->msg_name)
4691edd096SCatalin Marinas 		kmsg->msg_namelen = 0;
4791edd096SCatalin Marinas 
4891edd096SCatalin Marinas 	if (kmsg->msg_namelen < 0)
4991edd096SCatalin Marinas 		return -EINVAL;
5091edd096SCatalin Marinas 
511661bf36SDan Carpenter 	if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
52db31c55aSDan Carpenter 		kmsg->msg_namelen = sizeof(struct sockaddr_storage);
535da028a8SAl Viro 
541f466e1fSChristoph Hellwig 	kmsg->msg_control_is_user = true;
55d547c1b7STetsuo Handa 	kmsg->msg_get_inq = 0;
5672c531f8SDylan Yudaken 	kmsg->msg_control_user = compat_ptr(msg->msg_control);
5772c531f8SDylan Yudaken 	kmsg->msg_controllen = msg->msg_controllen;
581da177e4SLinus Torvalds 
5908adb7daSAl Viro 	if (save_addr)
6072c531f8SDylan Yudaken 		*save_addr = compat_ptr(msg->msg_name);
611da177e4SLinus Torvalds 
6272c531f8SDylan Yudaken 	if (msg->msg_name && kmsg->msg_namelen) {
6308adb7daSAl Viro 		if (!save_addr) {
6472c531f8SDylan Yudaken 			err = move_addr_to_kernel(compat_ptr(msg->msg_name),
6508adb7daSAl Viro 						  kmsg->msg_namelen,
6608adb7daSAl Viro 						  kmsg->msg_name);
671da177e4SLinus Torvalds 			if (err < 0)
681da177e4SLinus Torvalds 				return err;
691da177e4SLinus Torvalds 		}
7040eea803SAndrey Ryabinin 	} else {
7108adb7daSAl Viro 		kmsg->msg_name = NULL;
7208adb7daSAl Viro 		kmsg->msg_namelen = 0;
7340eea803SAndrey Ryabinin 	}
741da177e4SLinus Torvalds 
7572c531f8SDylan Yudaken 	if (msg->msg_iovlen > UIO_MAXIOV)
7608449320SAl Viro 		return -EMSGSIZE;
771da177e4SLinus Torvalds 
780345f931Stadeusz.struk@intel.com 	kmsg->msg_iocb = NULL;
797c701d92SPavel Begunkov 	kmsg->msg_ubuf = NULL;
800a384abfSJens Axboe 	return 0;
810a384abfSJens Axboe }
820345f931Stadeusz.struk@intel.com 
get_compat_msghdr(struct msghdr * kmsg,struct compat_msghdr __user * umsg,struct sockaddr __user ** save_addr,struct iovec ** iov)830a384abfSJens Axboe int get_compat_msghdr(struct msghdr *kmsg,
840a384abfSJens Axboe 		      struct compat_msghdr __user *umsg,
850a384abfSJens Axboe 		      struct sockaddr __user **save_addr,
860a384abfSJens Axboe 		      struct iovec **iov)
870a384abfSJens Axboe {
8872c531f8SDylan Yudaken 	struct compat_msghdr msg;
890a384abfSJens Axboe 	ssize_t err;
900a384abfSJens Axboe 
9172c531f8SDylan Yudaken 	if (copy_from_user(&msg, umsg, sizeof(*umsg)))
9272c531f8SDylan Yudaken 		return -EFAULT;
9372c531f8SDylan Yudaken 
944f6a94d3SJens Axboe 	err = __get_compat_msghdr(kmsg, &msg, save_addr);
950a384abfSJens Axboe 	if (err)
960a384abfSJens Axboe 		return err;
970a384abfSJens Axboe 
98de4eda9dSAl Viro 	err = import_iovec(save_addr ? ITER_DEST : ITER_SOURCE,
99de4eda9dSAl Viro 			   compat_ptr(msg.msg_iov), msg.msg_iovlen,
10089cd35c5SChristoph Hellwig 			   UIO_FASTIOV, iov, &kmsg->msg_iter);
10187e5e6daSJens Axboe 	return err < 0 ? err : 0;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds /* Bleech... */
1051da177e4SLinus Torvalds #define CMSG_COMPAT_ALIGN(len)	ALIGN((len), sizeof(s32))
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds #define CMSG_COMPAT_DATA(cmsg)				\
1081ff8cebfSyuan linyu 	((void __user *)((char __user *)(cmsg) + sizeof(struct compat_cmsghdr)))
1091da177e4SLinus Torvalds #define CMSG_COMPAT_SPACE(len)				\
1101ff8cebfSyuan linyu 	(sizeof(struct compat_cmsghdr) + CMSG_COMPAT_ALIGN(len))
1111da177e4SLinus Torvalds #define CMSG_COMPAT_LEN(len)				\
1121ff8cebfSyuan linyu 	(sizeof(struct compat_cmsghdr) + (len))
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds #define CMSG_COMPAT_FIRSTHDR(msg)			\
1151da177e4SLinus Torvalds 	(((msg)->msg_controllen) >= sizeof(struct compat_cmsghdr) ?	\
116c39ef213SKevin Brodsky 	 (struct compat_cmsghdr __user *)((msg)->msg_control_user) :	\
1171da177e4SLinus Torvalds 	 (struct compat_cmsghdr __user *)NULL)
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds #define CMSG_COMPAT_OK(ucmlen, ucmsg, mhdr) \
1201da177e4SLinus Torvalds 	((ucmlen) >= sizeof(struct compat_cmsghdr) && \
1211da177e4SLinus Torvalds 	 (ucmlen) <= (unsigned long) \
1221da177e4SLinus Torvalds 	 ((mhdr)->msg_controllen - \
1231f466e1fSChristoph Hellwig 	  ((char __user *)(ucmsg) - (char __user *)(mhdr)->msg_control_user)))
1241da177e4SLinus Torvalds 
cmsg_compat_nxthdr(struct msghdr * msg,struct compat_cmsghdr __user * cmsg,int cmsg_len)1251da177e4SLinus Torvalds static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *msg,
1261da177e4SLinus Torvalds 		struct compat_cmsghdr __user *cmsg, int cmsg_len)
1271da177e4SLinus Torvalds {
1281da177e4SLinus Torvalds 	char __user *ptr = (char __user *)cmsg + CMSG_COMPAT_ALIGN(cmsg_len);
129c39ef213SKevin Brodsky 	if ((unsigned long)(ptr + 1 - (char __user *)msg->msg_control_user) >
1301da177e4SLinus Torvalds 			msg->msg_controllen)
1311da177e4SLinus Torvalds 		return NULL;
1321da177e4SLinus Torvalds 	return (struct compat_cmsghdr __user *)ptr;
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds /* There is a lot of hair here because the alignment rules (and
1361da177e4SLinus Torvalds  * thus placement) of cmsg headers and length are different for
1371da177e4SLinus Torvalds  * 32-bit apps.  -DaveM
1381da177e4SLinus Torvalds  */
cmsghdr_from_user_compat_to_kern(struct msghdr * kmsg,struct sock * sk,unsigned char * stackbuf,int stackbuf_size)1398920e8f9SAl Viro int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk,
1401da177e4SLinus Torvalds 			       unsigned char *stackbuf, int stackbuf_size)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds 	struct compat_cmsghdr __user *ucmsg;
1431da177e4SLinus Torvalds 	struct cmsghdr *kcmsg, *kcmsg_base;
1441da177e4SLinus Torvalds 	compat_size_t ucmlen;
1451da177e4SLinus Torvalds 	__kernel_size_t kcmlen, tmp;
1468920e8f9SAl Viro 	int err = -EFAULT;
1471da177e4SLinus Torvalds 
148ac4340fcSDavid S. Miller 	BUILD_BUG_ON(sizeof(struct compat_cmsghdr) !=
149ac4340fcSDavid S. Miller 		     CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)));
150ac4340fcSDavid S. Miller 
1511da177e4SLinus Torvalds 	kcmlen = 0;
1521da177e4SLinus Torvalds 	kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf;
1531da177e4SLinus Torvalds 	ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg);
1541da177e4SLinus Torvalds 	while (ucmsg != NULL) {
1551da177e4SLinus Torvalds 		if (get_user(ucmlen, &ucmsg->cmsg_len))
1561da177e4SLinus Torvalds 			return -EFAULT;
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 		/* Catch bogons. */
1591da177e4SLinus Torvalds 		if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg))
1601da177e4SLinus Torvalds 			return -EINVAL;
1611da177e4SLinus Torvalds 
1621ff8cebfSyuan linyu 		tmp = ((ucmlen - sizeof(*ucmsg)) + sizeof(struct cmsghdr));
1638920e8f9SAl Viro 		tmp = CMSG_ALIGN(tmp);
1641da177e4SLinus Torvalds 		kcmlen += tmp;
1651da177e4SLinus Torvalds 		ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen);
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 	if (kcmlen == 0)
1681da177e4SLinus Torvalds 		return -EINVAL;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	/* The kcmlen holds the 64-bit version of the control length.
1711da177e4SLinus Torvalds 	 * It may not be modified as we do not stick it into the kmsg
1721da177e4SLinus Torvalds 	 * until we have successfully copied over all of the data
1731da177e4SLinus Torvalds 	 * from the user.
1741da177e4SLinus Torvalds 	 */
1751da177e4SLinus Torvalds 	if (kcmlen > stackbuf_size)
1768920e8f9SAl Viro 		kcmsg_base = kcmsg = sock_kmalloc(sk, kcmlen, GFP_KERNEL);
1771da177e4SLinus Torvalds 	if (kcmsg == NULL)
17849251cd0SZheng Yongjun 		return -ENOMEM;
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	/* Now copy them over neatly. */
1811da177e4SLinus Torvalds 	memset(kcmsg, 0, kcmlen);
1821da177e4SLinus Torvalds 	ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg);
1831da177e4SLinus Torvalds 	while (ucmsg != NULL) {
184547ce4cfSAl Viro 		struct compat_cmsghdr cmsg;
185547ce4cfSAl Viro 		if (copy_from_user(&cmsg, ucmsg, sizeof(cmsg)))
1868920e8f9SAl Viro 			goto Efault;
187547ce4cfSAl Viro 		if (!CMSG_COMPAT_OK(cmsg.cmsg_len, ucmsg, kmsg))
1888920e8f9SAl Viro 			goto Einval;
189547ce4cfSAl Viro 		tmp = ((cmsg.cmsg_len - sizeof(*ucmsg)) + sizeof(struct cmsghdr));
1908920e8f9SAl Viro 		if ((char *)kcmsg_base + kcmlen - (char *)kcmsg < CMSG_ALIGN(tmp))
1918920e8f9SAl Viro 			goto Einval;
1921da177e4SLinus Torvalds 		kcmsg->cmsg_len = tmp;
193547ce4cfSAl Viro 		kcmsg->cmsg_level = cmsg.cmsg_level;
194547ce4cfSAl Viro 		kcmsg->cmsg_type = cmsg.cmsg_type;
1958920e8f9SAl Viro 		tmp = CMSG_ALIGN(tmp);
196547ce4cfSAl Viro 		if (copy_from_user(CMSG_DATA(kcmsg),
1971da177e4SLinus Torvalds 				   CMSG_COMPAT_DATA(ucmsg),
198547ce4cfSAl Viro 				   (cmsg.cmsg_len - sizeof(*ucmsg))))
1998920e8f9SAl Viro 			goto Efault;
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds 		/* Advance. */
2028920e8f9SAl Viro 		kcmsg = (struct cmsghdr *)((char *)kcmsg + tmp);
203181964e6SAl Viro 		ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, cmsg.cmsg_len);
2041da177e4SLinus Torvalds 	}
2051da177e4SLinus Torvalds 
206c2a64bb9SMeng Xu 	/*
207c2a64bb9SMeng Xu 	 * check the length of messages copied in is the same as the
208c2a64bb9SMeng Xu 	 * what we get from the first loop
209c2a64bb9SMeng Xu 	 */
210c2a64bb9SMeng Xu 	if ((char *)kcmsg - (char *)kcmsg_base != kcmlen)
211c2a64bb9SMeng Xu 		goto Einval;
212c2a64bb9SMeng Xu 
2131da177e4SLinus Torvalds 	/* Ok, looks like we made it.  Hook it up and return success. */
21460daf8d4SKevin Brodsky 	kmsg->msg_control_is_user = false;
2151da177e4SLinus Torvalds 	kmsg->msg_control = kcmsg_base;
2161da177e4SLinus Torvalds 	kmsg->msg_controllen = kcmlen;
2171da177e4SLinus Torvalds 	return 0;
2181da177e4SLinus Torvalds 
2198920e8f9SAl Viro Einval:
2208920e8f9SAl Viro 	err = -EINVAL;
2218920e8f9SAl Viro Efault:
2221da177e4SLinus Torvalds 	if (kcmsg_base != (struct cmsghdr *)stackbuf)
2238920e8f9SAl Viro 		sock_kfree_s(sk, kcmsg_base, kcmlen);
2248920e8f9SAl Viro 	return err;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
put_cmsg_compat(struct msghdr * kmsg,int level,int type,int len,void * data)2271da177e4SLinus Torvalds int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data)
2281da177e4SLinus Torvalds {
229c39ef213SKevin Brodsky 	struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control_user;
2301da177e4SLinus Torvalds 	struct compat_cmsghdr cmhdr;
23113c6ee2aSDeepa Dinamani 	struct old_timeval32 ctv;
23213c6ee2aSDeepa Dinamani 	struct old_timespec32 cts[3];
2331da177e4SLinus Torvalds 	int cmlen;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	if (cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
2361da177e4SLinus Torvalds 		kmsg->msg_flags |= MSG_CTRUNC;
2371da177e4SLinus Torvalds 		return 0; /* XXX: return error? check spec. */
2381da177e4SLinus Torvalds 	}
2391da177e4SLinus Torvalds 
240ee4fa23cSH. J. Lu 	if (!COMPAT_USE_64BIT_TIME) {
2417f1bc6e9SDeepa Dinamani 		if (level == SOL_SOCKET && type == SO_TIMESTAMP_OLD) {
24213c6ee2aSDeepa Dinamani 			struct __kernel_old_timeval *tv = (struct __kernel_old_timeval *)data;
2431da177e4SLinus Torvalds 			ctv.tv_sec = tv->tv_sec;
2441da177e4SLinus Torvalds 			ctv.tv_usec = tv->tv_usec;
2451da177e4SLinus Torvalds 			data = &ctv;
24692f37fd2SEric Dumazet 			len = sizeof(ctv);
24792f37fd2SEric Dumazet 		}
24820d49473SPatrick Ohly 		if (level == SOL_SOCKET &&
2497f1bc6e9SDeepa Dinamani 		    (type == SO_TIMESTAMPNS_OLD || type == SO_TIMESTAMPING_OLD)) {
2507f1bc6e9SDeepa Dinamani 			int count = type == SO_TIMESTAMPNS_OLD ? 1 : 3;
25120d49473SPatrick Ohly 			int i;
252df1b4ba9SArnd Bergmann 			struct __kernel_old_timespec *ts = data;
25320d49473SPatrick Ohly 			for (i = 0; i < count; i++) {
25420d49473SPatrick Ohly 				cts[i].tv_sec = ts[i].tv_sec;
25520d49473SPatrick Ohly 				cts[i].tv_nsec = ts[i].tv_nsec;
25620d49473SPatrick Ohly 			}
25792f37fd2SEric Dumazet 			data = &cts;
25820d49473SPatrick Ohly 			len = sizeof(cts[0]) * count;
2591da177e4SLinus Torvalds 		}
260ee4fa23cSH. J. Lu 	}
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds 	cmlen = CMSG_COMPAT_LEN(len);
2631da177e4SLinus Torvalds 	if (kmsg->msg_controllen < cmlen) {
2641da177e4SLinus Torvalds 		kmsg->msg_flags |= MSG_CTRUNC;
2651da177e4SLinus Torvalds 		cmlen = kmsg->msg_controllen;
2661da177e4SLinus Torvalds 	}
2671da177e4SLinus Torvalds 	cmhdr.cmsg_level = level;
2681da177e4SLinus Torvalds 	cmhdr.cmsg_type = type;
2691da177e4SLinus Torvalds 	cmhdr.cmsg_len = cmlen;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
2721da177e4SLinus Torvalds 		return -EFAULT;
2731da177e4SLinus Torvalds 	if (copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr)))
2741da177e4SLinus Torvalds 		return -EFAULT;
2751da177e4SLinus Torvalds 	cmlen = CMSG_COMPAT_SPACE(len);
2761ac70e7aSWei Yongjun 	if (kmsg->msg_controllen < cmlen)
2771ac70e7aSWei Yongjun 		cmlen = kmsg->msg_controllen;
278c39ef213SKevin Brodsky 	kmsg->msg_control_user += cmlen;
2791da177e4SLinus Torvalds 	kmsg->msg_controllen -= cmlen;
2801da177e4SLinus Torvalds 	return 0;
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds 
scm_max_fds_compat(struct msghdr * msg)283c0029de5SKees Cook static int scm_max_fds_compat(struct msghdr *msg)
2841da177e4SLinus Torvalds {
285c0029de5SKees Cook 	if (msg->msg_controllen <= sizeof(struct compat_cmsghdr))
286c0029de5SKees Cook 		return 0;
287c0029de5SKees Cook 	return (msg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int);
288c0029de5SKees Cook }
289c0029de5SKees Cook 
scm_detach_fds_compat(struct msghdr * msg,struct scm_cookie * scm)290c0029de5SKees Cook void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm)
291c0029de5SKees Cook {
292c0029de5SKees Cook 	struct compat_cmsghdr __user *cm =
293c39ef213SKevin Brodsky 		(struct compat_cmsghdr __user *)msg->msg_control_user;
294c0029de5SKees Cook 	unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
295c0029de5SKees Cook 	int fdmax = min_t(int, scm_max_fds_compat(msg), scm->fp->count);
29616b89f69SKees Cook 	int __user *cmsg_data = CMSG_COMPAT_DATA(cm);
2971da177e4SLinus Torvalds 	int err = 0, i;
2981da177e4SLinus Torvalds 
299c0029de5SKees Cook 	for (i = 0; i < fdmax; i++) {
300*eac9189cSChristian Brauner 		err = scm_recv_one_fd(scm->fp->fp[i], cmsg_data + i, o_flags);
3011da177e4SLinus Torvalds 		if (err < 0)
3021da177e4SLinus Torvalds 			break;
3031da177e4SLinus Torvalds 	}
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	if (i > 0) {
3061da177e4SLinus Torvalds 		int cmlen = CMSG_COMPAT_LEN(i * sizeof(int));
307c0029de5SKees Cook 
3081da177e4SLinus Torvalds 		err = put_user(SOL_SOCKET, &cm->cmsg_level);
3091da177e4SLinus Torvalds 		if (!err)
3101da177e4SLinus Torvalds 			err = put_user(SCM_RIGHTS, &cm->cmsg_type);
3111da177e4SLinus Torvalds 		if (!err)
3121da177e4SLinus Torvalds 			err = put_user(cmlen, &cm->cmsg_len);
3131da177e4SLinus Torvalds 		if (!err) {
3141da177e4SLinus Torvalds 			cmlen = CMSG_COMPAT_SPACE(i * sizeof(int));
315c0029de5SKees Cook 			if (msg->msg_controllen < cmlen)
316c0029de5SKees Cook 				cmlen = msg->msg_controllen;
317c39ef213SKevin Brodsky 			msg->msg_control_user += cmlen;
318c0029de5SKees Cook 			msg->msg_controllen -= cmlen;
3191da177e4SLinus Torvalds 		}
3201da177e4SLinus Torvalds 	}
321c0029de5SKees Cook 
322c0029de5SKees Cook 	if (i < scm->fp->count || (scm->fp->count && fdmax <= 0))
323c0029de5SKees Cook 		msg->msg_flags |= MSG_CTRUNC;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	/*
326c0029de5SKees Cook 	 * All of the files that fit in the message have had their usage counts
327c0029de5SKees Cook 	 * incremented, so we just free the list.
3281da177e4SLinus Torvalds 	 */
3291da177e4SLinus Torvalds 	__scm_destroy(scm);
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds /* Argument list sizes for compat_sys_socketcall */
3331da177e4SLinus Torvalds #define AL(x) ((x) * sizeof(u32))
334228e548eSAnton Blanchard static unsigned char nas[21] = {
335c6d409cfSEric Dumazet 	AL(0), AL(3), AL(3), AL(3), AL(2), AL(3),
3361da177e4SLinus Torvalds 	AL(3), AL(3), AL(4), AL(4), AL(4), AL(6),
337aaca0bdcSUlrich Drepper 	AL(6), AL(2), AL(5), AL(5), AL(3), AL(3),
338228e548eSAnton Blanchard 	AL(4), AL(5), AL(4)
339c6d409cfSEric Dumazet };
3401da177e4SLinus Torvalds #undef AL
3411da177e4SLinus Torvalds 
__compat_sys_sendmsg(int fd,struct compat_msghdr __user * msg,unsigned int flags)3426df35465SDominik Brodowski static inline long __compat_sys_sendmsg(int fd,
3436df35465SDominik Brodowski 					struct compat_msghdr __user *msg,
3446df35465SDominik Brodowski 					unsigned int flags)
3451da177e4SLinus Torvalds {
346e1834a32SDominik Brodowski 	return __sys_sendmsg(fd, (struct user_msghdr __user *)msg,
347e1834a32SDominik Brodowski 			     flags | MSG_CMSG_COMPAT, false);
3481da177e4SLinus Torvalds }
3491da177e4SLinus Torvalds 
COMPAT_SYSCALL_DEFINE3(sendmsg,int,fd,struct compat_msghdr __user *,msg,unsigned int,flags)3506df35465SDominik Brodowski COMPAT_SYSCALL_DEFINE3(sendmsg, int, fd, struct compat_msghdr __user *, msg,
3516df35465SDominik Brodowski 		       unsigned int, flags)
3526df35465SDominik Brodowski {
3536df35465SDominik Brodowski 	return __compat_sys_sendmsg(fd, msg, flags);
3546df35465SDominik Brodowski }
3556df35465SDominik Brodowski 
__compat_sys_sendmmsg(int fd,struct compat_mmsghdr __user * mmsg,unsigned int vlen,unsigned int flags)3566df35465SDominik Brodowski static inline long __compat_sys_sendmmsg(int fd,
3576df35465SDominik Brodowski 					 struct compat_mmsghdr __user *mmsg,
3586df35465SDominik Brodowski 					 unsigned int vlen, unsigned int flags)
3596df35465SDominik Brodowski {
3606df35465SDominik Brodowski 	return __sys_sendmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
3616df35465SDominik Brodowski 			      flags | MSG_CMSG_COMPAT, false);
3626df35465SDominik Brodowski }
3636df35465SDominik Brodowski 
COMPAT_SYSCALL_DEFINE4(sendmmsg,int,fd,struct compat_mmsghdr __user *,mmsg,unsigned int,vlen,unsigned int,flags)364361d93c4SHeiko Carstens COMPAT_SYSCALL_DEFINE4(sendmmsg, int, fd, struct compat_mmsghdr __user *, mmsg,
365361d93c4SHeiko Carstens 		       unsigned int, vlen, unsigned int, flags)
366228e548eSAnton Blanchard {
3676df35465SDominik Brodowski 	return __compat_sys_sendmmsg(fd, mmsg, vlen, flags);
368228e548eSAnton Blanchard }
369228e548eSAnton Blanchard 
__compat_sys_recvmsg(int fd,struct compat_msghdr __user * msg,unsigned int flags)3706df35465SDominik Brodowski static inline long __compat_sys_recvmsg(int fd,
3716df35465SDominik Brodowski 					struct compat_msghdr __user *msg,
3726df35465SDominik Brodowski 					unsigned int flags)
3731da177e4SLinus Torvalds {
374e1834a32SDominik Brodowski 	return __sys_recvmsg(fd, (struct user_msghdr __user *)msg,
375e1834a32SDominik Brodowski 			     flags | MSG_CMSG_COMPAT, false);
3761da177e4SLinus Torvalds }
3771da177e4SLinus Torvalds 
COMPAT_SYSCALL_DEFINE3(recvmsg,int,fd,struct compat_msghdr __user *,msg,unsigned int,flags)3786df35465SDominik Brodowski COMPAT_SYSCALL_DEFINE3(recvmsg, int, fd, struct compat_msghdr __user *, msg,
3796df35465SDominik Brodowski 		       unsigned int, flags)
3806df35465SDominik Brodowski {
3816df35465SDominik Brodowski 	return __compat_sys_recvmsg(fd, msg, flags);
3826df35465SDominik Brodowski }
3836df35465SDominik Brodowski 
__compat_sys_recvfrom(int fd,void __user * buf,compat_size_t len,unsigned int flags,struct sockaddr __user * addr,int __user * addrlen)384fd4e82f5SDominik Brodowski static inline long __compat_sys_recvfrom(int fd, void __user *buf,
385fd4e82f5SDominik Brodowski 					 compat_size_t len, unsigned int flags,
386fd4e82f5SDominik Brodowski 					 struct sockaddr __user *addr,
387fd4e82f5SDominik Brodowski 					 int __user *addrlen)
388fd4e82f5SDominik Brodowski {
389fd4e82f5SDominik Brodowski 	return __sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr,
390fd4e82f5SDominik Brodowski 			      addrlen);
391fd4e82f5SDominik Brodowski }
392fd4e82f5SDominik Brodowski 
COMPAT_SYSCALL_DEFINE4(recv,int,fd,void __user *,buf,compat_size_t,len,unsigned int,flags)3933a49a0f7SHeiko Carstens COMPAT_SYSCALL_DEFINE4(recv, int, fd, void __user *, buf, compat_size_t, len, unsigned int, flags)
3941dacc76dSJohannes Berg {
395fd4e82f5SDominik Brodowski 	return __compat_sys_recvfrom(fd, buf, len, flags, NULL, NULL);
3961dacc76dSJohannes Berg }
3971dacc76dSJohannes Berg 
COMPAT_SYSCALL_DEFINE6(recvfrom,int,fd,void __user *,buf,compat_size_t,len,unsigned int,flags,struct sockaddr __user *,addr,int __user *,addrlen)3983a49a0f7SHeiko Carstens COMPAT_SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, buf, compat_size_t, len,
3993a49a0f7SHeiko Carstens 		       unsigned int, flags, struct sockaddr __user *, addr,
4003a49a0f7SHeiko Carstens 		       int __user *, addrlen)
4011dacc76dSJohannes Berg {
402fd4e82f5SDominik Brodowski 	return __compat_sys_recvfrom(fd, buf, len, flags, addr, addrlen);
4031dacc76dSJohannes Berg }
4041dacc76dSJohannes Berg 
COMPAT_SYSCALL_DEFINE5(recvmmsg_time64,int,fd,struct compat_mmsghdr __user *,mmsg,unsigned int,vlen,unsigned int,flags,struct __kernel_timespec __user *,timeout)405e11d4284SArnd Bergmann COMPAT_SYSCALL_DEFINE5(recvmmsg_time64, int, fd, struct compat_mmsghdr __user *, mmsg,
406e11d4284SArnd Bergmann 		       unsigned int, vlen, unsigned int, flags,
407e11d4284SArnd Bergmann 		       struct __kernel_timespec __user *, timeout)
408a2e27255SArnaldo Carvalho de Melo {
4095b23136bSJean-Mickael Guerin 	return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
410e11d4284SArnd Bergmann 			      flags | MSG_CMSG_COMPAT, timeout, NULL);
411a2e27255SArnaldo Carvalho de Melo }
412a2e27255SArnaldo Carvalho de Melo 
413e11d4284SArnd Bergmann #ifdef CONFIG_COMPAT_32BIT_TIME
COMPAT_SYSCALL_DEFINE5(recvmmsg_time32,int,fd,struct compat_mmsghdr __user *,mmsg,unsigned int,vlen,unsigned int,flags,struct old_timespec32 __user *,timeout)4148dabe724SArnd Bergmann COMPAT_SYSCALL_DEFINE5(recvmmsg_time32, int, fd, struct compat_mmsghdr __user *, mmsg,
415157b334aSDominik Brodowski 		       unsigned int, vlen, unsigned int, flags,
4169afc5eeeSArnd Bergmann 		       struct old_timespec32 __user *, timeout)
417157b334aSDominik Brodowski {
418e11d4284SArnd Bergmann 	return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
419e11d4284SArnd Bergmann 			      flags | MSG_CMSG_COMPAT, NULL, timeout);
420157b334aSDominik Brodowski }
421e11d4284SArnd Bergmann #endif
422157b334aSDominik Brodowski 
COMPAT_SYSCALL_DEFINE2(socketcall,int,call,u32 __user *,args)423361d93c4SHeiko Carstens COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args)
4241da177e4SLinus Torvalds {
42562bc306eSRichard Guy Briggs 	u32 a[AUDITSC_ARGS];
42662bc306eSRichard Guy Briggs 	unsigned int len;
4271da177e4SLinus Torvalds 	u32 a0, a1;
42862bc306eSRichard Guy Briggs 	int ret;
4291da177e4SLinus Torvalds 
430228e548eSAnton Blanchard 	if (call < SYS_SOCKET || call > SYS_SENDMMSG)
4311da177e4SLinus Torvalds 		return -EINVAL;
43262bc306eSRichard Guy Briggs 	len = nas[call];
43362bc306eSRichard Guy Briggs 	if (len > sizeof(a))
43462bc306eSRichard Guy Briggs 		return -EINVAL;
43562bc306eSRichard Guy Briggs 
43662bc306eSRichard Guy Briggs 	if (copy_from_user(a, args, len))
4371da177e4SLinus Torvalds 		return -EFAULT;
43862bc306eSRichard Guy Briggs 
43962bc306eSRichard Guy Briggs 	ret = audit_socketcall_compat(len / sizeof(a[0]), a);
44062bc306eSRichard Guy Briggs 	if (ret)
44162bc306eSRichard Guy Briggs 		return ret;
44262bc306eSRichard Guy Briggs 
4431da177e4SLinus Torvalds 	a0 = a[0];
4441da177e4SLinus Torvalds 	a1 = a[1];
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	switch (call) {
4471da177e4SLinus Torvalds 	case SYS_SOCKET:
4489d6a15c3SDominik Brodowski 		ret = __sys_socket(a0, a1, a[2]);
4491da177e4SLinus Torvalds 		break;
4501da177e4SLinus Torvalds 	case SYS_BIND:
451a87d35d8SDominik Brodowski 		ret = __sys_bind(a0, compat_ptr(a1), a[2]);
4521da177e4SLinus Torvalds 		break;
4531da177e4SLinus Torvalds 	case SYS_CONNECT:
4541387c2c2SDominik Brodowski 		ret = __sys_connect(a0, compat_ptr(a1), a[2]);
4551da177e4SLinus Torvalds 		break;
4561da177e4SLinus Torvalds 	case SYS_LISTEN:
45725e290eeSDominik Brodowski 		ret = __sys_listen(a0, a1);
4581da177e4SLinus Torvalds 		break;
4591da177e4SLinus Torvalds 	case SYS_ACCEPT:
4604541e805SDominik Brodowski 		ret = __sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), 0);
4611da177e4SLinus Torvalds 		break;
4621da177e4SLinus Torvalds 	case SYS_GETSOCKNAME:
4638882a107SDominik Brodowski 		ret = __sys_getsockname(a0, compat_ptr(a1), compat_ptr(a[2]));
4641da177e4SLinus Torvalds 		break;
4651da177e4SLinus Torvalds 	case SYS_GETPEERNAME:
466b21c8f83SDominik Brodowski 		ret = __sys_getpeername(a0, compat_ptr(a1), compat_ptr(a[2]));
4671da177e4SLinus Torvalds 		break;
4681da177e4SLinus Torvalds 	case SYS_SOCKETPAIR:
4696debc8d8SDominik Brodowski 		ret = __sys_socketpair(a0, a1, a[2], compat_ptr(a[3]));
4701da177e4SLinus Torvalds 		break;
4711da177e4SLinus Torvalds 	case SYS_SEND:
472f3bf896bSDominik Brodowski 		ret = __sys_sendto(a0, compat_ptr(a1), a[2], a[3], NULL, 0);
4731da177e4SLinus Torvalds 		break;
4741da177e4SLinus Torvalds 	case SYS_SENDTO:
475211b634bSDominik Brodowski 		ret = __sys_sendto(a0, compat_ptr(a1), a[2], a[3],
476211b634bSDominik Brodowski 				   compat_ptr(a[4]), a[5]);
4771da177e4SLinus Torvalds 		break;
4781da177e4SLinus Torvalds 	case SYS_RECV:
479fd4e82f5SDominik Brodowski 		ret = __compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3],
480fd4e82f5SDominik Brodowski 					    NULL, NULL);
4811da177e4SLinus Torvalds 		break;
4821da177e4SLinus Torvalds 	case SYS_RECVFROM:
483fd4e82f5SDominik Brodowski 		ret = __compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3],
484fd4e82f5SDominik Brodowski 					    compat_ptr(a[4]),
485fd4e82f5SDominik Brodowski 					    compat_ptr(a[5]));
4861da177e4SLinus Torvalds 		break;
4871da177e4SLinus Torvalds 	case SYS_SHUTDOWN:
488005a1aeaSDominik Brodowski 		ret = __sys_shutdown(a0, a1);
4891da177e4SLinus Torvalds 		break;
4901da177e4SLinus Torvalds 	case SYS_SETSOCKOPT:
49155db9c0eSChristoph Hellwig 		ret = __sys_setsockopt(a0, a1, a[2], compat_ptr(a[3]), a[4]);
4921da177e4SLinus Torvalds 		break;
4931da177e4SLinus Torvalds 	case SYS_GETSOCKOPT:
49455db9c0eSChristoph Hellwig 		ret = __sys_getsockopt(a0, a1, a[2], compat_ptr(a[3]),
4958770cf4aSDominik Brodowski 				       compat_ptr(a[4]));
4961da177e4SLinus Torvalds 		break;
4971da177e4SLinus Torvalds 	case SYS_SENDMSG:
4986df35465SDominik Brodowski 		ret = __compat_sys_sendmsg(a0, compat_ptr(a1), a[2]);
4991da177e4SLinus Torvalds 		break;
500228e548eSAnton Blanchard 	case SYS_SENDMMSG:
5016df35465SDominik Brodowski 		ret = __compat_sys_sendmmsg(a0, compat_ptr(a1), a[2], a[3]);
502228e548eSAnton Blanchard 		break;
5031da177e4SLinus Torvalds 	case SYS_RECVMSG:
5046df35465SDominik Brodowski 		ret = __compat_sys_recvmsg(a0, compat_ptr(a1), a[2]);
5051da177e4SLinus Torvalds 		break;
506a2e27255SArnaldo Carvalho de Melo 	case SYS_RECVMMSG:
507e11d4284SArnd Bergmann 		ret = __sys_recvmmsg(a0, compat_ptr(a1), a[2],
508e11d4284SArnd Bergmann 				     a[3] | MSG_CMSG_COMPAT, NULL,
509a2e27255SArnaldo Carvalho de Melo 				     compat_ptr(a[4]));
510a2e27255SArnaldo Carvalho de Melo 		break;
511de11defeSUlrich Drepper 	case SYS_ACCEPT4:
5124541e805SDominik Brodowski 		ret = __sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]);
513aaca0bdcSUlrich Drepper 		break;
5141da177e4SLinus Torvalds 	default:
5151da177e4SLinus Torvalds 		ret = -EINVAL;
5161da177e4SLinus Torvalds 		break;
5171da177e4SLinus Torvalds 	}
5181da177e4SLinus Torvalds 	return ret;
5191da177e4SLinus Torvalds }
520