162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/ipc/util.c
462306a36Sopenharmony_ci * Copyright (C) 1992 Krishna Balasubramanian
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Sep 1997 - Call suser() last after "normal" permission checks so we
762306a36Sopenharmony_ci *            get BSD style process accounting right.
862306a36Sopenharmony_ci *            Occurs in several places in the IPC code.
962306a36Sopenharmony_ci *            Chris Evans, <chris@ferret.lmh.ox.ac.uk>
1062306a36Sopenharmony_ci * Nov 1999 - ipc helper functions, unified SMP locking
1162306a36Sopenharmony_ci *	      Manfred Spraul <manfred@colorfullife.com>
1262306a36Sopenharmony_ci * Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
1362306a36Sopenharmony_ci *            Mingming Cao <cmm@us.ibm.com>
1462306a36Sopenharmony_ci * Mar 2006 - support for audit of ipc object properties
1562306a36Sopenharmony_ci *            Dustin Kirkland <dustin.kirkland@us.ibm.com>
1662306a36Sopenharmony_ci * Jun 2006 - namespaces ssupport
1762306a36Sopenharmony_ci *            OpenVZ, SWsoft Inc.
1862306a36Sopenharmony_ci *            Pavel Emelianov <xemul@openvz.org>
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * General sysv ipc locking scheme:
2162306a36Sopenharmony_ci *	rcu_read_lock()
2262306a36Sopenharmony_ci *          obtain the ipc object (kern_ipc_perm) by looking up the id in an idr
2362306a36Sopenharmony_ci *	    tree.
2462306a36Sopenharmony_ci *	    - perform initial checks (capabilities, auditing and permission,
2562306a36Sopenharmony_ci *	      etc).
2662306a36Sopenharmony_ci *	    - perform read-only operations, such as INFO command, that
2762306a36Sopenharmony_ci *	      do not demand atomicity
2862306a36Sopenharmony_ci *	      acquire the ipc lock (kern_ipc_perm.lock) through
2962306a36Sopenharmony_ci *	      ipc_lock_object()
3062306a36Sopenharmony_ci *		- perform read-only operations that demand atomicity,
3162306a36Sopenharmony_ci *		  such as STAT command.
3262306a36Sopenharmony_ci *		- perform data updates, such as SET, RMID commands and
3362306a36Sopenharmony_ci *		  mechanism-specific operations (semop/semtimedop,
3462306a36Sopenharmony_ci *		  msgsnd/msgrcv, shmat/shmdt).
3562306a36Sopenharmony_ci *	    drop the ipc lock, through ipc_unlock_object().
3662306a36Sopenharmony_ci *	rcu_read_unlock()
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci *  The ids->rwsem must be taken when:
3962306a36Sopenharmony_ci *	- creating, removing and iterating the existing entries in ipc
4062306a36Sopenharmony_ci *	  identifier sets.
4162306a36Sopenharmony_ci *	- iterating through files under /proc/sysvipc/
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci *  Note that sems have a special fast path that avoids kern_ipc_perm.lock -
4462306a36Sopenharmony_ci *  see sem_lock().
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include <linux/mm.h>
4862306a36Sopenharmony_ci#include <linux/shm.h>
4962306a36Sopenharmony_ci#include <linux/init.h>
5062306a36Sopenharmony_ci#include <linux/msg.h>
5162306a36Sopenharmony_ci#include <linux/vmalloc.h>
5262306a36Sopenharmony_ci#include <linux/slab.h>
5362306a36Sopenharmony_ci#include <linux/notifier.h>
5462306a36Sopenharmony_ci#include <linux/capability.h>
5562306a36Sopenharmony_ci#include <linux/highuid.h>
5662306a36Sopenharmony_ci#include <linux/security.h>
5762306a36Sopenharmony_ci#include <linux/rcupdate.h>
5862306a36Sopenharmony_ci#include <linux/workqueue.h>
5962306a36Sopenharmony_ci#include <linux/seq_file.h>
6062306a36Sopenharmony_ci#include <linux/proc_fs.h>
6162306a36Sopenharmony_ci#include <linux/audit.h>
6262306a36Sopenharmony_ci#include <linux/nsproxy.h>
6362306a36Sopenharmony_ci#include <linux/rwsem.h>
6462306a36Sopenharmony_ci#include <linux/memory.h>
6562306a36Sopenharmony_ci#include <linux/ipc_namespace.h>
6662306a36Sopenharmony_ci#include <linux/rhashtable.h>
6762306a36Sopenharmony_ci#include <linux/log2.h>
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#include <asm/unistd.h>
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#include "util.h"
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct ipc_proc_iface {
7462306a36Sopenharmony_ci	const char *path;
7562306a36Sopenharmony_ci	const char *header;
7662306a36Sopenharmony_ci	int ids;
7762306a36Sopenharmony_ci	int (*show)(struct seq_file *, void *);
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/**
8162306a36Sopenharmony_ci * ipc_init - initialise ipc subsystem
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * The various sysv ipc resources (semaphores, messages and shared
8462306a36Sopenharmony_ci * memory) are initialised.
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * A callback routine is registered into the memory hotplug notifier
8762306a36Sopenharmony_ci * chain: since msgmni scales to lowmem this callback routine will be
8862306a36Sopenharmony_ci * called upon successful memory add / remove to recompute msmgni.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic int __init ipc_init(void)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	proc_mkdir("sysvipc", NULL);
9362306a36Sopenharmony_ci	sem_init();
9462306a36Sopenharmony_ci	msg_init();
9562306a36Sopenharmony_ci	shm_init();
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_cidevice_initcall(ipc_init);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic const struct rhashtable_params ipc_kht_params = {
10262306a36Sopenharmony_ci	.head_offset		= offsetof(struct kern_ipc_perm, khtnode),
10362306a36Sopenharmony_ci	.key_offset		= offsetof(struct kern_ipc_perm, key),
10462306a36Sopenharmony_ci	.key_len		= sizeof_field(struct kern_ipc_perm, key),
10562306a36Sopenharmony_ci	.automatic_shrinking	= true,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/**
10962306a36Sopenharmony_ci * ipc_init_ids	- initialise ipc identifiers
11062306a36Sopenharmony_ci * @ids: ipc identifier set
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * Set up the sequence range to use for the ipc identifier range (limited
11362306a36Sopenharmony_ci * below ipc_mni) then initialise the keys hashtable and ids idr.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_civoid ipc_init_ids(struct ipc_ids *ids)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	ids->in_use = 0;
11862306a36Sopenharmony_ci	ids->seq = 0;
11962306a36Sopenharmony_ci	init_rwsem(&ids->rwsem);
12062306a36Sopenharmony_ci	rhashtable_init(&ids->key_ht, &ipc_kht_params);
12162306a36Sopenharmony_ci	idr_init(&ids->ipcs_idr);
12262306a36Sopenharmony_ci	ids->max_idx = -1;
12362306a36Sopenharmony_ci	ids->last_idx = -1;
12462306a36Sopenharmony_ci#ifdef CONFIG_CHECKPOINT_RESTORE
12562306a36Sopenharmony_ci	ids->next_id = -1;
12662306a36Sopenharmony_ci#endif
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
13062306a36Sopenharmony_cistatic const struct proc_ops sysvipc_proc_ops;
13162306a36Sopenharmony_ci/**
13262306a36Sopenharmony_ci * ipc_init_proc_interface -  create a proc interface for sysipc types using a seq_file interface.
13362306a36Sopenharmony_ci * @path: Path in procfs
13462306a36Sopenharmony_ci * @header: Banner to be printed at the beginning of the file.
13562306a36Sopenharmony_ci * @ids: ipc id table to iterate.
13662306a36Sopenharmony_ci * @show: show routine.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_civoid __init ipc_init_proc_interface(const char *path, const char *header,
13962306a36Sopenharmony_ci		int ids, int (*show)(struct seq_file *, void *))
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct proc_dir_entry *pde;
14262306a36Sopenharmony_ci	struct ipc_proc_iface *iface;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	iface = kmalloc(sizeof(*iface), GFP_KERNEL);
14562306a36Sopenharmony_ci	if (!iface)
14662306a36Sopenharmony_ci		return;
14762306a36Sopenharmony_ci	iface->path	= path;
14862306a36Sopenharmony_ci	iface->header	= header;
14962306a36Sopenharmony_ci	iface->ids	= ids;
15062306a36Sopenharmony_ci	iface->show	= show;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	pde = proc_create_data(path,
15362306a36Sopenharmony_ci			       S_IRUGO,        /* world readable */
15462306a36Sopenharmony_ci			       NULL,           /* parent dir */
15562306a36Sopenharmony_ci			       &sysvipc_proc_ops,
15662306a36Sopenharmony_ci			       iface);
15762306a36Sopenharmony_ci	if (!pde)
15862306a36Sopenharmony_ci		kfree(iface);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci#endif
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/**
16362306a36Sopenharmony_ci * ipc_findkey	- find a key in an ipc identifier set
16462306a36Sopenharmony_ci * @ids: ipc identifier set
16562306a36Sopenharmony_ci * @key: key to find
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * Returns the locked pointer to the ipc structure if found or NULL
16862306a36Sopenharmony_ci * otherwise. If key is found ipc points to the owning ipc structure
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * Called with writer ipc_ids.rwsem held.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct kern_ipc_perm *ipcp;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ipcp = rhashtable_lookup_fast(&ids->key_ht, &key,
17762306a36Sopenharmony_ci					      ipc_kht_params);
17862306a36Sopenharmony_ci	if (!ipcp)
17962306a36Sopenharmony_ci		return NULL;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	rcu_read_lock();
18262306a36Sopenharmony_ci	ipc_lock_object(ipcp);
18362306a36Sopenharmony_ci	return ipcp;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/*
18762306a36Sopenharmony_ci * Insert new IPC object into idr tree, and set sequence number and id
18862306a36Sopenharmony_ci * in the correct order.
18962306a36Sopenharmony_ci * Especially:
19062306a36Sopenharmony_ci * - the sequence number must be set before inserting the object into the idr,
19162306a36Sopenharmony_ci *   because the sequence number is accessed without a lock.
19262306a36Sopenharmony_ci * - the id can/must be set after inserting the object into the idr.
19362306a36Sopenharmony_ci *   All accesses must be done after getting kern_ipc_perm.lock.
19462306a36Sopenharmony_ci *
19562306a36Sopenharmony_ci * The caller must own kern_ipc_perm.lock.of the new object.
19662306a36Sopenharmony_ci * On error, the function returns a (negative) error code.
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * To conserve sequence number space, especially with extended ipc_mni,
19962306a36Sopenharmony_ci * the sequence number is incremented only when the returned ID is less than
20062306a36Sopenharmony_ci * the last one.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	int idx, next_id = -1;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci#ifdef CONFIG_CHECKPOINT_RESTORE
20762306a36Sopenharmony_ci	next_id = ids->next_id;
20862306a36Sopenharmony_ci	ids->next_id = -1;
20962306a36Sopenharmony_ci#endif
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 * As soon as a new object is inserted into the idr,
21362306a36Sopenharmony_ci	 * ipc_obtain_object_idr() or ipc_obtain_object_check() can find it,
21462306a36Sopenharmony_ci	 * and the lockless preparations for ipc operations can start.
21562306a36Sopenharmony_ci	 * This means especially: permission checks, audit calls, allocation
21662306a36Sopenharmony_ci	 * of undo structures, ...
21762306a36Sopenharmony_ci	 *
21862306a36Sopenharmony_ci	 * Thus the object must be fully initialized, and if something fails,
21962306a36Sopenharmony_ci	 * then the full tear-down sequence must be followed.
22062306a36Sopenharmony_ci	 * (i.e.: set new->deleted, reduce refcount, call_rcu())
22162306a36Sopenharmony_ci	 */
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (next_id < 0) { /* !CHECKPOINT_RESTORE or next_id is unset */
22462306a36Sopenharmony_ci		int max_idx;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		max_idx = max(ids->in_use*3/2, ipc_min_cycle);
22762306a36Sopenharmony_ci		max_idx = min(max_idx, ipc_mni);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		/* allocate the idx, with a NULL struct kern_ipc_perm */
23062306a36Sopenharmony_ci		idx = idr_alloc_cyclic(&ids->ipcs_idr, NULL, 0, max_idx,
23162306a36Sopenharmony_ci					GFP_NOWAIT);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		if (idx >= 0) {
23462306a36Sopenharmony_ci			/*
23562306a36Sopenharmony_ci			 * idx got allocated successfully.
23662306a36Sopenharmony_ci			 * Now calculate the sequence number and set the
23762306a36Sopenharmony_ci			 * pointer for real.
23862306a36Sopenharmony_ci			 */
23962306a36Sopenharmony_ci			if (idx <= ids->last_idx) {
24062306a36Sopenharmony_ci				ids->seq++;
24162306a36Sopenharmony_ci				if (ids->seq >= ipcid_seq_max())
24262306a36Sopenharmony_ci					ids->seq = 0;
24362306a36Sopenharmony_ci			}
24462306a36Sopenharmony_ci			ids->last_idx = idx;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci			new->seq = ids->seq;
24762306a36Sopenharmony_ci			/* no need for smp_wmb(), this is done
24862306a36Sopenharmony_ci			 * inside idr_replace, as part of
24962306a36Sopenharmony_ci			 * rcu_assign_pointer
25062306a36Sopenharmony_ci			 */
25162306a36Sopenharmony_ci			idr_replace(&ids->ipcs_idr, new, idx);
25262306a36Sopenharmony_ci		}
25362306a36Sopenharmony_ci	} else {
25462306a36Sopenharmony_ci		new->seq = ipcid_to_seqx(next_id);
25562306a36Sopenharmony_ci		idx = idr_alloc(&ids->ipcs_idr, new, ipcid_to_idx(next_id),
25662306a36Sopenharmony_ci				0, GFP_NOWAIT);
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	if (idx >= 0)
25962306a36Sopenharmony_ci		new->id = (new->seq << ipcmni_seq_shift()) + idx;
26062306a36Sopenharmony_ci	return idx;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci/**
26462306a36Sopenharmony_ci * ipc_addid - add an ipc identifier
26562306a36Sopenharmony_ci * @ids: ipc identifier set
26662306a36Sopenharmony_ci * @new: new ipc permission set
26762306a36Sopenharmony_ci * @limit: limit for the number of used ids
26862306a36Sopenharmony_ci *
26962306a36Sopenharmony_ci * Add an entry 'new' to the ipc ids idr. The permissions object is
27062306a36Sopenharmony_ci * initialised and the first free entry is set up and the index assigned
27162306a36Sopenharmony_ci * is returned. The 'new' entry is returned in a locked state on success.
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * On failure the entry is not locked and a negative err-code is returned.
27462306a36Sopenharmony_ci * The caller must use ipc_rcu_putref() to free the identifier.
27562306a36Sopenharmony_ci *
27662306a36Sopenharmony_ci * Called with writer ipc_ids.rwsem held.
27762306a36Sopenharmony_ci */
27862306a36Sopenharmony_ciint ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	kuid_t euid;
28162306a36Sopenharmony_ci	kgid_t egid;
28262306a36Sopenharmony_ci	int idx, err;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* 1) Initialize the refcount so that ipc_rcu_putref works */
28562306a36Sopenharmony_ci	refcount_set(&new->refcount, 1);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (limit > ipc_mni)
28862306a36Sopenharmony_ci		limit = ipc_mni;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (ids->in_use >= limit)
29162306a36Sopenharmony_ci		return -ENOSPC;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	spin_lock_init(&new->lock);
29662306a36Sopenharmony_ci	rcu_read_lock();
29762306a36Sopenharmony_ci	spin_lock(&new->lock);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	current_euid_egid(&euid, &egid);
30062306a36Sopenharmony_ci	new->cuid = new->uid = euid;
30162306a36Sopenharmony_ci	new->gid = new->cgid = egid;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	new->deleted = false;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	idx = ipc_idr_alloc(ids, new);
30662306a36Sopenharmony_ci	idr_preload_end();
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (idx >= 0 && new->key != IPC_PRIVATE) {
30962306a36Sopenharmony_ci		err = rhashtable_insert_fast(&ids->key_ht, &new->khtnode,
31062306a36Sopenharmony_ci					     ipc_kht_params);
31162306a36Sopenharmony_ci		if (err < 0) {
31262306a36Sopenharmony_ci			idr_remove(&ids->ipcs_idr, idx);
31362306a36Sopenharmony_ci			idx = err;
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	if (idx < 0) {
31762306a36Sopenharmony_ci		new->deleted = true;
31862306a36Sopenharmony_ci		spin_unlock(&new->lock);
31962306a36Sopenharmony_ci		rcu_read_unlock();
32062306a36Sopenharmony_ci		return idx;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	ids->in_use++;
32462306a36Sopenharmony_ci	if (idx > ids->max_idx)
32562306a36Sopenharmony_ci		ids->max_idx = idx;
32662306a36Sopenharmony_ci	return idx;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/**
33062306a36Sopenharmony_ci * ipcget_new -	create a new ipc object
33162306a36Sopenharmony_ci * @ns: ipc namespace
33262306a36Sopenharmony_ci * @ids: ipc identifier set
33362306a36Sopenharmony_ci * @ops: the actual creation routine to call
33462306a36Sopenharmony_ci * @params: its parameters
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci * This routine is called by sys_msgget, sys_semget() and sys_shmget()
33762306a36Sopenharmony_ci * when the key is IPC_PRIVATE.
33862306a36Sopenharmony_ci */
33962306a36Sopenharmony_cistatic int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,
34062306a36Sopenharmony_ci		const struct ipc_ops *ops, struct ipc_params *params)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	int err;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	down_write(&ids->rwsem);
34562306a36Sopenharmony_ci	err = ops->getnew(ns, params);
34662306a36Sopenharmony_ci	up_write(&ids->rwsem);
34762306a36Sopenharmony_ci	return err;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/**
35162306a36Sopenharmony_ci * ipc_check_perms - check security and permissions for an ipc object
35262306a36Sopenharmony_ci * @ns: ipc namespace
35362306a36Sopenharmony_ci * @ipcp: ipc permission set
35462306a36Sopenharmony_ci * @ops: the actual security routine to call
35562306a36Sopenharmony_ci * @params: its parameters
35662306a36Sopenharmony_ci *
35762306a36Sopenharmony_ci * This routine is called by sys_msgget(), sys_semget() and sys_shmget()
35862306a36Sopenharmony_ci * when the key is not IPC_PRIVATE and that key already exists in the
35962306a36Sopenharmony_ci * ds IDR.
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * On success, the ipc id is returned.
36262306a36Sopenharmony_ci *
36362306a36Sopenharmony_ci * It is called with ipc_ids.rwsem and ipcp->lock held.
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic int ipc_check_perms(struct ipc_namespace *ns,
36662306a36Sopenharmony_ci			   struct kern_ipc_perm *ipcp,
36762306a36Sopenharmony_ci			   const struct ipc_ops *ops,
36862306a36Sopenharmony_ci			   struct ipc_params *params)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	int err;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (ipcperms(ns, ipcp, params->flg))
37362306a36Sopenharmony_ci		err = -EACCES;
37462306a36Sopenharmony_ci	else {
37562306a36Sopenharmony_ci		err = ops->associate(ipcp, params->flg);
37662306a36Sopenharmony_ci		if (!err)
37762306a36Sopenharmony_ci			err = ipcp->id;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return err;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/**
38462306a36Sopenharmony_ci * ipcget_public - get an ipc object or create a new one
38562306a36Sopenharmony_ci * @ns: ipc namespace
38662306a36Sopenharmony_ci * @ids: ipc identifier set
38762306a36Sopenharmony_ci * @ops: the actual creation routine to call
38862306a36Sopenharmony_ci * @params: its parameters
38962306a36Sopenharmony_ci *
39062306a36Sopenharmony_ci * This routine is called by sys_msgget, sys_semget() and sys_shmget()
39162306a36Sopenharmony_ci * when the key is not IPC_PRIVATE.
39262306a36Sopenharmony_ci * It adds a new entry if the key is not found and does some permission
39362306a36Sopenharmony_ci * / security checkings if the key is found.
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * On success, the ipc id is returned.
39662306a36Sopenharmony_ci */
39762306a36Sopenharmony_cistatic int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
39862306a36Sopenharmony_ci		const struct ipc_ops *ops, struct ipc_params *params)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct kern_ipc_perm *ipcp;
40162306a36Sopenharmony_ci	int flg = params->flg;
40262306a36Sopenharmony_ci	int err;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/*
40562306a36Sopenharmony_ci	 * Take the lock as a writer since we are potentially going to add
40662306a36Sopenharmony_ci	 * a new entry + read locks are not "upgradable"
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	down_write(&ids->rwsem);
40962306a36Sopenharmony_ci	ipcp = ipc_findkey(ids, params->key);
41062306a36Sopenharmony_ci	if (ipcp == NULL) {
41162306a36Sopenharmony_ci		/* key not used */
41262306a36Sopenharmony_ci		if (!(flg & IPC_CREAT))
41362306a36Sopenharmony_ci			err = -ENOENT;
41462306a36Sopenharmony_ci		else
41562306a36Sopenharmony_ci			err = ops->getnew(ns, params);
41662306a36Sopenharmony_ci	} else {
41762306a36Sopenharmony_ci		/* ipc object has been locked by ipc_findkey() */
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		if (flg & IPC_CREAT && flg & IPC_EXCL)
42062306a36Sopenharmony_ci			err = -EEXIST;
42162306a36Sopenharmony_ci		else {
42262306a36Sopenharmony_ci			err = 0;
42362306a36Sopenharmony_ci			if (ops->more_checks)
42462306a36Sopenharmony_ci				err = ops->more_checks(ipcp, params);
42562306a36Sopenharmony_ci			if (!err)
42662306a36Sopenharmony_ci				/*
42762306a36Sopenharmony_ci				 * ipc_check_perms returns the IPC id on
42862306a36Sopenharmony_ci				 * success
42962306a36Sopenharmony_ci				 */
43062306a36Sopenharmony_ci				err = ipc_check_perms(ns, ipcp, ops, params);
43162306a36Sopenharmony_ci		}
43262306a36Sopenharmony_ci		ipc_unlock(ipcp);
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	up_write(&ids->rwsem);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return err;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/**
44062306a36Sopenharmony_ci * ipc_kht_remove - remove an ipc from the key hashtable
44162306a36Sopenharmony_ci * @ids: ipc identifier set
44262306a36Sopenharmony_ci * @ipcp: ipc perm structure containing the key to remove
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
44562306a36Sopenharmony_ci * before this function is called, and remain locked on the exit.
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_cistatic void ipc_kht_remove(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	if (ipcp->key != IPC_PRIVATE)
45062306a36Sopenharmony_ci		WARN_ON_ONCE(rhashtable_remove_fast(&ids->key_ht, &ipcp->khtnode,
45162306a36Sopenharmony_ci				       ipc_kht_params));
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/**
45562306a36Sopenharmony_ci * ipc_search_maxidx - search for the highest assigned index
45662306a36Sopenharmony_ci * @ids: ipc identifier set
45762306a36Sopenharmony_ci * @limit: known upper limit for highest assigned index
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * The function determines the highest assigned index in @ids. It is intended
46062306a36Sopenharmony_ci * to be called when ids->max_idx needs to be updated.
46162306a36Sopenharmony_ci * Updating ids->max_idx is necessary when the current highest index ipc
46262306a36Sopenharmony_ci * object is deleted.
46362306a36Sopenharmony_ci * If no ipc object is allocated, then -1 is returned.
46462306a36Sopenharmony_ci *
46562306a36Sopenharmony_ci * ipc_ids.rwsem needs to be held by the caller.
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_cistatic int ipc_search_maxidx(struct ipc_ids *ids, int limit)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	int tmpidx;
47062306a36Sopenharmony_ci	int i;
47162306a36Sopenharmony_ci	int retval;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	i = ilog2(limit+1);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	retval = 0;
47662306a36Sopenharmony_ci	for (; i >= 0; i--) {
47762306a36Sopenharmony_ci		tmpidx = retval | (1<<i);
47862306a36Sopenharmony_ci		/*
47962306a36Sopenharmony_ci		 * "0" is a possible index value, thus search using
48062306a36Sopenharmony_ci		 * e.g. 15,7,3,1,0 instead of 16,8,4,2,1.
48162306a36Sopenharmony_ci		 */
48262306a36Sopenharmony_ci		tmpidx = tmpidx-1;
48362306a36Sopenharmony_ci		if (idr_get_next(&ids->ipcs_idr, &tmpidx))
48462306a36Sopenharmony_ci			retval |= (1<<i);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci	return retval - 1;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci/**
49062306a36Sopenharmony_ci * ipc_rmid - remove an ipc identifier
49162306a36Sopenharmony_ci * @ids: ipc identifier set
49262306a36Sopenharmony_ci * @ipcp: ipc perm structure containing the identifier to remove
49362306a36Sopenharmony_ci *
49462306a36Sopenharmony_ci * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
49562306a36Sopenharmony_ci * before this function is called, and remain locked on the exit.
49662306a36Sopenharmony_ci */
49762306a36Sopenharmony_civoid ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	int idx = ipcid_to_idx(ipcp->id);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	WARN_ON_ONCE(idr_remove(&ids->ipcs_idr, idx) != ipcp);
50262306a36Sopenharmony_ci	ipc_kht_remove(ids, ipcp);
50362306a36Sopenharmony_ci	ids->in_use--;
50462306a36Sopenharmony_ci	ipcp->deleted = true;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (unlikely(idx == ids->max_idx)) {
50762306a36Sopenharmony_ci		idx = ids->max_idx-1;
50862306a36Sopenharmony_ci		if (idx >= 0)
50962306a36Sopenharmony_ci			idx = ipc_search_maxidx(ids, idx);
51062306a36Sopenharmony_ci		ids->max_idx = idx;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci/**
51562306a36Sopenharmony_ci * ipc_set_key_private - switch the key of an existing ipc to IPC_PRIVATE
51662306a36Sopenharmony_ci * @ids: ipc identifier set
51762306a36Sopenharmony_ci * @ipcp: ipc perm structure containing the key to modify
51862306a36Sopenharmony_ci *
51962306a36Sopenharmony_ci * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
52062306a36Sopenharmony_ci * before this function is called, and remain locked on the exit.
52162306a36Sopenharmony_ci */
52262306a36Sopenharmony_civoid ipc_set_key_private(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	ipc_kht_remove(ids, ipcp);
52562306a36Sopenharmony_ci	ipcp->key = IPC_PRIVATE;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cibool ipc_rcu_getref(struct kern_ipc_perm *ptr)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	return refcount_inc_not_zero(&ptr->refcount);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_civoid ipc_rcu_putref(struct kern_ipc_perm *ptr,
53462306a36Sopenharmony_ci			void (*func)(struct rcu_head *head))
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	if (!refcount_dec_and_test(&ptr->refcount))
53762306a36Sopenharmony_ci		return;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	call_rcu(&ptr->rcu, func);
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci/**
54362306a36Sopenharmony_ci * ipcperms - check ipc permissions
54462306a36Sopenharmony_ci * @ns: ipc namespace
54562306a36Sopenharmony_ci * @ipcp: ipc permission set
54662306a36Sopenharmony_ci * @flag: desired permission set
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci * Check user, group, other permissions for access
54962306a36Sopenharmony_ci * to ipc resources. return 0 if allowed
55062306a36Sopenharmony_ci *
55162306a36Sopenharmony_ci * @flag will most probably be 0 or ``S_...UGO`` from <linux/stat.h>
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_ciint ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	kuid_t euid = current_euid();
55662306a36Sopenharmony_ci	int requested_mode, granted_mode;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	audit_ipc_obj(ipcp);
55962306a36Sopenharmony_ci	requested_mode = (flag >> 6) | (flag >> 3) | flag;
56062306a36Sopenharmony_ci	granted_mode = ipcp->mode;
56162306a36Sopenharmony_ci	if (uid_eq(euid, ipcp->cuid) ||
56262306a36Sopenharmony_ci	    uid_eq(euid, ipcp->uid))
56362306a36Sopenharmony_ci		granted_mode >>= 6;
56462306a36Sopenharmony_ci	else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
56562306a36Sopenharmony_ci		granted_mode >>= 3;
56662306a36Sopenharmony_ci	/* is there some bit set in requested_mode but not in granted_mode? */
56762306a36Sopenharmony_ci	if ((requested_mode & ~granted_mode & 0007) &&
56862306a36Sopenharmony_ci	    !ns_capable(ns->user_ns, CAP_IPC_OWNER))
56962306a36Sopenharmony_ci		return -1;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return security_ipc_permission(ipcp, flag);
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci/*
57562306a36Sopenharmony_ci * Functions to convert between the kern_ipc_perm structure and the
57662306a36Sopenharmony_ci * old/new ipc_perm structures
57762306a36Sopenharmony_ci */
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/**
58062306a36Sopenharmony_ci * kernel_to_ipc64_perm	- convert kernel ipc permissions to user
58162306a36Sopenharmony_ci * @in: kernel permissions
58262306a36Sopenharmony_ci * @out: new style ipc permissions
58362306a36Sopenharmony_ci *
58462306a36Sopenharmony_ci * Turn the kernel object @in into a set of permissions descriptions
58562306a36Sopenharmony_ci * for returning to userspace (@out).
58662306a36Sopenharmony_ci */
58762306a36Sopenharmony_civoid kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	out->key	= in->key;
59062306a36Sopenharmony_ci	out->uid	= from_kuid_munged(current_user_ns(), in->uid);
59162306a36Sopenharmony_ci	out->gid	= from_kgid_munged(current_user_ns(), in->gid);
59262306a36Sopenharmony_ci	out->cuid	= from_kuid_munged(current_user_ns(), in->cuid);
59362306a36Sopenharmony_ci	out->cgid	= from_kgid_munged(current_user_ns(), in->cgid);
59462306a36Sopenharmony_ci	out->mode	= in->mode;
59562306a36Sopenharmony_ci	out->seq	= in->seq;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/**
59962306a36Sopenharmony_ci * ipc64_perm_to_ipc_perm - convert new ipc permissions to old
60062306a36Sopenharmony_ci * @in: new style ipc permissions
60162306a36Sopenharmony_ci * @out: old style ipc permissions
60262306a36Sopenharmony_ci *
60362306a36Sopenharmony_ci * Turn the new style permissions object @in into a compatibility
60462306a36Sopenharmony_ci * object and store it into the @out pointer.
60562306a36Sopenharmony_ci */
60662306a36Sopenharmony_civoid ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	out->key	= in->key;
60962306a36Sopenharmony_ci	SET_UID(out->uid, in->uid);
61062306a36Sopenharmony_ci	SET_GID(out->gid, in->gid);
61162306a36Sopenharmony_ci	SET_UID(out->cuid, in->cuid);
61262306a36Sopenharmony_ci	SET_GID(out->cgid, in->cgid);
61362306a36Sopenharmony_ci	out->mode	= in->mode;
61462306a36Sopenharmony_ci	out->seq	= in->seq;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/**
61862306a36Sopenharmony_ci * ipc_obtain_object_idr
61962306a36Sopenharmony_ci * @ids: ipc identifier set
62062306a36Sopenharmony_ci * @id: ipc id to look for
62162306a36Sopenharmony_ci *
62262306a36Sopenharmony_ci * Look for an id in the ipc ids idr and return associated ipc object.
62362306a36Sopenharmony_ci *
62462306a36Sopenharmony_ci * Call inside the RCU critical section.
62562306a36Sopenharmony_ci * The ipc object is *not* locked on exit.
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_cistruct kern_ipc_perm *ipc_obtain_object_idr(struct ipc_ids *ids, int id)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct kern_ipc_perm *out;
63062306a36Sopenharmony_ci	int idx = ipcid_to_idx(id);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	out = idr_find(&ids->ipcs_idr, idx);
63362306a36Sopenharmony_ci	if (!out)
63462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return out;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/**
64062306a36Sopenharmony_ci * ipc_obtain_object_check
64162306a36Sopenharmony_ci * @ids: ipc identifier set
64262306a36Sopenharmony_ci * @id: ipc id to look for
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * Similar to ipc_obtain_object_idr() but also checks the ipc object
64562306a36Sopenharmony_ci * sequence number.
64662306a36Sopenharmony_ci *
64762306a36Sopenharmony_ci * Call inside the RCU critical section.
64862306a36Sopenharmony_ci * The ipc object is *not* locked on exit.
64962306a36Sopenharmony_ci */
65062306a36Sopenharmony_cistruct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct kern_ipc_perm *out = ipc_obtain_object_idr(ids, id);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (IS_ERR(out))
65562306a36Sopenharmony_ci		goto out;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (ipc_checkid(out, id))
65862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
65962306a36Sopenharmony_ciout:
66062306a36Sopenharmony_ci	return out;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci/**
66462306a36Sopenharmony_ci * ipcget - Common sys_*get() code
66562306a36Sopenharmony_ci * @ns: namespace
66662306a36Sopenharmony_ci * @ids: ipc identifier set
66762306a36Sopenharmony_ci * @ops: operations to be called on ipc object creation, permission checks
66862306a36Sopenharmony_ci *       and further checks
66962306a36Sopenharmony_ci * @params: the parameters needed by the previous operations.
67062306a36Sopenharmony_ci *
67162306a36Sopenharmony_ci * Common routine called by sys_msgget(), sys_semget() and sys_shmget().
67262306a36Sopenharmony_ci */
67362306a36Sopenharmony_ciint ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
67462306a36Sopenharmony_ci			const struct ipc_ops *ops, struct ipc_params *params)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	if (params->key == IPC_PRIVATE)
67762306a36Sopenharmony_ci		return ipcget_new(ns, ids, ops, params);
67862306a36Sopenharmony_ci	else
67962306a36Sopenharmony_ci		return ipcget_public(ns, ids, ops, params);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/**
68362306a36Sopenharmony_ci * ipc_update_perm - update the permissions of an ipc object
68462306a36Sopenharmony_ci * @in:  the permission given as input.
68562306a36Sopenharmony_ci * @out: the permission of the ipc to set.
68662306a36Sopenharmony_ci */
68762306a36Sopenharmony_ciint ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	kuid_t uid = make_kuid(current_user_ns(), in->uid);
69062306a36Sopenharmony_ci	kgid_t gid = make_kgid(current_user_ns(), in->gid);
69162306a36Sopenharmony_ci	if (!uid_valid(uid) || !gid_valid(gid))
69262306a36Sopenharmony_ci		return -EINVAL;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	out->uid = uid;
69562306a36Sopenharmony_ci	out->gid = gid;
69662306a36Sopenharmony_ci	out->mode = (out->mode & ~S_IRWXUGO)
69762306a36Sopenharmony_ci		| (in->mode & S_IRWXUGO);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return 0;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci/**
70362306a36Sopenharmony_ci * ipcctl_obtain_check - retrieve an ipc object and check permissions
70462306a36Sopenharmony_ci * @ns:  ipc namespace
70562306a36Sopenharmony_ci * @ids:  the table of ids where to look for the ipc
70662306a36Sopenharmony_ci * @id:   the id of the ipc to retrieve
70762306a36Sopenharmony_ci * @cmd:  the cmd to check
70862306a36Sopenharmony_ci * @perm: the permission to set
70962306a36Sopenharmony_ci * @extra_perm: one extra permission parameter used by msq
71062306a36Sopenharmony_ci *
71162306a36Sopenharmony_ci * This function does some common audit and permissions check for some IPC_XXX
71262306a36Sopenharmony_ci * cmd and is called from semctl_down, shmctl_down and msgctl_down.
71362306a36Sopenharmony_ci *
71462306a36Sopenharmony_ci * It:
71562306a36Sopenharmony_ci *   - retrieves the ipc object with the given id in the given table.
71662306a36Sopenharmony_ci *   - performs some audit and permission check, depending on the given cmd
71762306a36Sopenharmony_ci *   - returns a pointer to the ipc object or otherwise, the corresponding
71862306a36Sopenharmony_ci *     error.
71962306a36Sopenharmony_ci *
72062306a36Sopenharmony_ci * Call holding the both the rwsem and the rcu read lock.
72162306a36Sopenharmony_ci */
72262306a36Sopenharmony_cistruct kern_ipc_perm *ipcctl_obtain_check(struct ipc_namespace *ns,
72362306a36Sopenharmony_ci					struct ipc_ids *ids, int id, int cmd,
72462306a36Sopenharmony_ci					struct ipc64_perm *perm, int extra_perm)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	kuid_t euid;
72762306a36Sopenharmony_ci	int err = -EPERM;
72862306a36Sopenharmony_ci	struct kern_ipc_perm *ipcp;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	ipcp = ipc_obtain_object_check(ids, id);
73162306a36Sopenharmony_ci	if (IS_ERR(ipcp)) {
73262306a36Sopenharmony_ci		err = PTR_ERR(ipcp);
73362306a36Sopenharmony_ci		goto err;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	audit_ipc_obj(ipcp);
73762306a36Sopenharmony_ci	if (cmd == IPC_SET)
73862306a36Sopenharmony_ci		audit_ipc_set_perm(extra_perm, perm->uid,
73962306a36Sopenharmony_ci				   perm->gid, perm->mode);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	euid = current_euid();
74262306a36Sopenharmony_ci	if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid)  ||
74362306a36Sopenharmony_ci	    ns_capable(ns->user_ns, CAP_SYS_ADMIN))
74462306a36Sopenharmony_ci		return ipcp; /* successful lookup */
74562306a36Sopenharmony_cierr:
74662306a36Sopenharmony_ci	return ERR_PTR(err);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci/**
75362306a36Sopenharmony_ci * ipc_parse_version - ipc call version
75462306a36Sopenharmony_ci * @cmd: pointer to command
75562306a36Sopenharmony_ci *
75662306a36Sopenharmony_ci * Return IPC_64 for new style IPC and IPC_OLD for old style IPC.
75762306a36Sopenharmony_ci * The @cmd value is turned from an encoding command and version into
75862306a36Sopenharmony_ci * just the command code.
75962306a36Sopenharmony_ci */
76062306a36Sopenharmony_ciint ipc_parse_version(int *cmd)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	if (*cmd & IPC_64) {
76362306a36Sopenharmony_ci		*cmd ^= IPC_64;
76462306a36Sopenharmony_ci		return IPC_64;
76562306a36Sopenharmony_ci	} else {
76662306a36Sopenharmony_ci		return IPC_OLD;
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci#endif /* CONFIG_ARCH_WANT_IPC_PARSE_VERSION */
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
77362306a36Sopenharmony_cistruct ipc_proc_iter {
77462306a36Sopenharmony_ci	struct ipc_namespace *ns;
77562306a36Sopenharmony_ci	struct pid_namespace *pid_ns;
77662306a36Sopenharmony_ci	struct ipc_proc_iface *iface;
77762306a36Sopenharmony_ci};
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistruct pid_namespace *ipc_seq_pid_ns(struct seq_file *s)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct ipc_proc_iter *iter = s->private;
78262306a36Sopenharmony_ci	return iter->pid_ns;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci/**
78662306a36Sopenharmony_ci * sysvipc_find_ipc - Find and lock the ipc structure based on seq pos
78762306a36Sopenharmony_ci * @ids: ipc identifier set
78862306a36Sopenharmony_ci * @pos: expected position
78962306a36Sopenharmony_ci *
79062306a36Sopenharmony_ci * The function finds an ipc structure, based on the sequence file
79162306a36Sopenharmony_ci * position @pos. If there is no ipc structure at position @pos, then
79262306a36Sopenharmony_ci * the successor is selected.
79362306a36Sopenharmony_ci * If a structure is found, then it is locked (both rcu_read_lock() and
79462306a36Sopenharmony_ci * ipc_lock_object()) and  @pos is set to the position needed to locate
79562306a36Sopenharmony_ci * the found ipc structure.
79662306a36Sopenharmony_ci * If nothing is found (i.e. EOF), @pos is not modified.
79762306a36Sopenharmony_ci *
79862306a36Sopenharmony_ci * The function returns the found ipc structure, or NULL at EOF.
79962306a36Sopenharmony_ci */
80062306a36Sopenharmony_cistatic struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t *pos)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	int tmpidx;
80362306a36Sopenharmony_ci	struct kern_ipc_perm *ipc;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/* convert from position to idr index -> "-1" */
80662306a36Sopenharmony_ci	tmpidx = *pos - 1;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	ipc = idr_get_next(&ids->ipcs_idr, &tmpidx);
80962306a36Sopenharmony_ci	if (ipc != NULL) {
81062306a36Sopenharmony_ci		rcu_read_lock();
81162306a36Sopenharmony_ci		ipc_lock_object(ipc);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		/* convert from idr index to position  -> "+1" */
81462306a36Sopenharmony_ci		*pos = tmpidx + 1;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci	return ipc;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct ipc_proc_iter *iter = s->private;
82262306a36Sopenharmony_ci	struct ipc_proc_iface *iface = iter->iface;
82362306a36Sopenharmony_ci	struct kern_ipc_perm *ipc = it;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/* If we had an ipc id locked before, unlock it */
82662306a36Sopenharmony_ci	if (ipc && ipc != SEQ_START_TOKEN)
82762306a36Sopenharmony_ci		ipc_unlock(ipc);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	/* Next -> search for *pos+1 */
83062306a36Sopenharmony_ci	(*pos)++;
83162306a36Sopenharmony_ci	return sysvipc_find_ipc(&iter->ns->ids[iface->ids], pos);
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci/*
83562306a36Sopenharmony_ci * File positions: pos 0 -> header, pos n -> ipc idx = n - 1.
83662306a36Sopenharmony_ci * SeqFile iterator: iterator value locked ipc pointer or SEQ_TOKEN_START.
83762306a36Sopenharmony_ci */
83862306a36Sopenharmony_cistatic void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci	struct ipc_proc_iter *iter = s->private;
84162306a36Sopenharmony_ci	struct ipc_proc_iface *iface = iter->iface;
84262306a36Sopenharmony_ci	struct ipc_ids *ids;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	ids = &iter->ns->ids[iface->ids];
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/*
84762306a36Sopenharmony_ci	 * Take the lock - this will be released by the corresponding
84862306a36Sopenharmony_ci	 * call to stop().
84962306a36Sopenharmony_ci	 */
85062306a36Sopenharmony_ci	down_read(&ids->rwsem);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* pos < 0 is invalid */
85362306a36Sopenharmony_ci	if (*pos < 0)
85462306a36Sopenharmony_ci		return NULL;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/* pos == 0 means header */
85762306a36Sopenharmony_ci	if (*pos == 0)
85862306a36Sopenharmony_ci		return SEQ_START_TOKEN;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/* Otherwise return the correct ipc structure */
86162306a36Sopenharmony_ci	return sysvipc_find_ipc(ids, pos);
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic void sysvipc_proc_stop(struct seq_file *s, void *it)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct kern_ipc_perm *ipc = it;
86762306a36Sopenharmony_ci	struct ipc_proc_iter *iter = s->private;
86862306a36Sopenharmony_ci	struct ipc_proc_iface *iface = iter->iface;
86962306a36Sopenharmony_ci	struct ipc_ids *ids;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* If we had a locked structure, release it */
87262306a36Sopenharmony_ci	if (ipc && ipc != SEQ_START_TOKEN)
87362306a36Sopenharmony_ci		ipc_unlock(ipc);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	ids = &iter->ns->ids[iface->ids];
87662306a36Sopenharmony_ci	/* Release the lock we took in start() */
87762306a36Sopenharmony_ci	up_read(&ids->rwsem);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int sysvipc_proc_show(struct seq_file *s, void *it)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	struct ipc_proc_iter *iter = s->private;
88362306a36Sopenharmony_ci	struct ipc_proc_iface *iface = iter->iface;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (it == SEQ_START_TOKEN) {
88662306a36Sopenharmony_ci		seq_puts(s, iface->header);
88762306a36Sopenharmony_ci		return 0;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	return iface->show(s, it);
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic const struct seq_operations sysvipc_proc_seqops = {
89462306a36Sopenharmony_ci	.start = sysvipc_proc_start,
89562306a36Sopenharmony_ci	.stop  = sysvipc_proc_stop,
89662306a36Sopenharmony_ci	.next  = sysvipc_proc_next,
89762306a36Sopenharmony_ci	.show  = sysvipc_proc_show,
89862306a36Sopenharmony_ci};
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic int sysvipc_proc_open(struct inode *inode, struct file *file)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct ipc_proc_iter *iter;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	iter = __seq_open_private(file, &sysvipc_proc_seqops, sizeof(*iter));
90562306a36Sopenharmony_ci	if (!iter)
90662306a36Sopenharmony_ci		return -ENOMEM;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	iter->iface = pde_data(inode);
90962306a36Sopenharmony_ci	iter->ns    = get_ipc_ns(current->nsproxy->ipc_ns);
91062306a36Sopenharmony_ci	iter->pid_ns = get_pid_ns(task_active_pid_ns(current));
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	return 0;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_cistatic int sysvipc_proc_release(struct inode *inode, struct file *file)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	struct seq_file *seq = file->private_data;
91862306a36Sopenharmony_ci	struct ipc_proc_iter *iter = seq->private;
91962306a36Sopenharmony_ci	put_ipc_ns(iter->ns);
92062306a36Sopenharmony_ci	put_pid_ns(iter->pid_ns);
92162306a36Sopenharmony_ci	return seq_release_private(inode, file);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic const struct proc_ops sysvipc_proc_ops = {
92562306a36Sopenharmony_ci	.proc_flags	= PROC_ENTRY_PERMANENT,
92662306a36Sopenharmony_ci	.proc_open	= sysvipc_proc_open,
92762306a36Sopenharmony_ci	.proc_read	= seq_read,
92862306a36Sopenharmony_ci	.proc_lseek	= seq_lseek,
92962306a36Sopenharmony_ci	.proc_release	= sysvipc_proc_release,
93062306a36Sopenharmony_ci};
93162306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */
932