Subject: [PATCH] Support for private SystemV namespaces From: Eric W. Biederman Date: 1132730416 -0700 --- include/linux/init_task.h | 2 + include/linux/ipc.h | 8 +++ include/linux/sched.h | 4 + ipc/msg.c | 57 ++++++++++++------- ipc/sem.c | 61 ++++++++++++++------- ipc/shm.c | 132 +++++++++++++++++++++++++++++---------------- ipc/util.c | 78 ++++++++++++++++++++++++++- ipc/util.h | 25 +++++++++ kernel/exit.c | 1 kernel/fork.c | 14 ++++- 10 files changed, 294 insertions(+), 88 deletions(-) 0c79670413b18716f0992a6a6a8d5335ce595a5c diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 113ca4f..9ecf3e1 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -72,6 +72,7 @@ } extern struct group_info init_groups; +extern struct ipc_ns init_ipc_ns; /* * INIT_TASK is used to set up the first task table, touch at @@ -112,6 +113,7 @@ extern struct group_info init_groups; .fs = &init_fs, \ .files = &init_files, \ .pspace = &init_pspace, \ + .ipc = &init_ipc_ns, \ .signal = &init_signals, \ .sighand = &init_sighand, \ .pending = { \ diff --git a/include/linux/ipc.h b/include/linux/ipc.h index b291189..0bf39fb 100644 --- a/include/linux/ipc.h +++ b/include/linux/ipc.h @@ -68,6 +68,14 @@ struct kern_ipc_perm void *security; }; +#ifdef CONFIG_SYSVIPC +extern int copy_sysvipc(int flags, struct task_struct *p); +extern void exit_sysvipc(struct task_struct *tsk); +#else +static inline int copy_sysvipc(int flags, struct task_struct *p) { return 0; } +static inline void exit_sysvipc(struct task_struct *tsk) { } +#endif + #endif /* __KERNEL__ */ #endif /* _LINUX_IPC_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 4947116..4244df9 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -61,6 +61,7 @@ struct exec_domain; #define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */ #define CLONE_STOPPED 0x02000000 /* Start in stopped state */ #define CLONE_NPSPACE 0x04000000 /* New process space */ +#define CLONE_NSYSVIPC 0x08000000 /* New sysvipc space */ /* * List of flags we want to share for kernel threads, @@ -230,6 +231,7 @@ asmlinkage void schedule(void); struct namespace; struct pspace; +struct ipc_ns; /* Maximum number of active map areas.. This is a random (large) number */ #define DEFAULT_MAX_MAP_COUNT 65536 @@ -753,6 +755,8 @@ struct task_struct { struct namespace *namespace; /* pid namespace */ struct pspace *pspace; +/* ipc namespace */ + struct ipc_ns *ipc; /* signal handlers */ struct signal_struct *signal; struct sighand_struct *sighand; diff --git a/ipc/msg.c b/ipc/msg.c index aa22703..b9f1610 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -63,31 +63,39 @@ struct msg_sender { static atomic_t msg_bytes = ATOMIC_INIT(0); static atomic_t msg_hdrs = ATOMIC_INIT(0); -static struct ipc_ids msg_ids; - -#define msg_lock(id) ((struct msg_queue*)ipc_lock(&msg_ids,id)) +#define msg_lock(id) ((struct msg_queue*)ipc_lock(¤t->ipc->msg_ids,id)) #define msg_unlock(msq) ipc_unlock(&(msq)->q_perm) -#define msg_rmid(id) ((struct msg_queue*)ipc_rmid(&msg_ids,id)) +#define msg_rmid(id) ((struct msg_queue*)ipc_rmid(¤t->ipc->msg_ids,id)) #define msg_checkid(msq, msgid) \ - ipc_checkid(&msg_ids,&msq->q_perm,msgid) + ipc_checkid(¤t->ipc->msg_ids,&msq->q_perm,msgid) #define msg_buildid(id, seq) \ - ipc_buildid(&msg_ids, id, seq) + ipc_buildid(¤t->ipc->msg_ids, id, seq) static void freeque (struct msg_queue *msq, int id); static int newque (key_t key, int msgflg); +static void msg_free(struct kern_ipc_perm *p); #ifdef CONFIG_PROC_FS static int sysvipc_msg_proc_show(struct seq_file *s, void *it); #endif void __init msg_init (void) { - ipc_init_ids(&msg_ids,msg_ctlmni); ipc_init_proc_interface("sysvipc/msg", " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n", - &msg_ids, + &init_ipc_ns.msg_ids, sysvipc_msg_proc_show); } +void msg_init_ipc(struct ipc_ns *ipc) +{ + ipc_init_ids(&ipc->msg_ids, msg_ctlmni); +} + +void msg_free_ipc(struct ipc_ns *ipc) +{ + ipc_free_ids(&ipc->msg_ids, msg_free); +} + static int newque (key_t key, int msgflg) { int id; @@ -108,7 +116,7 @@ static int newque (key_t key, int msgflg return retval; } - id = ipc_addid(&msg_ids, &msq->q_perm, msg_ctlmni); + id = ipc_addid(¤t->ipc->msg_ids, &msq->q_perm, msg_ctlmni); if(id == -1) { security_msg_queue_free(msq); ipc_rcu_putref(msq); @@ -174,13 +182,14 @@ static void expunge_all(struct msg_queue msr->r_msg = ERR_PTR(res); } } + /* * freeque() wakes up waiters on the sender and receiver waiting queue, * removes the message queue from message queue ID * array, and cleans up all the messages associated with this queue. * - * msg_ids.sem and the spinlock for this message queue is hold - * before freeque() is called. msg_ids.sem remains locked on exit. + * current->ipc->msg_ids.sem and the spinlock for this message queue is hold + * before freeque() is called. current->ipc->msg_ids.sem remains locked on exit. */ static void freeque (struct msg_queue *msq, int id) { @@ -203,15 +212,21 @@ static void freeque (struct msg_queue *m ipc_rcu_putref(msq); } +static void msg_free (struct kern_ipc_perm *p) +{ + struct msg_queue *msg = container_of(p, struct msg_queue, q_perm); + freeque(msg, msg->q_id); +} + asmlinkage long sys_msgget (key_t key, int msgflg) { int id, ret = -EPERM; struct msg_queue *msq; - down(&msg_ids.sem); + down(¤t->ipc->msg_ids.sem); if (key == IPC_PRIVATE) ret = newque(key, msgflg); - else if ((id = ipc_findkey(&msg_ids, key)) == -1) { /* key not used */ + else if ((id = ipc_findkey(¤t->ipc->msg_ids, key)) == -1) { /* key not used */ if (!(msgflg & IPC_CREAT)) ret = -ENOENT; else @@ -232,7 +247,7 @@ asmlinkage long sys_msgget (key_t key, i } msg_unlock(msq); } - up(&msg_ids.sem); + up(¤t->ipc->msg_ids.sem); return ret; } @@ -362,9 +377,9 @@ asmlinkage long sys_msgctl (int msqid, i msginfo.msgmnb = msg_ctlmnb; msginfo.msgssz = MSGSSZ; msginfo.msgseg = MSGSEG; - down(&msg_ids.sem); + down(¤t->ipc->msg_ids.sem); if (cmd == MSG_INFO) { - msginfo.msgpool = msg_ids.in_use; + msginfo.msgpool = current->ipc->msg_ids.in_use; msginfo.msgmap = atomic_read(&msg_hdrs); msginfo.msgtql = atomic_read(&msg_bytes); } else { @@ -372,8 +387,8 @@ asmlinkage long sys_msgctl (int msqid, i msginfo.msgpool = MSGPOOL; msginfo.msgtql = MSGTQL; } - max_id = msg_ids.max_id; - up(&msg_ids.sem); + max_id = current->ipc->msg_ids.max_id; + up(¤t->ipc->msg_ids.sem); if (copy_to_user (buf, &msginfo, sizeof(struct msginfo))) return -EFAULT; return (max_id < 0) ? 0: max_id; @@ -385,7 +400,7 @@ asmlinkage long sys_msgctl (int msqid, i int success_return; if (!buf) return -EFAULT; - if(cmd == MSG_STAT && msqid >= msg_ids.entries->size) + if(cmd == MSG_STAT && msqid >= current->ipc->msg_ids.entries->size) return -EINVAL; memset(&tbuf,0,sizeof(tbuf)); @@ -438,7 +453,7 @@ asmlinkage long sys_msgctl (int msqid, i return -EINVAL; } - down(&msg_ids.sem); + down(¤t->ipc->msg_ids.sem); msq = msg_lock(msqid); err=-EINVAL; if (msq == NULL) @@ -489,7 +504,7 @@ asmlinkage long sys_msgctl (int msqid, i } err = 0; out_up: - up(&msg_ids.sem); + up(¤t->ipc->msg_ids.sem); return err; out_unlock_up: msg_unlock(msq); diff --git a/ipc/sem.c b/ipc/sem.c index 68d12e1..77a01e7 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -79,17 +79,17 @@ #include "util.h" -#define sem_lock(id) ((struct sem_array*)ipc_lock(&sem_ids,id)) +#define sem_lock(id) ((struct sem_array*)ipc_lock(¤t->ipc->sem_ids,id)) #define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm) -#define sem_rmid(id) ((struct sem_array*)ipc_rmid(&sem_ids,id)) +#define sem_rmid(id) ((struct sem_array*)ipc_rmid(¤t->ipc->sem_ids,id)) #define sem_checkid(sma, semid) \ - ipc_checkid(&sem_ids,&sma->sem_perm,semid) + ipc_checkid(¤t->ipc->sem_ids,&sma->sem_perm,semid) #define sem_buildid(id, seq) \ - ipc_buildid(&sem_ids, id, seq) -static struct ipc_ids sem_ids; + ipc_buildid(¤t->ipc->sem_ids, id, seq) static int newary (key_t, int, int); static void freeary (struct sem_array *sma, int id); +static void sem_free (struct kern_ipc_perm *p); #ifdef CONFIG_PROC_FS static int sysvipc_sem_proc_show(struct seq_file *s, void *it); #endif @@ -116,14 +116,22 @@ static int used_sems; void __init sem_init (void) { - used_sems = 0; - ipc_init_ids(&sem_ids,sc_semmni); ipc_init_proc_interface("sysvipc/sem", " key semid perms nsems uid gid cuid cgid otime ctime\n", - &sem_ids, + &init_ipc_ns.sem_ids, sysvipc_sem_proc_show); } +void sem_init_ipc(struct ipc_ns *ipc) +{ + ipc_init_ids(&ipc->sem_ids, sc_semmni); +} + +void sem_free_ipc(struct ipc_ns *ipc) +{ + ipc_free_ids(&ipc->sem_ids, sem_free); +} + /* * Lockless wakeup algorithm: * Without the check/retry algorithm a lockless wakeup is possible: @@ -187,7 +195,7 @@ static int newary (key_t key, int nsems, return retval; } - id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni); + id = ipc_addid(¤t->ipc->sem_ids, &sma->sem_perm, sc_semmni); if(id == -1) { security_sem_free(sma); ipc_rcu_putref(sma); @@ -214,11 +222,11 @@ asmlinkage long sys_semget (key_t key, i if (nsems < 0 || nsems > sc_semmsl) return -EINVAL; - down(&sem_ids.sem); + down(¤t->ipc->sem_ids.sem); if (key == IPC_PRIVATE) { err = newary(key, nsems, semflg); - } else if ((id = ipc_findkey(&sem_ids, key)) == -1) { /* key not used */ + } else if ((id = ipc_findkey(¤t->ipc->sem_ids, key)) == -1) { /* key not used */ if (!(semflg & IPC_CREAT)) err = -ENOENT; else @@ -242,7 +250,7 @@ asmlinkage long sys_semget (key_t key, i sem_unlock(sma); } - up(&sem_ids.sem); + up(¤t->ipc->sem_ids.sem); return err; } @@ -476,6 +484,21 @@ static void freeary (struct sem_array *s ipc_rcu_putref(sma); } +/* + * sem_free - free a semaphore set. + * + * @p: struct to free + * + * Given a kern_ipc_perm pointer free the entire sem_array it controls. + * It has to be called with sem and sem_ids.sem locked, + * but returns with shp unlocked and freed. + */ +static void sem_free(struct kern_ipc_perm *p) +{ + struct sem_array *sma = container_of(p, struct sem_array, sem_perm); + freeary(sma, sma->sem_id); +} + static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version) { switch(version) { @@ -523,16 +546,16 @@ static int semctl_nolock(int semid, int seminfo.semmnu = SEMMNU; seminfo.semmap = SEMMAP; seminfo.semume = SEMUME; - down(&sem_ids.sem); + down(¤t->ipc->sem_ids.sem); if (cmd == SEM_INFO) { - seminfo.semusz = sem_ids.in_use; + seminfo.semusz = current->ipc->sem_ids.in_use; seminfo.semaem = used_sems; } else { seminfo.semusz = SEMUSZ; seminfo.semaem = SEMAEM; } - max_id = sem_ids.max_id; - up(&sem_ids.sem); + max_id = current->ipc->sem_ids.max_id; + up(¤t->ipc->sem_ids.sem); if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) return -EFAULT; return (max_id < 0) ? 0: max_id; @@ -542,7 +565,7 @@ static int semctl_nolock(int semid, int struct semid64_ds tbuf; int id; - if(semid >= sem_ids.entries->size) + if(semid >= current->ipc->sem_ids.entries->size) return -EINVAL; memset(&tbuf,0,sizeof(tbuf)); @@ -884,9 +907,9 @@ asmlinkage long sys_semctl (int semid, i return err; case IPC_RMID: case IPC_SET: - down(&sem_ids.sem); + down(¤t->ipc->sem_ids.sem); err = semctl_down(semid,semnum,cmd,version,arg); - up(&sem_ids.sem); + up(¤t->ipc->sem_ids.sem); return err; default: return -EINVAL; diff --git a/ipc/shm.c b/ipc/shm.c index 5afdfbe..1635715 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -40,17 +40,16 @@ static struct file_operations shm_file_operations; static struct vm_operations_struct shm_vm_ops; -static struct ipc_ids shm_ids; - -#define shm_lock(id) ((struct shmid_kernel*)ipc_lock(&shm_ids,id)) +#define shm_lock(ipc, id) ((struct shmid_kernel*)ipc_lock(&ipc->shm_ids,id)) #define shm_unlock(shp) ipc_unlock(&(shp)->shm_perm) -#define shm_get(id) ((struct shmid_kernel*)ipc_get(&shm_ids,id)) -#define shm_buildid(id, seq) \ - ipc_buildid(&shm_ids, id, seq) +#define shm_get(ipc, id) ((struct shmid_kernel*)ipc_get(&ipc->shm_ids,id)) +#define shm_buildid(ipc, id, seq) \ + ipc_buildid(&ipc->shm_ids, id, seq) static int newseg (key_t key, int shmflg, size_t size); static void shm_open (struct vm_area_struct *shmd); static void shm_close (struct vm_area_struct *shmd); +static void shm_free (struct kern_ipc_perm *p); #ifdef CONFIG_PROC_FS static int sysvipc_shm_proc_show(struct seq_file *s, void *it); #endif @@ -63,36 +62,45 @@ static int shm_tot; /* total number of s void __init shm_init (void) { - ipc_init_ids(&shm_ids, 1); ipc_init_proc_interface("sysvipc/shm", " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime\n", - &shm_ids, + &init_ipc_ns.shm_ids, sysvipc_shm_proc_show); } +void shm_init_ipc(struct ipc_ns *ipc) +{ + ipc_init_ids(&ipc->shm_ids, 1); +} + +void shm_free_ipc(struct ipc_ns *ipc) +{ + ipc_free_ids(&ipc->shm_ids, shm_free); +} + static inline int shm_checkid(struct shmid_kernel *s, int id) { - if (ipc_checkid(&shm_ids,&s->shm_perm,id)) + if (ipc_checkid(¤t->ipc->shm_ids,&s->shm_perm,id)) return -EIDRM; return 0; } static inline struct shmid_kernel *shm_rmid(int id) { - return (struct shmid_kernel *)ipc_rmid(&shm_ids,id); + return (struct shmid_kernel *)ipc_rmid(¤t->ipc->shm_ids,id); } static inline int shm_addid(struct shmid_kernel *shp) { - return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni); + return ipc_addid(¤t->ipc->shm_ids, &shp->shm_perm, shm_ctlmni); } -static inline void shm_inc (int id) { +static inline void shm_inc (struct ipc_ns *ipc, int id) { struct shmid_kernel *shp; - if(!(shp = shm_lock(id))) + if(!(shp = shm_lock(ipc, id))) BUG(); shp->shm_atim = get_seconds(); shp->shm_lprid = current->tgid; @@ -103,7 +111,9 @@ static inline void shm_inc (int id) { /* This is called by fork, once for every shm attach. */ static void shm_open (struct vm_area_struct *shmd) { - shm_inc (shmd->vm_file->f_dentry->d_inode->i_ino); + struct file *file = shmd->vm_file; + struct ipc_ns *ipc = file->private_data; + shm_inc (ipc, file->f_dentry->d_inode->i_ino); } /* @@ -130,6 +140,22 @@ static void shm_destroy (struct shmid_ke } /* + * shm_free - free the struct shmid_kernel + * + * @p: struct to free + * + * Given a kern_ipc_perm pointer free the entire shmid_kernel structure + * It has to be called with shp and shm_ids.sem locked, + * but returns with shp unlocked and freed. + */ +static void shm_free(struct kern_ipc_perm *p) +{ + struct shmid_kernel *shp = container_of(p, struct shmid_kernel, shm_perm); + BUG_ON(shp->shm_nattch); + shm_destroy(shp); +} + +/* * remove the attach descriptor shmd. * free memory for segment if it is marked destroyed. * The descriptor has already been removed from the current->mm->mmap list @@ -139,11 +165,12 @@ static void shm_close (struct vm_area_st { struct file * file = shmd->vm_file; int id = file->f_dentry->d_inode->i_ino; + struct ipc_ns *ipc = file->private_data; struct shmid_kernel *shp; - down (&shm_ids.sem); + down (&ipc->shm_ids.sem); /* remove from the list of attaches of the shm segment */ - if(!(shp = shm_lock(id))) + if(!(shp = shm_lock(ipc, id))) BUG(); shp->shm_lprid = current->tgid; shp->shm_dtim = get_seconds(); @@ -153,19 +180,29 @@ static void shm_close (struct vm_area_st shm_destroy (shp); else shm_unlock(shp); - up (&shm_ids.sem); + up (&ipc->shm_ids.sem); } static int shm_mmap(struct file * file, struct vm_area_struct * vma) { + struct ipc_ns *ipc = file->private_data; file_accessed(file); vma->vm_ops = &shm_vm_ops; - shm_inc(file->f_dentry->d_inode->i_ino); + shm_inc(ipc, file->f_dentry->d_inode->i_ino); + return 0; +} + +static int shm_release(struct inode *inode, struct file *file) +{ + struct ipc_ns *ipc = file->private_data; + file->private_data = NULL; + put_sysvipc(ipc); return 0; } static struct file_operations shm_file_operations = { - .mmap = shm_mmap + .mmap = shm_mmap, + .release = shm_release, }; static struct vm_operations_struct shm_vm_ops = { @@ -180,6 +217,7 @@ static struct vm_operations_struct shm_v static int newseg (key_t key, int shmflg, size_t size) { + struct ipc_ns *ipc = current->ipc; int error; struct shmid_kernel *shp; int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT; @@ -231,9 +269,11 @@ static int newseg (key_t key, int shmflg shp->shm_ctim = get_seconds(); shp->shm_segsz = size; shp->shm_nattch = 0; - shp->id = shm_buildid(id,shp->shm_perm.seq); + shp->id = shm_buildid(ipc, id,shp->shm_perm.seq); shp->shm_file = file; file->f_dentry->d_inode->i_ino = shp->id; + get_sysvipc(ipc); + file->private_data = ipc; if (shmflg & SHM_HUGETLB) set_file_hugepages(file); else @@ -255,10 +295,10 @@ asmlinkage long sys_shmget (key_t key, s struct shmid_kernel *shp; int err, id = 0; - down(&shm_ids.sem); + down(¤t->ipc->shm_ids.sem); if (key == IPC_PRIVATE) { err = newseg(key, shmflg, size); - } else if ((id = ipc_findkey(&shm_ids, key)) == -1) { + } else if ((id = ipc_findkey(¤t->ipc->shm_ids, key)) == -1) { if (!(shmflg & IPC_CREAT)) err = -ENOENT; else @@ -266,7 +306,7 @@ asmlinkage long sys_shmget (key_t key, s } else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) { err = -EEXIST; } else { - shp = shm_lock(id); + shp = shm_lock(current->ipc, id); if(shp==NULL) BUG(); if (shp->shm_segsz < size) @@ -274,14 +314,14 @@ asmlinkage long sys_shmget (key_t key, s else if (ipcperms(&shp->shm_perm, shmflg)) err = -EACCES; else { - int shmid = shm_buildid(id, shp->shm_perm.seq); + int shmid = shm_buildid(current->ipc, id, shp->shm_perm.seq); err = security_shm_associate(shp, shmflg); if (!err) err = shmid; } shm_unlock(shp); } - up(&shm_ids.sem); + up(¤t->ipc->shm_ids.sem); return err; } @@ -384,11 +424,11 @@ static void shm_get_stat(unsigned long * *rss = 0; *swp = 0; - for (i = 0; i <= shm_ids.max_id; i++) { + for (i = 0; i <= current->ipc->shm_ids.max_id; i++) { struct shmid_kernel *shp; struct inode *inode; - shp = shm_get(i); + shp = shm_get(current->ipc, i); if(!shp) continue; @@ -438,7 +478,7 @@ asmlinkage long sys_shmctl (int shmid, i if(copy_shminfo_to_user (buf, &shminfo, version)) return -EFAULT; /* reading a integer is always atomic */ - err= shm_ids.max_id; + err= current->ipc->shm_ids.max_id; if(err<0) err = 0; goto out; @@ -452,14 +492,14 @@ asmlinkage long sys_shmctl (int shmid, i return err; memset(&shm_info,0,sizeof(shm_info)); - down(&shm_ids.sem); - shm_info.used_ids = shm_ids.in_use; + down(¤t->ipc->shm_ids.sem); + shm_info.used_ids = current->ipc->shm_ids.in_use; shm_get_stat (&shm_info.shm_rss, &shm_info.shm_swp); shm_info.shm_tot = shm_tot; shm_info.swap_attempts = 0; shm_info.swap_successes = 0; - err = shm_ids.max_id; - up(&shm_ids.sem); + err = current->ipc->shm_ids.max_id; + up(¤t->ipc->shm_ids.sem); if(copy_to_user (buf, &shm_info, sizeof(shm_info))) { err = -EFAULT; goto out; @@ -474,15 +514,15 @@ asmlinkage long sys_shmctl (int shmid, i struct shmid64_ds tbuf; int result; memset(&tbuf, 0, sizeof(tbuf)); - shp = shm_lock(shmid); + shp = shm_lock(current->ipc, shmid); if(shp==NULL) { err = -EINVAL; goto out; } else if(cmd==SHM_STAT) { err = -EINVAL; - if (shmid > shm_ids.max_id) + if (shmid > current->ipc->shm_ids.max_id) goto out_unlock; - result = shm_buildid(shmid, shp->shm_perm.seq); + result = shm_buildid(current->ipc, shmid, shp->shm_perm.seq); } else { err = shm_checkid(shp,shmid); if(err) @@ -516,7 +556,7 @@ asmlinkage long sys_shmctl (int shmid, i case SHM_LOCK: case SHM_UNLOCK: { - shp = shm_lock(shmid); + shp = shm_lock(current->ipc, shmid); if(shp==NULL) { err = -EINVAL; goto out; @@ -568,8 +608,8 @@ asmlinkage long sys_shmctl (int shmid, i * Instead we set a destroyed flag, and then blow * the name away when the usage hits zero. */ - down(&shm_ids.sem); - shp = shm_lock(shmid); + down(¤t->ipc->shm_ids.sem); + shp = shm_lock(current->ipc, shmid); err = -EINVAL; if (shp == NULL) goto out_up; @@ -595,7 +635,7 @@ asmlinkage long sys_shmctl (int shmid, i shm_unlock(shp); } else shm_destroy (shp); - up(&shm_ids.sem); + up(¤t->ipc->shm_ids.sem); goto out; } @@ -607,8 +647,8 @@ asmlinkage long sys_shmctl (int shmid, i } if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode))) return err; - down(&shm_ids.sem); - shp = shm_lock(shmid); + down(¤t->ipc->shm_ids.sem); + shp = shm_lock(current->ipc, shmid); err=-EINVAL; if(shp==NULL) goto out_up; @@ -643,7 +683,7 @@ asmlinkage long sys_shmctl (int shmid, i out_unlock_up: shm_unlock(shp); out_up: - up(&shm_ids.sem); + up(¤t->ipc->shm_ids.sem); goto out; out_unlock: shm_unlock(shp); @@ -710,7 +750,7 @@ long do_shmat(int shmid, char __user *sh * We cannot rely on the fs check since SYSV IPC does have an * additional creator id... */ - shp = shm_lock(shmid); + shp = shm_lock(current->ipc, shmid); if(shp == NULL) { err = -EINVAL; goto out; @@ -756,8 +796,8 @@ long do_shmat(int shmid, char __user *sh invalid: up_write(¤t->mm->mmap_sem); - down (&shm_ids.sem); - if(!(shp = shm_lock(shmid))) + down (¤t->ipc->shm_ids.sem); + if(!(shp = shm_lock(current->ipc, shmid))) BUG(); shp->shm_nattch--; if(shp->shm_nattch == 0 && @@ -765,7 +805,7 @@ invalid: shm_destroy (shp); else shm_unlock(shp); - up (&shm_ids.sem); + up (¤t->ipc->shm_ids.sem); *raddr = (unsigned long) user_addr; err = 0; diff --git a/ipc/util.c b/ipc/util.c index 10e836d..fd6761f 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -31,6 +31,10 @@ #include "util.h" +struct ipc_ns init_ipc_ns = { + .count = ATOMIC_INIT(1), +}; + struct ipc_proc_iface { const char *path; const char *header; @@ -47,6 +51,9 @@ struct ipc_proc_iface { static int __init ipc_init(void) { + msg_init_ipc(&init_ipc_ns); + sem_init_ipc(&init_ipc_ns); + shm_init_ipc(&init_ipc_ns); sem_init(); msg_init(); shm_init(); @@ -64,7 +71,7 @@ __initcall(ipc_init); * 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); @@ -95,6 +102,75 @@ void __init ipc_init_ids(struct ipc_ids* ids->entries->p[i] = NULL; } +/** + * ipc_free_ids - release IPC identifiers + * @ids: Identifier set + * + * Free the ipc identifier range when the process group using + * the range goes away. + */ + +void ipc_free_ids(struct ipc_ids *ids, void (*ipc_free)(struct kern_ipc_perm *p)) +{ + struct ipc_id_ary *old; + int id; + int size; + down(&ids->sem); + size = ids->entries->size; + for (id = 0; id < size; id++) { + struct kern_ipc_perm *p; + rcu_read_lock(); + p = ids->entries->p[id]; + spin_lock(&p->lock); + ipc_free(p); + } + BUG_ON(ids->in_use); + old = ids->entries; + rcu_assign_pointer(ids->entries, NULL); + ipc_rcu_putref(old); + up(&ids->sem); +} + +int copy_sysvipc(int flags, struct task_struct *p) +{ + struct ipc_ns *ipc; + int result; + result = 0; + if (likely(!(flags & CLONE_NSYSVIPC))) { + get_sysvipc(p->ipc); + goto out; + } + result = -ENOMEM; + ipc = kzalloc(sizeof(*ipc), GFP_KERNEL); + if (!ipc) + goto out; + result = 0; + atomic_set(&ipc->count, 1); + msg_init_ipc(ipc); + sem_init_ipc(ipc); + shm_init_ipc(ipc); + p->ipc = ipc; + out: + return result; +} + +void exit_sysvipc(struct task_struct *tsk) +{ + struct ipc_ns *ipc = current->ipc; + tsk->ipc = NULL; + put_sysvipc(ipc); +} + +void put_sysvipc(struct ipc_ns *ipc) +{ + if (atomic_dec_and_test(&ipc->count)) { + msg_free_ipc(ipc); + sem_free_ipc(ipc); + shm_free_ipc(ipc); + kfree(ipc); + } +} + #ifdef CONFIG_PROC_FS static struct file_operations sysvipc_proc_fops; /** diff --git a/ipc/util.h b/ipc/util.h index fc9a28b..68e6495 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -30,8 +30,33 @@ struct ipc_ids { struct ipc_id_ary* entries; }; +struct ipc_ns { + atomic_t count; + struct ipc_ids msg_ids; + struct ipc_ids sem_ids; + struct ipc_ids shm_ids; +}; + +extern struct ipc_ns init_ipc_ns; + +void msg_init_ipc(struct ipc_ns *ipc); +void sem_init_ipc(struct ipc_ns *ipc); +void shm_init_ipc(struct ipc_ns *ipc); + +void msg_free_ipc(struct ipc_ns *ipc); +void sem_free_ipc(struct ipc_ns *ipc); +void shm_free_ipc(struct ipc_ns *ipc); + +static inline void get_sysvipc(struct ipc_ns *ipc) +{ + atomic_inc(&ipc->count); +} +extern void put_sysvipc(struct ipc_ns *ipc); + + struct seq_file; void __init ipc_init_ids(struct ipc_ids* ids, int size); +void ipc_free_ids(struct ipc_ids *ids, void (*ipc_free)(struct kern_ipc_perm *p)); #ifdef CONFIG_PROC_FS void __init ipc_init_proc_interface(const char *path, const char *header, struct ipc_ids *ids, diff --git a/kernel/exit.c b/kernel/exit.c index 09cf567..cd1922b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -876,6 +876,7 @@ fastcall NORET_TYPE void do_exit(long co exit_mm(tsk); exit_sem(tsk); + exit_sysvipc(tsk); __exit_files(tsk); __exit_fs(tsk); exit_namespace(tsk); diff --git a/kernel/fork.c b/kernel/fork.c index c8e0118..341cd4c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -878,6 +878,14 @@ static task_t *copy_process(unsigned lon (clone_flags & (CLONE_THREAD|CLONE_SIGHAND))) return ERR_PTR(-EINVAL); + /* When you don't share your sysvipc namespace you clearly can't share + * the sysv semaphore undo sematics. Because you can't syare sysv + * semaphores. + */ + if ((clone_flags & CLONE_NSYSVIPC) && + (clone_flags & (CLONE_SYSVSEM))) + return ERR_PTR(-EINVAL); + /* * Thread groups must share signals as well, and detached threads * can only be started up within the thread group. @@ -998,8 +1006,10 @@ static task_t *copy_process(unsigned lon if ((retval = audit_alloc(p))) goto bad_fork_cleanup_security; /* copy all the process information */ - if ((retval = copy_semundo(clone_flags, p))) + if ((retval = copy_sysvipc(clone_flags, p))) goto bad_fork_cleanup_audit; + if ((retval = copy_semundo(clone_flags, p))) + goto bad_fork_cleanup_sysvipc; if ((retval = copy_files(clone_flags, p))) goto bad_fork_cleanup_semundo; if ((retval = copy_fs(clone_flags, p))) @@ -1191,6 +1201,8 @@ bad_fork_cleanup_files: exit_files(p); /* blocking */ bad_fork_cleanup_semundo: exit_sem(p); +bad_fork_cleanup_sysvipc: + exit_sysvipc(p); bad_fork_cleanup_audit: audit_free(p); bad_fork_cleanup_security: -- 1.0.GIT