Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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



Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 46939168
Loading
Loading
Loading
Loading
+94 −98
Original line number Diff line number Diff line
@@ -1177,53 +1177,14 @@ 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;

	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 err;

		memset(&tbuf, 0, sizeof(tbuf));
	memset(semid64, 0, sizeof(*semid64));

	rcu_read_lock();
	if (cmd == SEM_STAT) {
@@ -1249,40 +1210,62 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
	if (err)
		goto out_unlock;

		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;
	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();
		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;
	}