cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

namespace.c (5408B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * linux/ipc/namespace.c
      4 * Copyright (C) 2006 Pavel Emelyanov <xemul@openvz.org> OpenVZ, SWsoft Inc.
      5 */
      6
      7#include <linux/ipc.h>
      8#include <linux/msg.h>
      9#include <linux/ipc_namespace.h>
     10#include <linux/rcupdate.h>
     11#include <linux/nsproxy.h>
     12#include <linux/slab.h>
     13#include <linux/cred.h>
     14#include <linux/fs.h>
     15#include <linux/mount.h>
     16#include <linux/user_namespace.h>
     17#include <linux/proc_ns.h>
     18#include <linux/sched/task.h>
     19
     20#include "util.h"
     21
     22static struct ucounts *inc_ipc_namespaces(struct user_namespace *ns)
     23{
     24	return inc_ucount(ns, current_euid(), UCOUNT_IPC_NAMESPACES);
     25}
     26
     27static void dec_ipc_namespaces(struct ucounts *ucounts)
     28{
     29	dec_ucount(ucounts, UCOUNT_IPC_NAMESPACES);
     30}
     31
     32static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
     33					   struct ipc_namespace *old_ns)
     34{
     35	struct ipc_namespace *ns;
     36	struct ucounts *ucounts;
     37	int err;
     38
     39	err = -ENOSPC;
     40	ucounts = inc_ipc_namespaces(user_ns);
     41	if (!ucounts)
     42		goto fail;
     43
     44	err = -ENOMEM;
     45	ns = kzalloc(sizeof(struct ipc_namespace), GFP_KERNEL_ACCOUNT);
     46	if (ns == NULL)
     47		goto fail_dec;
     48
     49	err = ns_alloc_inum(&ns->ns);
     50	if (err)
     51		goto fail_free;
     52	ns->ns.ops = &ipcns_operations;
     53
     54	refcount_set(&ns->ns.count, 1);
     55	ns->user_ns = get_user_ns(user_ns);
     56	ns->ucounts = ucounts;
     57
     58	err = mq_init_ns(ns);
     59	if (err)
     60		goto fail_put;
     61
     62	err = -ENOMEM;
     63	if (!setup_mq_sysctls(ns))
     64		goto fail_put;
     65
     66	if (!setup_ipc_sysctls(ns))
     67		goto fail_put;
     68
     69	sem_init_ns(ns);
     70	msg_init_ns(ns);
     71	shm_init_ns(ns);
     72
     73	return ns;
     74
     75fail_put:
     76	put_user_ns(ns->user_ns);
     77	ns_free_inum(&ns->ns);
     78fail_free:
     79	kfree(ns);
     80fail_dec:
     81	dec_ipc_namespaces(ucounts);
     82fail:
     83	return ERR_PTR(err);
     84}
     85
     86struct ipc_namespace *copy_ipcs(unsigned long flags,
     87	struct user_namespace *user_ns, struct ipc_namespace *ns)
     88{
     89	if (!(flags & CLONE_NEWIPC))
     90		return get_ipc_ns(ns);
     91	return create_ipc_ns(user_ns, ns);
     92}
     93
     94/*
     95 * free_ipcs - free all ipcs of one type
     96 * @ns:   the namespace to remove the ipcs from
     97 * @ids:  the table of ipcs to free
     98 * @free: the function called to free each individual ipc
     99 *
    100 * Called for each kind of ipc when an ipc_namespace exits.
    101 */
    102void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
    103	       void (*free)(struct ipc_namespace *, struct kern_ipc_perm *))
    104{
    105	struct kern_ipc_perm *perm;
    106	int next_id;
    107	int total, in_use;
    108
    109	down_write(&ids->rwsem);
    110
    111	in_use = ids->in_use;
    112
    113	for (total = 0, next_id = 0; total < in_use; next_id++) {
    114		perm = idr_find(&ids->ipcs_idr, next_id);
    115		if (perm == NULL)
    116			continue;
    117		rcu_read_lock();
    118		ipc_lock_object(perm);
    119		free(ns, perm);
    120		total++;
    121	}
    122	up_write(&ids->rwsem);
    123}
    124
    125static void free_ipc_ns(struct ipc_namespace *ns)
    126{
    127	/* mq_put_mnt() waits for a grace period as kern_unmount()
    128	 * uses synchronize_rcu().
    129	 */
    130	mq_put_mnt(ns);
    131	sem_exit_ns(ns);
    132	msg_exit_ns(ns);
    133	shm_exit_ns(ns);
    134
    135	retire_mq_sysctls(ns);
    136	retire_ipc_sysctls(ns);
    137
    138	dec_ipc_namespaces(ns->ucounts);
    139	put_user_ns(ns->user_ns);
    140	ns_free_inum(&ns->ns);
    141	kfree(ns);
    142}
    143
    144static LLIST_HEAD(free_ipc_list);
    145static void free_ipc(struct work_struct *unused)
    146{
    147	struct llist_node *node = llist_del_all(&free_ipc_list);
    148	struct ipc_namespace *n, *t;
    149
    150	llist_for_each_entry_safe(n, t, node, mnt_llist)
    151		free_ipc_ns(n);
    152}
    153
    154/*
    155 * The work queue is used to avoid the cost of synchronize_rcu in kern_unmount.
    156 */
    157static DECLARE_WORK(free_ipc_work, free_ipc);
    158
    159/*
    160 * put_ipc_ns - drop a reference to an ipc namespace.
    161 * @ns: the namespace to put
    162 *
    163 * If this is the last task in the namespace exiting, and
    164 * it is dropping the refcount to 0, then it can race with
    165 * a task in another ipc namespace but in a mounts namespace
    166 * which has this ipcns's mqueuefs mounted, doing some action
    167 * with one of the mqueuefs files.  That can raise the refcount.
    168 * So dropping the refcount, and raising the refcount when
    169 * accessing it through the VFS, are protected with mq_lock.
    170 *
    171 * (Clearly, a task raising the refcount on its own ipc_ns
    172 * needn't take mq_lock since it can't race with the last task
    173 * in the ipcns exiting).
    174 */
    175void put_ipc_ns(struct ipc_namespace *ns)
    176{
    177	if (refcount_dec_and_lock(&ns->ns.count, &mq_lock)) {
    178		mq_clear_sbinfo(ns);
    179		spin_unlock(&mq_lock);
    180
    181		if (llist_add(&ns->mnt_llist, &free_ipc_list))
    182			schedule_work(&free_ipc_work);
    183	}
    184}
    185
    186static inline struct ipc_namespace *to_ipc_ns(struct ns_common *ns)
    187{
    188	return container_of(ns, struct ipc_namespace, ns);
    189}
    190
    191static struct ns_common *ipcns_get(struct task_struct *task)
    192{
    193	struct ipc_namespace *ns = NULL;
    194	struct nsproxy *nsproxy;
    195
    196	task_lock(task);
    197	nsproxy = task->nsproxy;
    198	if (nsproxy)
    199		ns = get_ipc_ns(nsproxy->ipc_ns);
    200	task_unlock(task);
    201
    202	return ns ? &ns->ns : NULL;
    203}
    204
    205static void ipcns_put(struct ns_common *ns)
    206{
    207	return put_ipc_ns(to_ipc_ns(ns));
    208}
    209
    210static int ipcns_install(struct nsset *nsset, struct ns_common *new)
    211{
    212	struct nsproxy *nsproxy = nsset->nsproxy;
    213	struct ipc_namespace *ns = to_ipc_ns(new);
    214	if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
    215	    !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
    216		return -EPERM;
    217
    218	put_ipc_ns(nsproxy->ipc_ns);
    219	nsproxy->ipc_ns = get_ipc_ns(ns);
    220	return 0;
    221}
    222
    223static struct user_namespace *ipcns_owner(struct ns_common *ns)
    224{
    225	return to_ipc_ns(ns)->user_ns;
    226}
    227
    228const struct proc_ns_operations ipcns_operations = {
    229	.name		= "ipc",
    230	.type		= CLONE_NEWIPC,
    231	.get		= ipcns_get,
    232	.put		= ipcns_put,
    233	.install	= ipcns_install,
    234	.owner		= ipcns_owner,
    235};