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