Commit 45a4a64a authored by Al Viro's avatar Al Viro

semctl(): separate all layout-dependent copyin/copyout

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 46939168
...@@ -1177,53 +1177,14 @@ static time_t get_semotime(struct sem_array *sma) ...@@ -1177,53 +1177,14 @@ static time_t get_semotime(struct sem_array *sma)
return res; return res;
} }
static int semctl_nolock(struct ipc_namespace *ns, int semid, static int semctl_stat(struct ipc_namespace *ns, int semid,
int cmd, int version, void __user *p) int cmd, struct semid64_ds *semid64)
{ {
int err;
struct sem_array *sma; struct sem_array *sma;
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(&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;
}
case IPC_STAT:
case SEM_STAT:
{
struct semid64_ds tbuf;
int id = 0; int id = 0;
int err;
memset(&tbuf, 0, sizeof(tbuf)); memset(semid64, 0, sizeof(*semid64));
rcu_read_lock(); rcu_read_lock();
if (cmd == SEM_STAT) { if (cmd == SEM_STAT) {
...@@ -1249,40 +1210,62 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, ...@@ -1249,40 +1210,62 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
if (err) if (err)
goto out_unlock; goto out_unlock;
kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm); kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm);
tbuf.sem_otime = get_semotime(sma); semid64->sem_otime = get_semotime(sma);
tbuf.sem_ctime = sma->sem_ctime; semid64->sem_ctime = sma->sem_ctime;
tbuf.sem_nsems = sma->sem_nsems; semid64->sem_nsems = sma->sem_nsems;
rcu_read_unlock(); rcu_read_unlock();
if (copy_semid_to_user(p, &tbuf, version))
return -EFAULT;
return id; return id;
}
default:
return -EINVAL;
}
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
return err; 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, static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
unsigned long arg) int val)
{ {
struct sem_undo *un; struct sem_undo *un;
struct sem_array *sma; struct sem_array *sma;
struct sem *curr; struct sem *curr;
int err, val; int err;
DEFINE_WAKE_Q(wake_q); 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) if (val > SEMVMX || val < 0)
return -ERANGE; return -ERANGE;
...@@ -1531,23 +1514,17 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) ...@@ -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. * NOTE: no locks must be held, the rwsem is taken inside this function.
*/ */
static int semctl_down(struct ipc_namespace *ns, int semid, 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; struct sem_array *sma;
int err; int err;
struct semid64_ds semid64;
struct kern_ipc_perm *ipcp; 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); down_write(&sem_ids(ns).rwsem);
rcu_read_lock(); rcu_read_lock();
ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd, ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,
&semid64.sem_perm, 0); &semid64->sem_perm, 0);
if (IS_ERR(ipcp)) { if (IS_ERR(ipcp)) {
err = PTR_ERR(ipcp); err = PTR_ERR(ipcp);
goto out_unlock1; goto out_unlock1;
...@@ -1567,7 +1544,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, ...@@ -1567,7 +1544,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
goto out_up; goto out_up;
case IPC_SET: case IPC_SET:
sem_lock(sma, NULL, -1); sem_lock(sma, NULL, -1);
err = ipc_update_perm(&semid64.sem_perm, ipcp); err = ipc_update_perm(&semid64->sem_perm, ipcp);
if (err) if (err)
goto out_unlock0; goto out_unlock0;
sma->sem_ctime = get_seconds(); sma->sem_ctime = get_seconds();
...@@ -1591,6 +1568,8 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) ...@@ -1591,6 +1568,8 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
int version; int version;
struct ipc_namespace *ns; struct ipc_namespace *ns;
void __user *p = (void __user *)arg; void __user *p = (void __user *)arg;
struct semid64_ds semid64;
int err;
if (semid < 0) if (semid < 0)
return -EINVAL; return -EINVAL;
...@@ -1601,9 +1580,15 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) ...@@ -1601,9 +1580,15 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
switch (cmd) { switch (cmd) {
case IPC_INFO: case IPC_INFO:
case SEM_INFO: case SEM_INFO:
return semctl_info(ns, semid, cmd, p);
case IPC_STAT: case IPC_STAT:
case SEM_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 GETALL:
case GETVAL: case GETVAL:
case GETPID: case GETPID:
...@@ -1611,11 +1596,22 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) ...@@ -1611,11 +1596,22 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
case GETZCNT: case GETZCNT:
case SETALL: case SETALL:
return semctl_main(ns, semid, semnum, cmd, p); return semctl_main(ns, semid, semnum, cmd, p);
case SETVAL: case SETVAL: {
return semctl_setval(ns, semid, semnum, arg); int val;
case IPC_RMID: #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: 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: default:
return -EINVAL; return -EINVAL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment