18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/ipc/namespace.c 48c2ecf20Sopenharmony_ci * Copyright (C) 2006 Pavel Emelyanov <xemul@openvz.org> OpenVZ, SWsoft Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/ipc.h> 88c2ecf20Sopenharmony_ci#include <linux/msg.h> 98c2ecf20Sopenharmony_ci#include <linux/ipc_namespace.h> 108c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 118c2ecf20Sopenharmony_ci#include <linux/nsproxy.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/cred.h> 148c2ecf20Sopenharmony_ci#include <linux/fs.h> 158c2ecf20Sopenharmony_ci#include <linux/mount.h> 168c2ecf20Sopenharmony_ci#include <linux/user_namespace.h> 178c2ecf20Sopenharmony_ci#include <linux/proc_ns.h> 188c2ecf20Sopenharmony_ci#include <linux/sched/task.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "util.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct ucounts *inc_ipc_namespaces(struct user_namespace *ns) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci return inc_ucount(ns, current_euid(), UCOUNT_IPC_NAMESPACES); 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void dec_ipc_namespaces(struct ucounts *ucounts) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci dec_ucount(ucounts, UCOUNT_IPC_NAMESPACES); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns, 338c2ecf20Sopenharmony_ci struct ipc_namespace *old_ns) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 368c2ecf20Sopenharmony_ci struct ucounts *ucounts; 378c2ecf20Sopenharmony_ci int err; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci err = -ENOSPC; 408c2ecf20Sopenharmony_ci ucounts = inc_ipc_namespaces(user_ns); 418c2ecf20Sopenharmony_ci if (!ucounts) 428c2ecf20Sopenharmony_ci goto fail; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci err = -ENOMEM; 458c2ecf20Sopenharmony_ci ns = kzalloc(sizeof(struct ipc_namespace), GFP_KERNEL_ACCOUNT); 468c2ecf20Sopenharmony_ci if (ns == NULL) 478c2ecf20Sopenharmony_ci goto fail_dec; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci err = ns_alloc_inum(&ns->ns); 508c2ecf20Sopenharmony_ci if (err) 518c2ecf20Sopenharmony_ci goto fail_free; 528c2ecf20Sopenharmony_ci ns->ns.ops = &ipcns_operations; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci refcount_set(&ns->count, 1); 558c2ecf20Sopenharmony_ci ns->user_ns = get_user_ns(user_ns); 568c2ecf20Sopenharmony_ci ns->ucounts = ucounts; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci err = mq_init_ns(ns); 598c2ecf20Sopenharmony_ci if (err) 608c2ecf20Sopenharmony_ci goto fail_put; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci sem_init_ns(ns); 638c2ecf20Sopenharmony_ci msg_init_ns(ns); 648c2ecf20Sopenharmony_ci shm_init_ns(ns); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return ns; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cifail_put: 698c2ecf20Sopenharmony_ci put_user_ns(ns->user_ns); 708c2ecf20Sopenharmony_ci ns_free_inum(&ns->ns); 718c2ecf20Sopenharmony_cifail_free: 728c2ecf20Sopenharmony_ci kfree(ns); 738c2ecf20Sopenharmony_cifail_dec: 748c2ecf20Sopenharmony_ci dec_ipc_namespaces(ucounts); 758c2ecf20Sopenharmony_cifail: 768c2ecf20Sopenharmony_ci return ERR_PTR(err); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct ipc_namespace *copy_ipcs(unsigned long flags, 808c2ecf20Sopenharmony_ci struct user_namespace *user_ns, struct ipc_namespace *ns) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci if (!(flags & CLONE_NEWIPC)) 838c2ecf20Sopenharmony_ci return get_ipc_ns(ns); 848c2ecf20Sopenharmony_ci return create_ipc_ns(user_ns, ns); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * free_ipcs - free all ipcs of one type 898c2ecf20Sopenharmony_ci * @ns: the namespace to remove the ipcs from 908c2ecf20Sopenharmony_ci * @ids: the table of ipcs to free 918c2ecf20Sopenharmony_ci * @free: the function called to free each individual ipc 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * Called for each kind of ipc when an ipc_namespace exits. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_civoid free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids, 968c2ecf20Sopenharmony_ci void (*free)(struct ipc_namespace *, struct kern_ipc_perm *)) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct kern_ipc_perm *perm; 998c2ecf20Sopenharmony_ci int next_id; 1008c2ecf20Sopenharmony_ci int total, in_use; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci down_write(&ids->rwsem); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci in_use = ids->in_use; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (total = 0, next_id = 0; total < in_use; next_id++) { 1078c2ecf20Sopenharmony_ci perm = idr_find(&ids->ipcs_idr, next_id); 1088c2ecf20Sopenharmony_ci if (perm == NULL) 1098c2ecf20Sopenharmony_ci continue; 1108c2ecf20Sopenharmony_ci rcu_read_lock(); 1118c2ecf20Sopenharmony_ci ipc_lock_object(perm); 1128c2ecf20Sopenharmony_ci free(ns, perm); 1138c2ecf20Sopenharmony_ci total++; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci up_write(&ids->rwsem); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void free_ipc_ns(struct ipc_namespace *ns) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci /* mq_put_mnt() waits for a grace period as kern_unmount() 1218c2ecf20Sopenharmony_ci * uses synchronize_rcu(). 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci mq_put_mnt(ns); 1248c2ecf20Sopenharmony_ci sem_exit_ns(ns); 1258c2ecf20Sopenharmony_ci msg_exit_ns(ns); 1268c2ecf20Sopenharmony_ci shm_exit_ns(ns); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci dec_ipc_namespaces(ns->ucounts); 1298c2ecf20Sopenharmony_ci put_user_ns(ns->user_ns); 1308c2ecf20Sopenharmony_ci ns_free_inum(&ns->ns); 1318c2ecf20Sopenharmony_ci kfree(ns); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic LLIST_HEAD(free_ipc_list); 1358c2ecf20Sopenharmony_cistatic void free_ipc(struct work_struct *unused) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct llist_node *node = llist_del_all(&free_ipc_list); 1388c2ecf20Sopenharmony_ci struct ipc_namespace *n, *t; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci llist_for_each_entry_safe(n, t, node, mnt_llist) 1418c2ecf20Sopenharmony_ci free_ipc_ns(n); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * The work queue is used to avoid the cost of synchronize_rcu in kern_unmount. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistatic DECLARE_WORK(free_ipc_work, free_ipc); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * put_ipc_ns - drop a reference to an ipc namespace. 1518c2ecf20Sopenharmony_ci * @ns: the namespace to put 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * If this is the last task in the namespace exiting, and 1548c2ecf20Sopenharmony_ci * it is dropping the refcount to 0, then it can race with 1558c2ecf20Sopenharmony_ci * a task in another ipc namespace but in a mounts namespace 1568c2ecf20Sopenharmony_ci * which has this ipcns's mqueuefs mounted, doing some action 1578c2ecf20Sopenharmony_ci * with one of the mqueuefs files. That can raise the refcount. 1588c2ecf20Sopenharmony_ci * So dropping the refcount, and raising the refcount when 1598c2ecf20Sopenharmony_ci * accessing it through the VFS, are protected with mq_lock. 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * (Clearly, a task raising the refcount on its own ipc_ns 1628c2ecf20Sopenharmony_ci * needn't take mq_lock since it can't race with the last task 1638c2ecf20Sopenharmony_ci * in the ipcns exiting). 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_civoid put_ipc_ns(struct ipc_namespace *ns) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci if (refcount_dec_and_lock(&ns->count, &mq_lock)) { 1688c2ecf20Sopenharmony_ci mq_clear_sbinfo(ns); 1698c2ecf20Sopenharmony_ci spin_unlock(&mq_lock); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (llist_add(&ns->mnt_llist, &free_ipc_list)) 1728c2ecf20Sopenharmony_ci schedule_work(&free_ipc_work); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic inline struct ipc_namespace *to_ipc_ns(struct ns_common *ns) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return container_of(ns, struct ipc_namespace, ns); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic struct ns_common *ipcns_get(struct task_struct *task) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct ipc_namespace *ns = NULL; 1848c2ecf20Sopenharmony_ci struct nsproxy *nsproxy; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci task_lock(task); 1878c2ecf20Sopenharmony_ci nsproxy = task->nsproxy; 1888c2ecf20Sopenharmony_ci if (nsproxy) 1898c2ecf20Sopenharmony_ci ns = get_ipc_ns(nsproxy->ipc_ns); 1908c2ecf20Sopenharmony_ci task_unlock(task); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ns ? &ns->ns : NULL; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void ipcns_put(struct ns_common *ns) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci return put_ipc_ns(to_ipc_ns(ns)); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int ipcns_install(struct nsset *nsset, struct ns_common *new) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct nsproxy *nsproxy = nsset->nsproxy; 2038c2ecf20Sopenharmony_ci struct ipc_namespace *ns = to_ipc_ns(new); 2048c2ecf20Sopenharmony_ci if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) || 2058c2ecf20Sopenharmony_ci !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN)) 2068c2ecf20Sopenharmony_ci return -EPERM; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci put_ipc_ns(nsproxy->ipc_ns); 2098c2ecf20Sopenharmony_ci nsproxy->ipc_ns = get_ipc_ns(ns); 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic struct user_namespace *ipcns_owner(struct ns_common *ns) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci return to_ipc_ns(ns)->user_ns; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ciconst struct proc_ns_operations ipcns_operations = { 2198c2ecf20Sopenharmony_ci .name = "ipc", 2208c2ecf20Sopenharmony_ci .type = CLONE_NEWIPC, 2218c2ecf20Sopenharmony_ci .get = ipcns_get, 2228c2ecf20Sopenharmony_ci .put = ipcns_put, 2238c2ecf20Sopenharmony_ci .install = ipcns_install, 2248c2ecf20Sopenharmony_ci .owner = ipcns_owner, 2258c2ecf20Sopenharmony_ci}; 226