Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3893534 authored by Kirill Korotaev's avatar Kirill Korotaev Committed by Linus Torvalds
Browse files

[PATCH] IPC namespace - sem



IPC namespace support for IPC sem code.

Signed-off-by: default avatarPavel Emelianiov <xemul@openvz.org>
Signed-off-by: default avatarKirill Korotaev <dev@openvz.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 1e786937
Loading
Loading
Loading
Loading
+130 −71
Original line number Diff line number Diff line
@@ -64,6 +64,10 @@
 *
 * support for audit of ipc object properties and permission changes
 * Dustin Kirkland <dustin.kirkland@us.ibm.com>
 *
 * namespaces support
 * OpenVZ, SWsoft Inc.
 * Pavel Emelianov <xemul@openvz.org>
 */

#include <linux/slab.h>
@@ -78,22 +82,25 @@
#include <linux/capability.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/nsproxy.h>

#include <asm/uaccess.h>
#include "util.h"

#define sem_ids(ns)	(*((ns)->ids[IPC_SEM_IDS]))

#define sem_lock(id)	((struct sem_array*)ipc_lock(&sem_ids,id))
#define sem_lock(ns, id)	((struct sem_array*)ipc_lock(&sem_ids(ns), id))
#define sem_unlock(sma)		ipc_unlock(&(sma)->sem_perm)
#define sem_rmid(id)	((struct sem_array*)ipc_rmid(&sem_ids,id))
#define sem_checkid(sma, semid)	\
	ipc_checkid(&sem_ids,&sma->sem_perm,semid)
#define sem_buildid(id, seq) \
	ipc_buildid(&sem_ids, id, seq)
static struct ipc_ids sem_ids;

static int newary (key_t, int, int);
static void freeary (struct sem_array *sma, int id);
#define sem_rmid(ns, id)	((struct sem_array*)ipc_rmid(&sem_ids(ns), id))
#define sem_checkid(ns, sma, semid)	\
	ipc_checkid(&sem_ids(ns),&sma->sem_perm,semid)
#define sem_buildid(ns, id, seq) \
	ipc_buildid(&sem_ids(ns), id, seq)

static struct ipc_ids init_sem_ids;

static int newary(struct ipc_namespace *, key_t, int, int);
static void freeary(struct ipc_namespace *ns, struct sem_array *sma, int id);
#ifdef CONFIG_PROC_FS
static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
#endif
@@ -110,22 +117,61 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
 *	
 */

int sem_ctls[4] = {SEMMSL, SEMMNS, SEMOPM, SEMMNI};
#define sc_semmsl	(sem_ctls[0])
#define sc_semmns	(sem_ctls[1])
#define sc_semopm	(sem_ctls[2])
#define sc_semmni	(sem_ctls[3])
#define sc_semmsl	sem_ctls[0]
#define sc_semmns	sem_ctls[1]
#define sc_semopm	sem_ctls[2]
#define sc_semmni	sem_ctls[3]

static void __ipc_init __sem_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids)
{
	ns->ids[IPC_SEM_IDS] = ids;
	ns->sc_semmsl = SEMMSL;
	ns->sc_semmns = SEMMNS;
	ns->sc_semopm = SEMOPM;
	ns->sc_semmni = SEMMNI;
	ns->used_sems = 0;
	ipc_init_ids(ids, ns->sc_semmni);
}

#ifdef CONFIG_IPC_NS
int sem_init_ns(struct ipc_namespace *ns)
{
	struct ipc_ids *ids;

	ids = kmalloc(sizeof(struct ipc_ids), GFP_KERNEL);
	if (ids == NULL)
		return -ENOMEM;

	__sem_init_ns(ns, ids);
	return 0;
}

void sem_exit_ns(struct ipc_namespace *ns)
{
	int i;
	struct sem_array *sma;

static int used_sems;
	mutex_lock(&sem_ids(ns).mutex);
	for (i = 0; i <= sem_ids(ns).max_id; i++) {
		sma = sem_lock(ns, i);
		if (sma == NULL)
			continue;

		freeary(ns, sma, i);
	}
	mutex_unlock(&sem_ids(ns).mutex);

	kfree(ns->ids[IPC_SEM_IDS]);
	ns->ids[IPC_SEM_IDS] = NULL;
}
#endif

