IPC_SEM_IDS, sysvipc_sem_proc_show);
}
-/*
- * This routine is called in the paths where the rw_mutex is held to protect
- * access to the idr tree.
- */
-static inline struct sem_array *sem_lock_check_down(struct ipc_namespace *ns,
- int id)
-{
- struct kern_ipc_perm *ipcp = ipc_lock_check_down(&sem_ids(ns), id);
-
- if (IS_ERR(ipcp))
- return (struct sem_array *)ipcp;
-
- return container_of(ipcp, struct sem_array, sem_perm);
-}
-
/*
* sem_lock_(check_) routines are called in the paths where the rw_mutex
* is not held.
return err;
}
-struct sem_setbuf {
- uid_t uid;
- gid_t gid;
- mode_t mode;
-};
-
-static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void __user *buf, int version)
+static inline unsigned long
+copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version)
{
switch(version) {
case IPC_64:
- {
- struct semid64_ds tbuf;
-
- if(copy_from_user(&tbuf, buf, sizeof(tbuf)))
+ if (copy_from_user(out, buf, sizeof(*out)))
return -EFAULT;
-
- out->uid = tbuf.sem_perm.uid;
- out->gid = tbuf.sem_perm.gid;
- out->mode = tbuf.sem_perm.mode;
-
return 0;
- }
case IPC_OLD:
{
struct semid_ds tbuf_old;
if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
return -EFAULT;
- out->uid = tbuf_old.sem_perm.uid;
- out->gid = tbuf_old.sem_perm.gid;
- out->mode = tbuf_old.sem_perm.mode;
+ out->sem_perm.uid = tbuf_old.sem_perm.uid;
+ out->sem_perm.gid = tbuf_old.sem_perm.gid;
+ out->sem_perm.mode = tbuf_old.sem_perm.mode;
return 0;
}
}
}
-static int semctl_down(struct ipc_namespace *ns, int semid, int semnum,
- int cmd, int version, union semun arg)
+/*
+ * This function handles some semctl commands which require the rw_mutex
+ * to be held in write mode.
+ * NOTE: no locks must be held, the rw_mutex is taken inside this function.
+ */
+static int semctl_down(struct ipc_namespace *ns, int semid,
+ int cmd, int version, union semun arg)
{
struct sem_array *sma;
int err;
- struct sem_setbuf uninitialized_var(setbuf);
+ struct semid64_ds semid64;
struct kern_ipc_perm *ipcp;
if(cmd == IPC_SET) {
- if(copy_semid_from_user (&setbuf, arg.buf, version))
+ if (copy_semid_from_user(&semid64, arg.buf, version))
return -EFAULT;
}
- sma = sem_lock_check_down(ns, semid);
- if (IS_ERR(sma))
- return PTR_ERR(sma);
- ipcp = &sma->sem_perm;
-
- err = audit_ipc_obj(ipcp);
- if (err)
- goto out_unlock;
+ ipcp = ipcctl_pre_down(&sem_ids(ns), semid, cmd, &semid64.sem_perm, 0);
+ if (IS_ERR(ipcp))
+ return PTR_ERR(ipcp);
- if (cmd == IPC_SET) {
- err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
- if (err)
- goto out_unlock;
- }
- if (current->euid != ipcp->cuid &&
- current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
- err=-EPERM;
- goto out_unlock;
- }
+ sma = container_of(ipcp, struct sem_array, sem_perm);
err = security_sem_semctl(sma, cmd);
if (err)
switch(cmd){
case IPC_RMID:
freeary(ns, ipcp);
- err = 0;
- break;
+ goto out_up;
case IPC_SET:
- ipcp->uid = setbuf.uid;
- ipcp->gid = setbuf.gid;
- ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
- | (setbuf.mode & S_IRWXUGO);
+ ipc_update_perm(&semid64.sem_perm, ipcp);
sma->sem_ctime = get_seconds();
- sem_unlock(sma);
- err = 0;
break;
default:
- sem_unlock(sma);
err = -EINVAL;
- break;
}
- return err;
out_unlock:
sem_unlock(sma);
+out_up:
+ up_write(&sem_ids(ns).rw_mutex);
return err;
}
return err;
case IPC_RMID:
case IPC_SET:
- down_write(&sem_ids(ns).rw_mutex);
- err = semctl_down(ns,semid,semnum,cmd,version,arg);
- up_write(&sem_ids(ns).rw_mutex);
+ err = semctl_down(ns, semid, cmd, version, arg);
return err;
default:
return -EINVAL;
undo_list = tsk->sysvsem.undo_list;
if (!undo_list)
return;
+ tsk->sysvsem.undo_list = NULL;
if (!atomic_dec_and_test(&undo_list->refcnt))
return;