* Occurs in several places in the IPC code.
* Chris Evans, <chris@ferret.lmh.ox.ac.uk>
* Nov 1999 - ipc helper functions, unified SMP locking
- * Manfred Spraul <manfreds@colorfullife.com>
+ * Manfred Spraul <manfred@colorfullife.com>
* Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
* Mingming Cao <cmm@us.ibm.com>
+ * Mar 2006 - support for audit of ipc object properties
+ * Dustin Kirkland <dustin.kirkland@us.ibm.com>
+ * Jun 2006 - namespaces ssupport
+ * OpenVZ, SWsoft Inc.
+ * Pavel Emelianov <xemul@openvz.org>
*/
-#include <linux/config.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/init.h>
#include <linux/msg.h>
-#include <linux/smp_lock.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
+#include <linux/capability.h>
#include <linux/highuid.h>
#include <linux/security.h>
#include <linux/rcupdate.h>
#include <linux/workqueue.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
+#include <linux/audit.h>
+#include <linux/nsproxy.h>
#include <asm/unistd.h>
struct ipc_proc_iface {
const char *path;
const char *header;
- struct ipc_ids *ids;
+ int ids;
int (*show)(struct seq_file *, void *);
};
+struct ipc_namespace init_ipc_ns = {
+ .kref = {
+ .refcount = ATOMIC_INIT(2),
+ },
+};
+
+static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
+{
+ int err;
+ struct ipc_namespace *ns;
+
+ err = -ENOMEM;
+ ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
+ if (ns == NULL)
+ goto err_mem;
+
+ err = sem_init_ns(ns);
+ if (err)
+ goto err_sem;
+ err = msg_init_ns(ns);
+ if (err)
+ goto err_msg;
+ err = shm_init_ns(ns);
+ if (err)
+ goto err_shm;
+
+ kref_init(&ns->kref);
+ return ns;
+
+err_shm:
+ msg_exit_ns(ns);
+err_msg:
+ sem_exit_ns(ns);
+err_sem:
+ kfree(ns);
+err_mem:
+ return ERR_PTR(err);
+}
+
+struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns)
+{
+ struct ipc_namespace *new_ns;
+
+ BUG_ON(!ns);
+ get_ipc_ns(ns);
+
+ if (!(flags & CLONE_NEWIPC))
+ return ns;
+
+ new_ns = clone_ipc_ns(ns);
+
+ put_ipc_ns(ns);
+ return new_ns;
+}
+
+void free_ipc_ns(struct kref *kref)
+{
+ struct ipc_namespace *ns;
+
+ ns = container_of(kref, struct ipc_namespace, kref);
+ sem_exit_ns(ns);
+ msg_exit_ns(ns);
+ shm_exit_ns(ns);
+ kfree(ns);
+}
+
/**
* ipc_init - initialise IPC subsystem
*
* The various system5 IPC resources (semaphores, messages and shared
- * memory are initialised
+ * memory) are initialised
*/
static int __init ipc_init(void)
* array itself.
*/
-void __init ipc_init_ids(struct ipc_ids* ids, int size)
+void ipc_init_ids(struct ipc_ids* ids, int size)
{
int i;
- sema_init(&ids->sem,1);
+
+ mutex_init(&ids->mutex);
if(size > IPCMNI)
size = IPCMNI;
}
#ifdef CONFIG_PROC_FS
-static struct file_operations sysvipc_proc_fops;
+static const struct file_operations sysvipc_proc_fops;
/**
- * ipc_init_proc_interface - Create a proc interface for sysipc types
- * using a seq_file interface.
+ * ipc_init_proc_interface - Create a proc interface for sysipc types using a seq_file interface.
* @path: Path in procfs
* @header: Banner to be printed at the beginning of the file.
* @ids: ipc id table to iterate.
* @show: show routine.
*/
void __init ipc_init_proc_interface(const char *path, const char *header,
- struct ipc_ids *ids,
- int (*show)(struct seq_file *, void *))
+ int ids, int (*show)(struct seq_file *, void *))
{
struct proc_dir_entry *pde;
struct ipc_proc_iface *iface;
* @ids: Identifier set
* @key: The key to find
*
- * Requires ipc_ids.sem locked.
+ * Requires ipc_ids.mutex locked.
* Returns the identifier if found or -1 if not.
*/
/*
* rcu_dereference() is not needed here
- * since ipc_ids.sem is held
+ * since ipc_ids.mutex is held
*/
for (id = 0; id <= max_id; id++) {
p = ids->entries->p[id];
}
/*
- * Requires ipc_ids.sem locked
+ * Requires ipc_ids.mutex locked
*/
static int grow_ary(struct ipc_ids* ids, int newsize)
{
if(new == NULL)
return size;
new->size = newsize;
- memcpy(new->p, ids->entries->p, sizeof(struct kern_ipc_perm *)*size +
- sizeof(struct ipc_id_ary));
+ memcpy(new->p, ids->entries->p, sizeof(struct kern_ipc_perm *)*size);
for(i=size;i<newsize;i++) {
new->p[i] = NULL;
}
*/
rcu_assign_pointer(ids->entries, new);
- ipc_rcu_putref(old);
+ __ipc_fini_ids(ids, old);
return newsize;
}
* is returned. The list is returned in a locked state on success.
* On failure the list is not locked and -1 is returned.
*
- * Called with ipc_ids.sem held.
+ * Called with ipc_ids.mutex held.
*/
int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
/*
* rcu_dereference()() is not needed here since
- * ipc_ids.sem is held
+ * ipc_ids.mutex is held
*/
for (id = 0; id < size; id++) {
if(ids->entries->p[id] == NULL)
* fed an invalid identifier. The entry is removed and internal
* variables recomputed. The object associated with the identifier
* is returned.
- * ipc_ids.sem and the spinlock for this ID is hold before this function
+ * ipc_ids.mutex and the spinlock for this ID is hold before this function
* is called, and remain locked on the exit.
*/
{
struct kern_ipc_perm* p;
int lid = id % SEQ_MULTIPLIER;
- if(lid >= ids->entries->size)
- BUG();
+ BUG_ON(lid >= ids->entries->size);
/*
* do not need a rcu_dereference()() here to force ordering
- * on Alpha, since the ipc_ids.sem is held.
+ * on Alpha, since the ipc_ids.mutex is held.
*/
p = ids->entries->p[lid];
ids->entries->p[lid] = NULL;
- if(p==NULL)
- BUG();
+ BUG_ON(p==NULL);
ids->in_use--;
if (lid == ids->max_id) {
* @ptr: pointer returned by ipc_alloc
* @size: size of block
*
- * Free a block created with ipc_alloc. The caller must know the size
+ * Free a block created with ipc_alloc(). The caller must know the size
* used in the allocation call.
*/
container_of(ptr, struct ipc_rcu_hdr, data)->refcount++;
}
+static void ipc_do_vfree(struct work_struct *work)
+{
+ vfree(container_of(work, struct ipc_rcu_sched, work));
+}
+
/**
* ipc_schedule_free - free ipc + rcu space
* @head: RCU callback structure for queued work
*
* Since RCU callback function is called in bh,
- * we need to defer the vfree to schedule_work
+ * we need to defer the vfree to schedule_work().
*/
static void ipc_schedule_free(struct rcu_head *head)
{
struct ipc_rcu_sched *sched =
container_of(&(grace->data[0]), struct ipc_rcu_sched, data[0]);
- INIT_WORK(&sched->work, vfree, sched);
+ INIT_WORK(&sched->work, ipc_do_vfree);
schedule_work(&sched->work);
}
* ipc_immediate_free - free ipc + rcu space
* @head: RCU callback structure that contains pointer to be freed
*
- * Free from the RCU callback context
+ * Free from the RCU callback context.
*/
static void ipc_immediate_free(struct rcu_head *head)
{
int ipcperms (struct kern_ipc_perm *ipcp, short flag)
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
- int requested_mode, granted_mode;
+ int requested_mode, granted_mode, err;
+ if (unlikely((err = audit_ipc_obj(ipcp))))
+ return err;
requested_mode = (flag >> 6) | (flag >> 3) | flag;
granted_mode = ipcp->mode;
if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
* @in: kernel permissions
* @out: new style IPC permissions
*
- * Turn the kernel object 'in' into a set of permissions descriptions
- * for returning to userspace (out).
+ * Turn the kernel object @in into a set of permissions descriptions
+ * for returning to userspace (@out).
*/
* @in: new style IPC permissions
* @out: old style IPC permissions
*
- * Turn the new style permissions object in into a compatibility
- * object and store it into the 'out' pointer.
+ * Turn the new style permissions object @in into a compatibility
+ * object and store it into the @out pointer.
*/
void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
/*
* So far only shm_get_stat() calls ipc_get() via shm_get(), so ipc_get()
- * is called with shm_ids.sem locked. Since grow_ary() is also called with
- * shm_ids.sem down(for Shared Memory), there is no need to add read
+ * is called with shm_ids.mutex locked. Since grow_ary() is also called with
+ * shm_ids.mutex down(for Shared Memory), there is no need to add read
* barriers here to gurantee the writes in grow_ary() are seen in order
* here (for Alpha).
*
- * However ipc_get() itself does not necessary require ipc_ids.sem down. So
- * if in the future ipc_get() is used by other places without ipc_ids.sem
+ * However ipc_get() itself does not necessary require ipc_ids.mutex down. So
+ * if in the future ipc_get() is used by other places without ipc_ids.mutex
* down, then ipc_get() needs read memery barriers as ipc_lock() does.
*/
struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
* @cmd: pointer to command
*
* Return IPC_64 for new style IPC and IPC_OLD for old style IPC.
- * The cmd value is turned from an encoding command and version into
+ * The @cmd value is turned from an encoding command and version into
* just the command code.
*/
#endif /* __ARCH_WANT_IPC_PARSE_VERSION */
#ifdef CONFIG_PROC_FS
+struct ipc_proc_iter {
+ struct ipc_namespace *ns;
+ struct ipc_proc_iface *iface;
+};
+
static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos)
{
- struct ipc_proc_iface *iface = s->private;
+ struct ipc_proc_iter *iter = s->private;
+ struct ipc_proc_iface *iface = iter->iface;
struct kern_ipc_perm *ipc = it;
loff_t p;
+ struct ipc_ids *ids;
+
+ ids = iter->ns->ids[iface->ids];
/* If we had an ipc id locked before, unlock it */
if (ipc && ipc != SEQ_START_TOKEN)
* p = *pos - 1 (because id 0 starts at position 1)
* + 1 (because we increment the position by one)
*/
- for (p = *pos; p <= iface->ids->max_id; p++) {
- if ((ipc = ipc_lock(iface->ids, p)) != NULL) {
+ for (p = *pos; p <= ids->max_id; p++) {
+ if ((ipc = ipc_lock(ids, p)) != NULL) {
*pos = p + 1;
return ipc;
}
*/
static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
{
- struct ipc_proc_iface *iface = s->private;
+ struct ipc_proc_iter *iter = s->private;
+ struct ipc_proc_iface *iface = iter->iface;
struct kern_ipc_perm *ipc;
loff_t p;
+ struct ipc_ids *ids;
+
+ ids = iter->ns->ids[iface->ids];
/*
* Take the lock - this will be released by the corresponding
* call to stop().
*/
- down(&iface->ids->sem);
+ mutex_lock(&ids->mutex);
/* pos < 0 is invalid */
if (*pos < 0)
return SEQ_START_TOKEN;
/* Find the (pos-1)th ipc */
- for (p = *pos - 1; p <= iface->ids->max_id; p++) {
- if ((ipc = ipc_lock(iface->ids, p)) != NULL) {
+ for (p = *pos - 1; p <= ids->max_id; p++) {
+ if ((ipc = ipc_lock(ids, p)) != NULL) {
*pos = p + 1;
return ipc;
}
static void sysvipc_proc_stop(struct seq_file *s, void *it)
{
struct kern_ipc_perm *ipc = it;
- struct ipc_proc_iface *iface = s->private;
+ struct ipc_proc_iter *iter = s->private;
+ struct ipc_proc_iface *iface = iter->iface;
+ struct ipc_ids *ids;
/* If we had a locked segment, release it */
if (ipc && ipc != SEQ_START_TOKEN)
ipc_unlock(ipc);
+ ids = iter->ns->ids[iface->ids];
/* Release the lock we took in start() */
- up(&iface->ids->sem);
+ mutex_unlock(&ids->mutex);
}
static int sysvipc_proc_show(struct seq_file *s, void *it)
{
- struct ipc_proc_iface *iface = s->private;
+ struct ipc_proc_iter *iter = s->private;
+ struct ipc_proc_iface *iface = iter->iface;
if (it == SEQ_START_TOKEN)
return seq_puts(s, iface->header);
.show = sysvipc_proc_show,
};
-static int sysvipc_proc_open(struct inode *inode, struct file *file) {
+static int sysvipc_proc_open(struct inode *inode, struct file *file)
+{
int ret;
struct seq_file *seq;
+ struct ipc_proc_iter *iter;
+
+ ret = -ENOMEM;
+ iter = kmalloc(sizeof(*iter), GFP_KERNEL);
+ if (!iter)
+ goto out;
ret = seq_open(file, &sysvipc_proc_seqops);
- if (!ret) {
- seq = file->private_data;
- seq->private = PDE(inode)->data;
- }
+ if (ret)
+ goto out_kfree;
+
+ seq = file->private_data;
+ seq->private = iter;
+
+ iter->iface = PDE(inode)->data;
+ iter->ns = get_ipc_ns(current->nsproxy->ipc_ns);
+out:
return ret;
+out_kfree:
+ kfree(iter);
+ goto out;
+}
+
+static int sysvipc_proc_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq = file->private_data;
+ struct ipc_proc_iter *iter = seq->private;
+ put_ipc_ns(iter->ns);
+ return seq_release_private(inode, file);
}
-static struct file_operations sysvipc_proc_fops = {
+static const struct file_operations sysvipc_proc_fops = {
.open = sysvipc_proc_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = sysvipc_proc_release,
};
#endif /* CONFIG_PROC_FS */