162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* scm.c - Socket level control messages processing.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Author:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
562306a36Sopenharmony_ci *              Alignment and value checking mods by Craig Metz
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/signal.h>
1062306a36Sopenharmony_ci#include <linux/capability.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/sched/user.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/stat.h>
1762306a36Sopenharmony_ci#include <linux/socket.h>
1862306a36Sopenharmony_ci#include <linux/file.h>
1962306a36Sopenharmony_ci#include <linux/fcntl.h>
2062306a36Sopenharmony_ci#include <linux/net.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/netdevice.h>
2362306a36Sopenharmony_ci#include <linux/security.h>
2462306a36Sopenharmony_ci#include <linux/pid_namespace.h>
2562306a36Sopenharmony_ci#include <linux/pid.h>
2662306a36Sopenharmony_ci#include <linux/nsproxy.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/errqueue.h>
2962306a36Sopenharmony_ci#include <linux/io_uring.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <linux/uaccess.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <net/protocol.h>
3462306a36Sopenharmony_ci#include <linux/skbuff.h>
3562306a36Sopenharmony_ci#include <net/sock.h>
3662306a36Sopenharmony_ci#include <net/compat.h>
3762306a36Sopenharmony_ci#include <net/scm.h>
3862306a36Sopenharmony_ci#include <net/cls_cgroup.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci *	Only allow a user to send credentials, that they could set with
4362306a36Sopenharmony_ci *	setu(g)id.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic __inline__ int scm_check_creds(struct ucred *creds)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	const struct cred *cred = current_cred();
4962306a36Sopenharmony_ci	kuid_t uid = make_kuid(cred->user_ns, creds->uid);
5062306a36Sopenharmony_ci	kgid_t gid = make_kgid(cred->user_ns, creds->gid);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!uid_valid(uid) || !gid_valid(gid))
5362306a36Sopenharmony_ci		return -EINVAL;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if ((creds->pid == task_tgid_vnr(current) ||
5662306a36Sopenharmony_ci	     ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) &&
5762306a36Sopenharmony_ci	    ((uid_eq(uid, cred->uid)   || uid_eq(uid, cred->euid) ||
5862306a36Sopenharmony_ci	      uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) &&
5962306a36Sopenharmony_ci	    ((gid_eq(gid, cred->gid)   || gid_eq(gid, cred->egid) ||
6062306a36Sopenharmony_ci	      gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) {
6162306a36Sopenharmony_ci	       return 0;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	return -EPERM;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int *fdp = (int*)CMSG_DATA(cmsg);
6962306a36Sopenharmony_ci	struct scm_fp_list *fpl = *fplp;
7062306a36Sopenharmony_ci	struct file **fpp;
7162306a36Sopenharmony_ci	int i, num;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	num = (cmsg->cmsg_len - sizeof(struct cmsghdr))/sizeof(int);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (num <= 0)
7662306a36Sopenharmony_ci		return 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (num > SCM_MAX_FD)
7962306a36Sopenharmony_ci		return -EINVAL;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (!fpl)
8262306a36Sopenharmony_ci	{
8362306a36Sopenharmony_ci		fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL_ACCOUNT);
8462306a36Sopenharmony_ci		if (!fpl)
8562306a36Sopenharmony_ci			return -ENOMEM;
8662306a36Sopenharmony_ci		*fplp = fpl;
8762306a36Sopenharmony_ci		fpl->count = 0;
8862306a36Sopenharmony_ci		fpl->max = SCM_MAX_FD;
8962306a36Sopenharmony_ci		fpl->user = NULL;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	fpp = &fpl->fp[fpl->count];
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (fpl->count + num > fpl->max)
9462306a36Sopenharmony_ci		return -EINVAL;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/*
9762306a36Sopenharmony_ci	 *	Verify the descriptors and increment the usage count.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	for (i=0; i< num; i++)
10162306a36Sopenharmony_ci	{
10262306a36Sopenharmony_ci		int fd = fdp[i];
10362306a36Sopenharmony_ci		struct file *file;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		if (fd < 0 || !(file = fget_raw(fd)))
10662306a36Sopenharmony_ci			return -EBADF;
10762306a36Sopenharmony_ci		/* don't allow io_uring files */
10862306a36Sopenharmony_ci		if (io_is_uring_fops(file)) {
10962306a36Sopenharmony_ci			fput(file);
11062306a36Sopenharmony_ci			return -EINVAL;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci		*fpp++ = file;
11362306a36Sopenharmony_ci		fpl->count++;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!fpl->user)
11762306a36Sopenharmony_ci		fpl->user = get_uid(current_user());
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return num;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid __scm_destroy(struct scm_cookie *scm)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct scm_fp_list *fpl = scm->fp;
12562306a36Sopenharmony_ci	int i;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (fpl) {
12862306a36Sopenharmony_ci		scm->fp = NULL;
12962306a36Sopenharmony_ci		for (i=fpl->count-1; i>=0; i--)
13062306a36Sopenharmony_ci			fput(fpl->fp[i]);
13162306a36Sopenharmony_ci		free_uid(fpl->user);
13262306a36Sopenharmony_ci		kfree(fpl);
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ciEXPORT_SYMBOL(__scm_destroy);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciint __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	const struct proto_ops *ops = READ_ONCE(sock->ops);
14062306a36Sopenharmony_ci	struct cmsghdr *cmsg;
14162306a36Sopenharmony_ci	int err;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	for_each_cmsghdr(cmsg, msg) {
14462306a36Sopenharmony_ci		err = -EINVAL;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		/* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
14762306a36Sopenharmony_ci		/* The first check was omitted in <= 2.2.5. The reasoning was
14862306a36Sopenharmony_ci		   that parser checks cmsg_len in any case, so that
14962306a36Sopenharmony_ci		   additional check would be work duplication.
15062306a36Sopenharmony_ci		   But if cmsg_level is not SOL_SOCKET, we do not check
15162306a36Sopenharmony_ci		   for too short ancillary data object at all! Oops.
15262306a36Sopenharmony_ci		   OK, let's add it...
15362306a36Sopenharmony_ci		 */
15462306a36Sopenharmony_ci		if (!CMSG_OK(msg, cmsg))
15562306a36Sopenharmony_ci			goto error;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		if (cmsg->cmsg_level != SOL_SOCKET)
15862306a36Sopenharmony_ci			continue;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		switch (cmsg->cmsg_type)
16162306a36Sopenharmony_ci		{
16262306a36Sopenharmony_ci		case SCM_RIGHTS:
16362306a36Sopenharmony_ci			if (!ops || ops->family != PF_UNIX)
16462306a36Sopenharmony_ci				goto error;
16562306a36Sopenharmony_ci			err=scm_fp_copy(cmsg, &p->fp);
16662306a36Sopenharmony_ci			if (err<0)
16762306a36Sopenharmony_ci				goto error;
16862306a36Sopenharmony_ci			break;
16962306a36Sopenharmony_ci		case SCM_CREDENTIALS:
17062306a36Sopenharmony_ci		{
17162306a36Sopenharmony_ci			struct ucred creds;
17262306a36Sopenharmony_ci			kuid_t uid;
17362306a36Sopenharmony_ci			kgid_t gid;
17462306a36Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
17562306a36Sopenharmony_ci				goto error;
17662306a36Sopenharmony_ci			memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred));
17762306a36Sopenharmony_ci			err = scm_check_creds(&creds);
17862306a36Sopenharmony_ci			if (err)
17962306a36Sopenharmony_ci				goto error;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci			p->creds.pid = creds.pid;
18262306a36Sopenharmony_ci			if (!p->pid || pid_vnr(p->pid) != creds.pid) {
18362306a36Sopenharmony_ci				struct pid *pid;
18462306a36Sopenharmony_ci				err = -ESRCH;
18562306a36Sopenharmony_ci				pid = find_get_pid(creds.pid);
18662306a36Sopenharmony_ci				if (!pid)
18762306a36Sopenharmony_ci					goto error;
18862306a36Sopenharmony_ci				put_pid(p->pid);
18962306a36Sopenharmony_ci				p->pid = pid;
19062306a36Sopenharmony_ci			}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci			err = -EINVAL;
19362306a36Sopenharmony_ci			uid = make_kuid(current_user_ns(), creds.uid);
19462306a36Sopenharmony_ci			gid = make_kgid(current_user_ns(), creds.gid);
19562306a36Sopenharmony_ci			if (!uid_valid(uid) || !gid_valid(gid))
19662306a36Sopenharmony_ci				goto error;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci			p->creds.uid = uid;
19962306a36Sopenharmony_ci			p->creds.gid = gid;
20062306a36Sopenharmony_ci			break;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci		default:
20362306a36Sopenharmony_ci			goto error;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (p->fp && !p->fp->count)
20862306a36Sopenharmony_ci	{
20962306a36Sopenharmony_ci		kfree(p->fp);
21062306a36Sopenharmony_ci		p->fp = NULL;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci	return 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cierror:
21562306a36Sopenharmony_ci	scm_destroy(p);
21662306a36Sopenharmony_ci	return err;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ciEXPORT_SYMBOL(__scm_send);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciint put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	int cmlen = CMSG_LEN(len);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (msg->msg_flags & MSG_CMSG_COMPAT)
22562306a36Sopenharmony_ci		return put_cmsg_compat(msg, level, type, len, data);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) {
22862306a36Sopenharmony_ci		msg->msg_flags |= MSG_CTRUNC;
22962306a36Sopenharmony_ci		return 0; /* XXX: return error? check spec. */
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	if (msg->msg_controllen < cmlen) {
23262306a36Sopenharmony_ci		msg->msg_flags |= MSG_CTRUNC;
23362306a36Sopenharmony_ci		cmlen = msg->msg_controllen;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (msg->msg_control_is_user) {
23762306a36Sopenharmony_ci		struct cmsghdr __user *cm = msg->msg_control_user;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		check_object_size(data, cmlen - sizeof(*cm), true);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		if (!user_write_access_begin(cm, cmlen))
24262306a36Sopenharmony_ci			goto efault;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		unsafe_put_user(cmlen, &cm->cmsg_len, efault_end);
24562306a36Sopenharmony_ci		unsafe_put_user(level, &cm->cmsg_level, efault_end);
24662306a36Sopenharmony_ci		unsafe_put_user(type, &cm->cmsg_type, efault_end);
24762306a36Sopenharmony_ci		unsafe_copy_to_user(CMSG_USER_DATA(cm), data,
24862306a36Sopenharmony_ci				    cmlen - sizeof(*cm), efault_end);
24962306a36Sopenharmony_ci		user_write_access_end();
25062306a36Sopenharmony_ci	} else {
25162306a36Sopenharmony_ci		struct cmsghdr *cm = msg->msg_control;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		cm->cmsg_level = level;
25462306a36Sopenharmony_ci		cm->cmsg_type = type;
25562306a36Sopenharmony_ci		cm->cmsg_len = cmlen;
25662306a36Sopenharmony_ci		memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm));
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	cmlen = min(CMSG_SPACE(len), msg->msg_controllen);
26062306a36Sopenharmony_ci	if (msg->msg_control_is_user)
26162306a36Sopenharmony_ci		msg->msg_control_user += cmlen;
26262306a36Sopenharmony_ci	else
26362306a36Sopenharmony_ci		msg->msg_control += cmlen;
26462306a36Sopenharmony_ci	msg->msg_controllen -= cmlen;
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ciefault_end:
26862306a36Sopenharmony_ci	user_write_access_end();
26962306a36Sopenharmony_ciefault:
27062306a36Sopenharmony_ci	return -EFAULT;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ciEXPORT_SYMBOL(put_cmsg);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_civoid put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss_internal)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct scm_timestamping64 tss;
27762306a36Sopenharmony_ci	int i;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tss.ts); i++) {
28062306a36Sopenharmony_ci		tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec;
28162306a36Sopenharmony_ci		tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_NEW, sizeof(tss), &tss);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ciEXPORT_SYMBOL(put_cmsg_scm_timestamping64);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_civoid put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss_internal)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct scm_timestamping tss;
29162306a36Sopenharmony_ci	int i;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tss.ts); i++) {
29462306a36Sopenharmony_ci		tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec;
29562306a36Sopenharmony_ci		tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_OLD, sizeof(tss), &tss);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ciEXPORT_SYMBOL(put_cmsg_scm_timestamping);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int scm_max_fds(struct msghdr *msg)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	if (msg->msg_controllen <= sizeof(struct cmsghdr))
30562306a36Sopenharmony_ci		return 0;
30662306a36Sopenharmony_ci	return (msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_civoid scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct cmsghdr __user *cm =
31262306a36Sopenharmony_ci		(__force struct cmsghdr __user *)msg->msg_control_user;
31362306a36Sopenharmony_ci	unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
31462306a36Sopenharmony_ci	int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count);
31562306a36Sopenharmony_ci	int __user *cmsg_data = CMSG_USER_DATA(cm);
31662306a36Sopenharmony_ci	int err = 0, i;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* no use for FD passing from kernel space callers */
31962306a36Sopenharmony_ci	if (WARN_ON_ONCE(!msg->msg_control_is_user))
32062306a36Sopenharmony_ci		return;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (msg->msg_flags & MSG_CMSG_COMPAT) {
32362306a36Sopenharmony_ci		scm_detach_fds_compat(msg, scm);
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	for (i = 0; i < fdmax; i++) {
32862306a36Sopenharmony_ci		err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags);
32962306a36Sopenharmony_ci		if (err < 0)
33062306a36Sopenharmony_ci			break;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (i > 0) {
33462306a36Sopenharmony_ci		int cmlen = CMSG_LEN(i * sizeof(int));
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		err = put_user(SOL_SOCKET, &cm->cmsg_level);
33762306a36Sopenharmony_ci		if (!err)
33862306a36Sopenharmony_ci			err = put_user(SCM_RIGHTS, &cm->cmsg_type);
33962306a36Sopenharmony_ci		if (!err)
34062306a36Sopenharmony_ci			err = put_user(cmlen, &cm->cmsg_len);
34162306a36Sopenharmony_ci		if (!err) {
34262306a36Sopenharmony_ci			cmlen = CMSG_SPACE(i * sizeof(int));
34362306a36Sopenharmony_ci			if (msg->msg_controllen < cmlen)
34462306a36Sopenharmony_ci				cmlen = msg->msg_controllen;
34562306a36Sopenharmony_ci			msg->msg_control_user += cmlen;
34662306a36Sopenharmony_ci			msg->msg_controllen -= cmlen;
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (i < scm->fp->count || (scm->fp->count && fdmax <= 0))
35162306a36Sopenharmony_ci		msg->msg_flags |= MSG_CTRUNC;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/*
35462306a36Sopenharmony_ci	 * All of the files that fit in the message have had their usage counts
35562306a36Sopenharmony_ci	 * incremented, so we just free the list.
35662306a36Sopenharmony_ci	 */
35762306a36Sopenharmony_ci	__scm_destroy(scm);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ciEXPORT_SYMBOL(scm_detach_fds);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistruct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct scm_fp_list *new_fpl;
36462306a36Sopenharmony_ci	int i;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (!fpl)
36762306a36Sopenharmony_ci		return NULL;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]),
37062306a36Sopenharmony_ci			  GFP_KERNEL_ACCOUNT);
37162306a36Sopenharmony_ci	if (new_fpl) {
37262306a36Sopenharmony_ci		for (i = 0; i < fpl->count; i++)
37362306a36Sopenharmony_ci			get_file(fpl->fp[i]);
37462306a36Sopenharmony_ci		new_fpl->max = new_fpl->count;
37562306a36Sopenharmony_ci		new_fpl->user = get_uid(fpl->user);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci	return new_fpl;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ciEXPORT_SYMBOL(scm_fp_dup);
380