forked from Minki/linux
semctl(): separate all layout-dependent copyin/copyout
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
4693916846
commit
45a4a64ab4
198
ipc/sem.c
198
ipc/sem.c
@ -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;
|
||||
memset(semid64, 0, sizeof(*semid64));
|
||||
|
||||
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;
|
||||
|
||||
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))
|
||||
rcu_read_lock();
|
||||
if (cmd == SEM_STAT) {
|
||||
sma = sem_obtain_object(ns, semid);
|
||||
if (IS_ERR(sma)) {
|
||||
err = PTR_ERR(sma);
|
||||
goto out_unlock;
|
||||
|
||||
err = security_sem_semctl(sma, cmd);
|
||||
if (err)
|
||||
}
|
||||
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 = 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user