|
@@ -1177,112 +1177,95 @@ static time_t get_semotime(struct sem_array *sma)
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
-static int semctl_nolock(struct ipc_namespace *ns, int semid,
|
|
|
- int cmd, int version, void __user *p)
|
|
|
+static int semctl_stat(struct ipc_namespace *ns, int semid,
|
|
|
+ int cmd, struct semid64_ds *semid64)
|
|
|
{
|
|
|
- int err;
|
|
|
struct sem_array *sma;
|
|
|
+ int id = 0;
|
|
|
+ int err;
|
|
|
|
|
|
- switch (cmd) {
|
|
|
- case IPC_INFO:
|
|
|
- case SEM_INFO:
|
|
|
- {
|
|
|
- struct seminfo seminfo;
|
|
|
- int max_id;
|
|
|
-
|
|
|
- err = security_sem_semctl(NULL, cmd);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
+ memset(semid64, 0, sizeof(*semid64));
|
|
|
|
|
|
- memset(&seminfo, 0, sizeof(seminfo));
|
|
|
- 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;
|
|
|
- down_read(&sem_ids(ns).rwsem);
|
|
|
- if (cmd == SEM_INFO) {
|
|
|
- seminfo.semusz = sem_ids(ns).in_use;
|
|
|
- seminfo.semaem = ns->used_sems;
|
|
|
- } else {
|
|
|
- seminfo.semusz = SEMUSZ;
|
|
|
- seminfo.semaem = SEMAEM;
|
|
|
+ rcu_read_lock();
|
|
|
+ if (cmd == SEM_STAT) {
|
|
|
+ sma = sem_obtain_object(ns, semid);
|
|
|
+ if (IS_ERR(sma)) {
|
|
|
+ err = PTR_ERR(sma);
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+ id = sma->sem_perm.id;
|
|
|
+ } else {
|
|
|
+ sma = sem_obtain_object_check(ns, semid);
|
|
|
+ if (IS_ERR(sma)) {
|
|
|
+ err = PTR_ERR(sma);
|
|
|
+ goto out_unlock;
|
|
|
}
|
|
|
- max_id = ipc_get_maxid(&sem_ids(ns));
|
|
|
- up_read(&sem_ids(ns).rwsem);
|
|
|
- if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
|
|
|
- return -EFAULT;
|
|
|
- return (max_id < 0) ? 0 : max_id;
|
|
|
}
|
|
|
- case IPC_STAT:
|
|
|
- case SEM_STAT:
|
|
|
- {
|
|
|
- struct semid64_ds tbuf;
|
|
|
- int id = 0;
|
|
|
-
|
|
|
- memset(&tbuf, 0, sizeof(tbuf));
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
- if (cmd == SEM_STAT) {
|
|
|
- sma = sem_obtain_object(ns, semid);
|
|
|
- if (IS_ERR(sma)) {
|
|
|
- err = PTR_ERR(sma);
|
|
|
- goto out_unlock;
|
|
|
- }
|
|
|
- id = sma->sem_perm.id;
|
|
|
- } else {
|
|
|
- sma = sem_obtain_object_check(ns, semid);
|
|
|
- if (IS_ERR(sma)) {
|
|
|
- err = PTR_ERR(sma);
|
|
|
- goto out_unlock;
|
|
|
- }
|
|
|
- }
|
|
|
+ err = -EACCES;
|
|
|
+ if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
|
|
|
+ goto out_unlock;
|
|
|
|
|
|
- err = -EACCES;
|
|
|
- if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
|
|
|
- goto out_unlock;
|
|
|
+ err = security_sem_semctl(sma, cmd);
|
|
|
+ if (err)
|
|
|
+ goto out_unlock;
|
|
|
|
|
|
- err = security_sem_semctl(sma, cmd);
|
|
|
- if (err)
|
|
|
- goto out_unlock;
|
|
|
+ kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm);
|
|
|
+ semid64->sem_otime = get_semotime(sma);
|
|
|
+ semid64->sem_ctime = sma->sem_ctime;
|
|
|
+ semid64->sem_nsems = sma->sem_nsems;
|
|
|
+ rcu_read_unlock();
|
|
|
+ return id;
|
|
|
|
|
|
- kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
|
|
|
- tbuf.sem_otime = get_semotime(sma);
|
|
|
- tbuf.sem_ctime = sma->sem_ctime;
|
|
|
- tbuf.sem_nsems = sma->sem_nsems;
|
|
|
- rcu_read_unlock();
|
|
|
- if (copy_semid_to_user(p, &tbuf, version))
|
|
|
- return -EFAULT;
|
|
|
- return id;
|
|
|
- }
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
out_unlock:
|
|
|
rcu_read_unlock();
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int semctl_info(struct ipc_namespace *ns, int semid,
|
|
|
+ int cmd, void __user *p)
|
|
|
+{
|
|
|
+ struct seminfo seminfo;
|
|
|
+ int max_id;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = security_sem_semctl(NULL, cmd);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ memset(&seminfo, 0, sizeof(seminfo));
|
|
|
+ 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;
|
|
|
+ down_read(&sem_ids(ns).rwsem);
|
|
|
+ if (cmd == SEM_INFO) {
|
|
|
+ seminfo.semusz = sem_ids(ns).in_use;
|
|
|
+ seminfo.semaem = ns->used_sems;
|
|
|
+ } else {
|
|
|
+ seminfo.semusz = SEMUSZ;
|
|
|
+ seminfo.semaem = SEMAEM;
|
|
|
+ }
|
|
|
+ max_id = ipc_get_maxid(&sem_ids(ns));
|
|
|
+ up_read(&sem_ids(ns).rwsem);
|
|
|
+ if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
|
|
|
+ return -EFAULT;
|
|
|
+ return (max_id < 0) ? 0 : max_id;
|
|
|
+}
|
|
|
+
|
|
|
static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
|
|
|
- unsigned long arg)
|
|
|
+ int val)
|
|
|
{
|
|
|
struct sem_undo *un;
|
|
|
struct sem_array *sma;
|
|
|
struct sem *curr;
|
|
|
- int err, val;
|
|
|
+ int err;
|
|
|
DEFINE_WAKE_Q(wake_q);
|
|
|
|
|
|
-#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN)
|
|
|
- /* big-endian 64bit */
|
|
|
- val = arg >> 32;
|
|
|
-#else
|
|
|
- /* 32bit or little-endian 64bit */
|
|
|
- val = arg;
|
|
|
-#endif
|
|
|
-
|
|
|
if (val > SEMVMX || val < 0)
|
|
|
return -ERANGE;
|
|
|
|
|
@@ -1531,23 +1514,17 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version)
|
|
|
* NOTE: no locks must be held, the rwsem is taken inside this function.
|
|
|
*/
|
|
|
static int semctl_down(struct ipc_namespace *ns, int semid,
|
|
|
- int cmd, int version, void __user *p)
|
|
|
+ int cmd, struct semid64_ds *semid64)
|
|
|
{
|
|
|
struct sem_array *sma;
|
|
|
int err;
|
|
|
- struct semid64_ds semid64;
|
|
|
struct kern_ipc_perm *ipcp;
|
|
|
|
|
|
- if (cmd == IPC_SET) {
|
|
|
- if (copy_semid_from_user(&semid64, p, version))
|
|
|
- return -EFAULT;
|
|
|
- }
|
|
|
-
|
|
|
down_write(&sem_ids(ns).rwsem);
|
|
|
rcu_read_lock();
|
|
|
|
|
|
ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,
|
|
|
- &semid64.sem_perm, 0);
|
|
|
+ &semid64->sem_perm, 0);
|
|
|
if (IS_ERR(ipcp)) {
|
|
|
err = PTR_ERR(ipcp);
|
|
|
goto out_unlock1;
|
|
@@ -1567,7 +1544,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
|
|
|
goto out_up;
|
|
|
case IPC_SET:
|
|
|
sem_lock(sma, NULL, -1);
|
|
|
- err = ipc_update_perm(&semid64.sem_perm, ipcp);
|
|
|
+ err = ipc_update_perm(&semid64->sem_perm, ipcp);
|
|
|
if (err)
|
|
|
goto out_unlock0;
|
|
|
sma->sem_ctime = get_seconds();
|
|
@@ -1591,6 +1568,8 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
|
|
|
int version;
|
|
|
struct ipc_namespace *ns;
|
|
|
void __user *p = (void __user *)arg;
|
|
|
+ struct semid64_ds semid64;
|
|
|
+ int err;
|
|
|
|
|
|
if (semid < 0)
|
|
|
return -EINVAL;
|
|
@@ -1601,9 +1580,15 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
|
|
|
switch (cmd) {
|
|
|
case IPC_INFO:
|
|
|
case SEM_INFO:
|
|
|
+ return semctl_info(ns, semid, cmd, p);
|
|
|
case IPC_STAT:
|
|
|
case SEM_STAT:
|
|
|
- return semctl_nolock(ns, semid, cmd, version, p);
|
|
|
+ err = semctl_stat(ns, semid, cmd, &semid64);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ if (copy_semid_to_user(p, &semid64, version))
|
|
|
+ err = -EFAULT;
|
|
|
+ return err;
|
|
|
case GETALL:
|
|
|
case GETVAL:
|
|
|
case GETPID:
|
|
@@ -1611,11 +1596,22 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
|
|
|
case GETZCNT:
|
|
|
case SETALL:
|
|
|
return semctl_main(ns, semid, semnum, cmd, p);
|
|
|
- case SETVAL:
|
|
|
- return semctl_setval(ns, semid, semnum, arg);
|
|
|
- case IPC_RMID:
|
|
|
+ case SETVAL: {
|
|
|
+ int val;
|
|
|
+#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN)
|
|
|
+ /* big-endian 64bit */
|
|
|
+ val = arg >> 32;
|
|
|
+#else
|
|
|
+ /* 32bit or little-endian 64bit */
|
|
|
+ val = arg;
|
|
|
+#endif
|
|
|
+ return semctl_setval(ns, semid, semnum, val);
|
|
|
+ }
|
|
|
case IPC_SET:
|
|
|
- return semctl_down(ns, semid, cmd, version, p);
|
|
|
+ if (copy_semid_from_user(&semid64, p, version))
|
|
|
+ return -EFAULT;
|
|
|
+ case IPC_RMID:
|
|
|
+ return semctl_down(ns, semid, cmd, &semid64);
|
|
|
default:
|
|
|
return -EINVAL;
|
|
|
}
|