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