void __init sem_init (void)
{
	used_sems = 0;
	ipc_init_ids(&sem_ids,sc_semmni);
	__sem_init_ns(&init_ipc_ns, &init_sem_ids);
	ipc_init_proc_interface("sysvipc/sem",
				"       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime\n",
				&sem_ids,
				sysvipc_sem_proc_show);
				IPC_SEM_IDS, sysvipc_sem_proc_show);
}

/*
@@ -162,7 +208,7 @@ void __init sem_init (void)
 */
#define IN_WAKEUP	1

static int newary (key_t key, int nsems, int semflg)
static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg)
{
	int id;
	int retval;
@@ -171,7 +217,7 @@ static int newary (key_t key, int nsems, int semflg)

	if (!nsems)
		return -EINVAL;
	if (used_sems + nsems > sc_semmns)
	if (ns->used_sems + nsems > ns->sc_semmns)
		return -ENOSPC;

	size = sizeof (*sma) + nsems * sizeof (struct sem);
@@ -191,15 +237,15 @@ static int newary (key_t key, int nsems, int semflg)
		return retval;
	}

	id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni);
	id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
	if(id == -1) {
		security_sem_free(sma);
		ipc_rcu_putref(sma);
		return -ENOSPC;
	}
	used_sems += nsems;
	ns->used_sems += nsems;

	sma->sem_id = sem_buildid(id, sma->sem_perm.seq);
	sma->sem_id = sem_buildid(ns, id, sma->sem_perm.seq);
	sma->sem_base = (struct sem *) &sma[1];
	/* sma->sem_pending = NULL; */
	sma->sem_pending_last = &sma->sem_pending;
@@ -215,29 +261,32 @@ asmlinkage long sys_semget (key_t key, int nsems, int semflg)
{
	int id, err = -EINVAL;
	struct sem_array *sma;
	struct ipc_namespace *ns;

	ns = current->nsproxy->ipc_ns;

	if (nsems < 0 || nsems > sc_semmsl)
	if (nsems < 0 || nsems > ns->sc_semmsl)
		return -EINVAL;
	mutex_lock(&sem_ids.mutex);
	mutex_lock(&sem_ids(ns).mutex);
	
	if (key == IPC_PRIVATE) {
		err = newary(key, nsems, semflg);
	} else if ((id = ipc_findkey(&sem_ids, key)) == -1) {  /* key not used */
		err = newary(ns, key, nsems, semflg);
	} else if ((id = ipc_findkey(&sem_ids(ns), key)) == -1) {  /* key not used */
		if (!(semflg & IPC_CREAT))
			err = -ENOENT;
		else
			err = newary(key, nsems, semflg);
			err = newary(ns, key, nsems, semflg);
	} else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
		err = -EEXIST;
	} else {
		sma = sem_lock(id);
		sma = sem_lock(ns, id);
		BUG_ON(sma==NULL);
		if (nsems > sma->sem_nsems)
			err = -EINVAL;
		else if (ipcperms(&sma->sem_perm, semflg))
			err = -EACCES;
		else {
			int semid = sem_buildid(id, sma->sem_perm.seq);
			int semid = sem_buildid(ns, id, sma->sem_perm.seq);
			err = security_sem_associate(sma, semflg);
			if (!err)
				err = semid;
@@ -245,7 +294,7 @@ asmlinkage long sys_semget (key_t key, int nsems, int semflg)
		sem_unlock(sma);
	}

	mutex_unlock(&sem_ids.mutex);
	mutex_unlock(&sem_ids(ns).mutex);
	return err;
}

@@ -444,7 +493,7 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum)
 * the spinlock for this semaphore set hold. sem_ids.mutex remains locked
 * on exit.
 */
static void freeary (struct sem_array *sma, int id)
static void freeary (struct ipc_namespace *ns, struct sem_array *sma, int id)
{
	struct sem_undo *un;
	struct sem_queue *q;
@@ -472,10 +521,10 @@ static void freeary (struct sem_array *sma, int id)
	}

	/* Remove the semaphore set from the ID array*/
	sma = sem_rmid(id);
	sma = sem_rmid(ns, id);
	sem_unlock(sma);

	used_sems -= sma->sem_nsems;
	ns->used_sems -= sma->sem_nsems;
	size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem);
	security_sem_free(sma);
	ipc_rcu_putref(sma);
@@ -503,7 +552,8 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in,
	}
}

