18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* scm.c - Socket level control messages processing.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
58c2ecf20Sopenharmony_ci *              Alignment and value checking mods by Craig Metz
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/signal.h>
108c2ecf20Sopenharmony_ci#include <linux/capability.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/sched.h>
138c2ecf20Sopenharmony_ci#include <linux/sched/user.h>
148c2ecf20Sopenharmony_ci#include <linux/mm.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/stat.h>
178c2ecf20Sopenharmony_ci#include <linux/socket.h>
188c2ecf20Sopenharmony_ci#include <linux/file.h>
198c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
208c2ecf20Sopenharmony_ci#include <linux/net.h>
218c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
228c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
238c2ecf20Sopenharmony_ci#include <linux/security.h>
248c2ecf20Sopenharmony_ci#include <linux/pid_namespace.h>
258c2ecf20Sopenharmony_ci#include <linux/pid.h>
268c2ecf20Sopenharmony_ci#include <linux/nsproxy.h>
278c2ecf20Sopenharmony_ci#include <linux/slab.h>
288c2ecf20Sopenharmony_ci#include <linux/errqueue.h>
298c2ecf20Sopenharmony_ci#include <linux/io_uring.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <net/protocol.h>
348c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
358c2ecf20Sopenharmony_ci#include <net/sock.h>
368c2ecf20Sopenharmony_ci#include <net/compat.h>
378c2ecf20Sopenharmony_ci#include <net/scm.h>
388c2ecf20Sopenharmony_ci#include <net/cls_cgroup.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci *	Only allow a user to send credentials, that they could set with
438c2ecf20Sopenharmony_ci *	setu(g)id.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic __inline__ int scm_check_creds(struct ucred *creds)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	const struct cred *cred = current_cred();
498c2ecf20Sopenharmony_ci	kuid_t uid = make_kuid(cred->user_ns, creds->uid);
508c2ecf20Sopenharmony_ci	kgid_t gid = make_kgid(cred->user_ns, creds->gid);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (!uid_valid(uid) || !gid_valid(gid))
538c2ecf20Sopenharmony_ci		return -EINVAL;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if ((creds->pid == task_tgid_vnr(current) ||
568c2ecf20Sopenharmony_ci	     ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) &&
578c2ecf20Sopenharmony_ci	    ((uid_eq(uid, cred->uid)   || uid_eq(uid, cred->euid) ||
588c2ecf20Sopenharmony_ci	      uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) &&
598c2ecf20Sopenharmony_ci	    ((gid_eq(gid, cred->gid)   || gid_eq(gid, cred->egid) ||
608c2ecf20Sopenharmony_ci	      gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) {
618c2ecf20Sopenharmony_ci	       return 0;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	return -EPERM;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	int *fdp = (int*)CMSG_DATA(cmsg);
698c2ecf20Sopenharmony_ci	struct scm_fp_list *fpl = *fplp;
708c2ecf20Sopenharmony_ci	struct file **fpp;
718c2ecf20Sopenharmony_ci	int i, num;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	num = (cmsg->cmsg_len - sizeof(struct cmsghdr))/sizeof(int);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (num <= 0)
768c2ecf20Sopenharmony_ci		return 0;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (num > SCM_MAX_FD)
798c2ecf20Sopenharmony_ci		return -EINVAL;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (!fpl)
828c2ecf20Sopenharmony_ci	{
838c2ecf20Sopenharmony_ci		fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
848c2ecf20Sopenharmony_ci		if (!fpl)
858c2ecf20Sopenharmony_ci			return -ENOMEM;
868c2ecf20Sopenharmony_ci		*fplp = fpl;
878c2ecf20Sopenharmony_ci		fpl->count = 0;
888c2ecf20Sopenharmony_ci		fpl->max = SCM_MAX_FD;
898c2ecf20Sopenharmony_ci		fpl->user = NULL;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci	fpp = &fpl->fp[fpl->count];
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (fpl->count + num > fpl->max)
948c2ecf20Sopenharmony_ci		return -EINVAL;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/*
978c2ecf20Sopenharmony_ci	 *	Verify the descriptors and increment the usage count.
988c2ecf20Sopenharmony_ci	 */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	for (i=0; i< num; i++)
1018c2ecf20Sopenharmony_ci	{
1028c2ecf20Sopenharmony_ci		int fd = fdp[i];
1038c2ecf20Sopenharmony_ci		struct file *file;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		if (fd < 0 || !(file = fget_raw(fd)))
1068c2ecf20Sopenharmony_ci			return -EBADF;
1078c2ecf20Sopenharmony_ci		/* don't allow io_uring files */
1088c2ecf20Sopenharmony_ci		if (io_uring_get_socket(file)) {
1098c2ecf20Sopenharmony_ci			fput(file);
1108c2ecf20Sopenharmony_ci			return -EINVAL;
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci		*fpp++ = file;
1138c2ecf20Sopenharmony_ci		fpl->count++;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (!fpl->user)
1178c2ecf20Sopenharmony_ci		fpl->user = get_uid(current_user());
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return num;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_civoid __scm_destroy(struct scm_cookie *scm)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct scm_fp_list *fpl = scm->fp;
1258c2ecf20Sopenharmony_ci	int i;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (fpl) {
1288c2ecf20Sopenharmony_ci		scm->fp = NULL;
1298c2ecf20Sopenharmony_ci		for (i=fpl->count-1; i>=0; i--)
1308c2ecf20Sopenharmony_ci			fput(fpl->fp[i]);
1318c2ecf20Sopenharmony_ci		free_uid(fpl->user);
1328c2ecf20Sopenharmony_ci		kfree(fpl);
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__scm_destroy);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ciint __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct cmsghdr *cmsg;
1408c2ecf20Sopenharmony_ci	int err;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for_each_cmsghdr(cmsg, msg) {
1438c2ecf20Sopenharmony_ci		err = -EINVAL;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		/* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
1468c2ecf20Sopenharmony_ci		/* The first check was omitted in <= 2.2.5. The reasoning was
1478c2ecf20Sopenharmony_ci		   that parser checks cmsg_len in any case, so that
1488c2ecf20Sopenharmony_ci		   additional check would be work duplication.
1498c2ecf20Sopenharmony_ci		   But if cmsg_level is not SOL_SOCKET, we do not check
1508c2ecf20Sopenharmony_ci		   for too short ancillary data object at all! Oops.
1518c2ecf20Sopenharmony_ci		   OK, let's add it...
1528c2ecf20Sopenharmony_ci		 */
1538c2ecf20Sopenharmony_ci		if (!CMSG_OK(msg, cmsg))
1548c2ecf20Sopenharmony_ci			goto error;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		if (cmsg->cmsg_level != SOL_SOCKET)
1578c2ecf20Sopenharmony_ci			continue;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		switch (cmsg->cmsg_type)
1608c2ecf20Sopenharmony_ci		{
1618c2ecf20Sopenharmony_ci		case SCM_RIGHTS:
1628c2ecf20Sopenharmony_ci			if (!sock->ops || sock->ops->family != PF_UNIX)
1638c2ecf20Sopenharmony_ci				goto error;
1648c2ecf20Sopenharmony_ci			err=scm_fp_copy(cmsg, &p->fp);
1658c2ecf20Sopenharmony_ci			if (err<0)
1668c2ecf20Sopenharmony_ci				goto error;
1678c2ecf20Sopenharmony_ci			break;
1688c2ecf20Sopenharmony_ci		case SCM_CREDENTIALS:
1698c2ecf20Sopenharmony_ci		{
1708c2ecf20Sopenharmony_ci			struct ucred creds;
1718c2ecf20Sopenharmony_ci			kuid_t uid;
1728c2ecf20Sopenharmony_ci			kgid_t gid;
1738c2ecf20Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
1748c2ecf20Sopenharmony_ci				goto error;
1758c2ecf20Sopenharmony_ci			memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred));
1768c2ecf20Sopenharmony_ci			err = scm_check_creds(&creds);
1778c2ecf20Sopenharmony_ci			if (err)
1788c2ecf20Sopenharmony_ci				goto error;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci			p->creds.pid = creds.pid;
1818c2ecf20Sopenharmony_ci			if (!p->pid || pid_vnr(p->pid) != creds.pid) {
1828c2ecf20Sopenharmony_ci				struct pid *pid;
1838c2ecf20Sopenharmony_ci				err = -ESRCH;
1848c2ecf20Sopenharmony_ci				pid = find_get_pid(creds.pid);
1858c2ecf20Sopenharmony_ci				if (!pid)
1868c2ecf20Sopenharmony_ci					goto error;
1878c2ecf20Sopenharmony_ci				put_pid(p->pid);
1888c2ecf20Sopenharmony_ci				p->pid = pid;
1898c2ecf20Sopenharmony_ci			}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci			err = -EINVAL;
1928c2ecf20Sopenharmony_ci			uid = make_kuid(current_user_ns(), creds.uid);
1938c2ecf20Sopenharmony_ci			gid = make_kgid(current_user_ns(), creds.gid);
1948c2ecf20Sopenharmony_ci			if (!uid_valid(uid) || !gid_valid(gid))
1958c2ecf20Sopenharmony_ci				goto error;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci			p->creds.uid = uid;
1988c2ecf20Sopenharmony_ci			p->creds.gid = gid;
1998c2ecf20Sopenharmony_ci			break;
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci		default:
2028c2ecf20Sopenharmony_ci			goto error;
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (p->fp && !p->fp->count)
2078c2ecf20Sopenharmony_ci	{
2088c2ecf20Sopenharmony_ci		kfree(p->fp);
2098c2ecf20Sopenharmony_ci		p->fp = NULL;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	return 0;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cierror:
2148c2ecf20Sopenharmony_ci	scm_destroy(p);
2158c2ecf20Sopenharmony_ci	return err;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__scm_send);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciint put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	int cmlen = CMSG_LEN(len);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (msg->msg_flags & MSG_CMSG_COMPAT)
2248c2ecf20Sopenharmony_ci		return put_cmsg_compat(msg, level, type, len, data);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (!msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) {
2278c2ecf20Sopenharmony_ci		msg->msg_flags |= MSG_CTRUNC;
2288c2ecf20Sopenharmony_ci		return 0; /* XXX: return error? check spec. */
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci	if (msg->msg_controllen < cmlen) {
2318c2ecf20Sopenharmony_ci		msg->msg_flags |= MSG_CTRUNC;
2328c2ecf20Sopenharmony_ci		cmlen = msg->msg_controllen;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (msg->msg_control_is_user) {
2368c2ecf20Sopenharmony_ci		struct cmsghdr __user *cm = msg->msg_control_user;
2378c2ecf20Sopenharmony_ci		struct cmsghdr cmhdr;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		cmhdr.cmsg_level = level;
2408c2ecf20Sopenharmony_ci		cmhdr.cmsg_type = type;
2418c2ecf20Sopenharmony_ci		cmhdr.cmsg_len = cmlen;
2428c2ecf20Sopenharmony_ci		if (copy_to_user(cm, &cmhdr, sizeof cmhdr) ||
2438c2ecf20Sopenharmony_ci		    copy_to_user(CMSG_USER_DATA(cm), data, cmlen - sizeof(*cm)))
2448c2ecf20Sopenharmony_ci			return -EFAULT;
2458c2ecf20Sopenharmony_ci	} else {
2468c2ecf20Sopenharmony_ci		struct cmsghdr *cm = msg->msg_control;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		cm->cmsg_level = level;
2498c2ecf20Sopenharmony_ci		cm->cmsg_type = type;
2508c2ecf20Sopenharmony_ci		cm->cmsg_len = cmlen;
2518c2ecf20Sopenharmony_ci		memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm));
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	cmlen = min(CMSG_SPACE(len), msg->msg_controllen);
2558c2ecf20Sopenharmony_ci	msg->msg_control += cmlen;
2568c2ecf20Sopenharmony_ci	msg->msg_controllen -= cmlen;
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(put_cmsg);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_civoid put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss_internal)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct scm_timestamping64 tss;
2648c2ecf20Sopenharmony_ci	int i;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tss.ts); i++) {
2678c2ecf20Sopenharmony_ci		tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec;
2688c2ecf20Sopenharmony_ci		tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_NEW, sizeof(tss), &tss);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(put_cmsg_scm_timestamping64);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_civoid put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss_internal)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct scm_timestamping tss;
2788c2ecf20Sopenharmony_ci	int i;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tss.ts); i++) {
2818c2ecf20Sopenharmony_ci		tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec;
2828c2ecf20Sopenharmony_ci		tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_OLD, sizeof(tss), &tss);
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(put_cmsg_scm_timestamping);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int scm_max_fds(struct msghdr *msg)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	if (msg->msg_controllen <= sizeof(struct cmsghdr))
2928c2ecf20Sopenharmony_ci		return 0;
2938c2ecf20Sopenharmony_ci	return (msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_civoid scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct cmsghdr __user *cm =
2998c2ecf20Sopenharmony_ci		(__force struct cmsghdr __user *)msg->msg_control;
3008c2ecf20Sopenharmony_ci	unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
3018c2ecf20Sopenharmony_ci	int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count);
3028c2ecf20Sopenharmony_ci	int __user *cmsg_data = CMSG_USER_DATA(cm);
3038c2ecf20Sopenharmony_ci	int err = 0, i;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* no use for FD passing from kernel space callers */
3068c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!msg->msg_control_is_user))
3078c2ecf20Sopenharmony_ci		return;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (msg->msg_flags & MSG_CMSG_COMPAT) {
3108c2ecf20Sopenharmony_ci		scm_detach_fds_compat(msg, scm);
3118c2ecf20Sopenharmony_ci		return;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	for (i = 0; i < fdmax; i++) {
3158c2ecf20Sopenharmony_ci		err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags);
3168c2ecf20Sopenharmony_ci		if (err < 0)
3178c2ecf20Sopenharmony_ci			break;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (i > 0) {
3218c2ecf20Sopenharmony_ci		int cmlen = CMSG_LEN(i * sizeof(int));
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		err = put_user(SOL_SOCKET, &cm->cmsg_level);
3248c2ecf20Sopenharmony_ci		if (!err)
3258c2ecf20Sopenharmony_ci			err = put_user(SCM_RIGHTS, &cm->cmsg_type);
3268c2ecf20Sopenharmony_ci		if (!err)
3278c2ecf20Sopenharmony_ci			err = put_user(cmlen, &cm->cmsg_len);
3288c2ecf20Sopenharmony_ci		if (!err) {
3298c2ecf20Sopenharmony_ci			cmlen = CMSG_SPACE(i * sizeof(int));
3308c2ecf20Sopenharmony_ci			if (msg->msg_controllen < cmlen)
3318c2ecf20Sopenharmony_ci				cmlen = msg->msg_controllen;
3328c2ecf20Sopenharmony_ci			msg->msg_control += cmlen;
3338c2ecf20Sopenharmony_ci			msg->msg_controllen -= cmlen;
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (i < scm->fp->count || (scm->fp->count && fdmax <= 0))
3388c2ecf20Sopenharmony_ci		msg->msg_flags |= MSG_CTRUNC;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/*
3418c2ecf20Sopenharmony_ci	 * All of the files that fit in the message have had their usage counts
3428c2ecf20Sopenharmony_ci	 * incremented, so we just free the list.
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	__scm_destroy(scm);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(scm_detach_fds);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistruct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct scm_fp_list *new_fpl;
3518c2ecf20Sopenharmony_ci	int i;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (!fpl)
3548c2ecf20Sopenharmony_ci		return NULL;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]),
3578c2ecf20Sopenharmony_ci			  GFP_KERNEL);
3588c2ecf20Sopenharmony_ci	if (new_fpl) {
3598c2ecf20Sopenharmony_ci		for (i = 0; i < fpl->count; i++)
3608c2ecf20Sopenharmony_ci			get_file(fpl->fp[i]);
3618c2ecf20Sopenharmony_ci		new_fpl->max = new_fpl->count;
3628c2ecf20Sopenharmony_ci		new_fpl->user = get_uid(fpl->user);
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci	return new_fpl;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(scm_fp_dup);
367