static int semctl_nolock(int semid, int semnum, int cmd, int version, union semun arg)
static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum,
		int cmd, int version, union semun arg)
{
	int err = -EINVAL;
	struct sem_array *sma;
@@ -520,24 +570,24 @@ static int semctl_nolock(int semid, int semnum, int cmd, int version, union semu
			return err;
		
		memset(&seminfo,0,sizeof(seminfo));
		seminfo.semmni = sc_semmni;
		seminfo.semmns = sc_semmns;
		seminfo.semmsl = sc_semmsl;
		seminfo.semopm = sc_semopm;
		seminfo.semmni = ns->sc_semmni;
		seminfo.semmns = ns->sc_semmns;
		seminfo.semmsl = ns->sc_semmsl;
		seminfo.semopm = ns->sc_semopm;
		seminfo.semvmx = SEMVMX;
		seminfo.semmnu = SEMMNU;
		seminfo.semmap = SEMMAP;
		seminfo.semume = SEMUME;
		mutex_lock(&sem_ids.mutex);
		mutex_lock(&sem_ids(ns).mutex);
		if (cmd == SEM_INFO) {
			seminfo.semusz = sem_ids.in_use;
			seminfo.semaem = used_sems;
			seminfo.semusz = sem_ids(ns).in_use;
			seminfo.semaem = ns->used_sems;
		} else {
			seminfo.semusz = SEMUSZ;
			seminfo.semaem = SEMAEM;
		}
		max_id = sem_ids.max_id;
		mutex_unlock(&sem_ids.mutex);
		max_id = sem_ids(ns).max_id;
		mutex_unlock(&sem_ids(ns).mutex);
		if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) 
			return -EFAULT;
		return (max_id < 0) ? 0: max_id;
@@ -547,12 +597,12 @@ static int semctl_nolock(int semid, int semnum, int cmd, int version, union semu
		struct semid64_ds tbuf;
		int id;

		if(semid >= sem_ids.entries->size)
		if(semid >= sem_ids(ns).entries->size)
			return -EINVAL;

		memset(&tbuf,0,sizeof(tbuf));

		sma = sem_lock(semid);
		sma = sem_lock(ns, semid);
		if(sma == NULL)
			return -EINVAL;

@@ -564,7 +614,7 @@ static int semctl_nolock(int semid, int semnum, int cmd, int version, union semu
		if (err)
			goto out_unlock;

		id = sem_buildid(semid, sma->sem_perm.seq);
		id = sem_buildid(ns, semid, sma->sem_perm.seq);

		kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
		tbuf.sem_otime  = sma->sem_otime;
@@ -584,7 +634,8 @@ out_unlock:
	return err;
}

static int semctl_main(int semid, int semnum, int cmd, int version, union semun arg)
static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
		int cmd, int version, union semun arg)
{
	struct sem_array *sma;
	struct sem* curr;
@@ -593,14 +644,14 @@ static int semctl_main(int semid, int semnum, int cmd, int version, union semun
	ushort* sem_io = fast_sem_io;
	int nsems;

	sma = sem_lock(semid);
	sma = sem_lock(ns, semid);
	if(sma==NULL)
		return -EINVAL;

	nsems = sma->sem_nsems;

	err=-EIDRM;
	if (sem_checkid(sma,semid))
	if (sem_checkid(ns,sma,semid))
		goto out_unlock;

	err = -EACCES;
@@ -802,7 +853,8 @@ static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void __
	}
}

static int semctl_down(int semid, int semnum, int cmd, int version, union semun arg)
static int semctl_down(struct ipc_namespace *ns, int semid, int semnum,
		int cmd, int version, union semun arg)
{
	struct sem_array *sma;
	int err;
@@ -813,11 +865,11 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
		if(copy_semid_from_user (&setbuf, arg.buf, version))
			return -EFAULT;
	}
	sma = sem_lock(semid);
	sma = sem_lock(ns, semid);
	if(sma==NULL)
		return -EINVAL;

	if (sem_checkid(sma,semid)) {
	if (sem_checkid(ns,sma,semid)) {
		err=-EIDRM;
		goto out_unlock;
	}	
@@ -844,7 +896,7 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun

	switch(cmd){
	case IPC_RMID:
		freeary(sma, semid);
		freeary(ns, sma, semid);
		err = 0;
		break;
	case IPC_SET:
@@ -872,17 +924,19 @@ asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
{
	int err = -EINVAL;
	int version;
	struct ipc_namespace *ns;

	if (semid < 0)
		return -EINVAL;

	version = ipc_parse_version(&cmd);
	ns = current->nsproxy->ipc_ns;

	switch(cmd) {
	case IPC_INFO:
	case SEM_INFO:
	case SEM_STAT:
		err = semctl_nolock(semid,semnum,cmd,version,arg);
		err = semctl_nolock(ns,semid,semnum,cmd,version,arg);
		return err;
	case GETALL:
	case GETVAL:
@@ -892,13 +946,13 @@ asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
	case IPC_STAT:
	case SETVAL:
	case SETALL:
		err = semctl_main(semid,semnum,cmd,version,arg);
		err = semctl_main(ns,semid,semnum,cmd,version,arg);
		return err;
	case IPC_RMID:
	case IPC_SET:
		mutex_lock(&sem_ids.mutex);
		err = semctl_down(semid,semnum,cmd,version,arg);
		mutex_unlock(&sem_ids.mutex);
		mutex_lock(&sem_ids(ns).mutex);
		err = semctl_down(ns,semid,semnum,cmd,version,arg);
		mutex_unlock(&sem_ids(ns).mutex);
		return err;
	default:
		return -EINVAL;
@@ -986,7 +1040,7 @@ static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
	return un;
}

static struct sem_undo *find_undo(int semid)
static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
{
	struct sem_array *sma;
	struct sem_undo_list *ulp;
@@ -1005,12 +1059,12 @@ static struct sem_undo *find_undo(int semid)
		goto out;

	/* no undo structure around - allocate one. */
	sma = sem_lock(semid);
	sma = sem_lock(ns, semid);
	un = ERR_PTR(-EINVAL);
	if(sma==NULL)
		goto out;
	un = ERR_PTR(-EIDRM);
	if (sem_checkid(sma,semid)) {
	if (sem_checkid(ns,sma,semid)) {
		sem_unlock(sma);
		goto out;
	}
@@ -1070,10 +1124,13 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops,
	int undos = 0, alter = 0, max;
	struct sem_queue queue;
	unsigned long jiffies_left = 0;
	struct ipc_namespace *ns;

	ns = current->nsproxy->ipc_ns;

	if (nsops < 1 || semid < 0)
		return -EINVAL;
	if (nsops > sc_semopm)
	if (nsops > ns->sc_semopm)
		return -E2BIG;
	if(nsops > SEMOPM_FAST) {
		sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
@@ -1109,7 +1166,7 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops,

retry_undos:
	if (undos) {
		un = find_undo(semid);
		un = find_undo(ns, semid);
		if (IS_ERR(un)) {
			error = PTR_ERR(un);
			goto out_free;
@@ -1117,12 +1174,12 @@ retry_undos:
	} else
		un = NULL;

	sma = sem_lock(semid);
	sma = sem_lock(ns, semid);
	error=-EINVAL;
	if(sma==NULL)
		goto out_free;
	error = -EIDRM;
	if (sem_checkid(sma,semid))
	if (sem_checkid(ns,sma,semid))
		goto out_unlock_free;
	/*
	 * semid identifies are not unique - find_undo may have
@@ -1190,7 +1247,7 @@ retry_undos:
		goto out_free;
	}

	sma = sem_lock(semid);
	sma = sem_lock(ns, semid);
	if(sma==NULL) {
		BUG_ON(queue.prev != NULL);
		error = -EIDRM;
@@ -1267,6 +1324,7 @@ void exit_sem(struct task_struct *tsk)
{
	struct sem_undo_list *undo_list;
	struct sem_undo *u, **up;
	struct ipc_namespace *ns;

	undo_list = tsk->sysvsem.undo_list;
	if (!undo_list)
@@ -1275,6 +1333,7 @@ void exit_sem(struct task_struct *tsk)
	if (!atomic_dec_and_test(&undo_list->refcnt))
		return;

	ns = tsk->nsproxy->ipc_ns;
	/* There's no need to hold the semundo list lock, as current
         * is the last task exiting for this undo list.
	 */
@@ -1288,14 +1347,14 @@ void exit_sem(struct task_struct *tsk)

		if(semid == -1)
			continue;
		sma = sem_lock(semid);
		sma = sem_lock(ns, semid);
		if (sma == NULL)
			continue;

		if (u->semid == -1)
			goto next_entry;

		BUG_ON(sem_checkid(sma,u->semid));
		BUG_ON(sem_checkid(ns,sma,u->semid));

		/* remove u from the sma->undo list */
		for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {