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

Commit 9ba720c1 authored by Al Viro's avatar Al Viro
Browse files

shmctl: split the work from copyin/copyout



Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 5771a8c0
Loading
Loading
Loading
Loading
+172 −175
Original line number Diff line number Diff line
@@ -813,23 +813,17 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
 * NOTE: no locks must be held, the rwsem is taken inside this function.
 */
static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
		       struct shmid_ds __user *buf, int version)
		       struct shmid64_ds *shmid64)
{
	struct kern_ipc_perm *ipcp;
	struct shmid64_ds shmid64;
	struct shmid_kernel *shp;
	int err;

	if (cmd == IPC_SET) {
		if (copy_shmid_from_user(&shmid64, buf, version))
			return -EFAULT;
	}

	down_write(&shm_ids(ns).rwsem);
	rcu_read_lock();

	ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd,
				      &shmid64.shm_perm, 0);
				      &shmid64->shm_perm, 0);
	if (IS_ERR(ipcp)) {
		err = PTR_ERR(ipcp);
		goto out_unlock1;
@@ -849,7 +843,7 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
		goto out_up;
	case IPC_SET:
		ipc_lock_object(&shp->shm_perm);
		err = ipc_update_perm(&shmid64.shm_perm, ipcp);
		err = ipc_update_perm(&shmid64->shm_perm, ipcp);
		if (err)
			goto out_unlock0;
		shp->shm_ctim = get_seconds();
@@ -868,67 +862,51 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
	return err;
}

static int shmctl_nolock(struct ipc_namespace *ns, int shmid,
			 int cmd, int version, void __user *buf)
{
	int err;
	struct shmid_kernel *shp;

	/* preliminary security checks for *_INFO */
	if (cmd == IPC_INFO || cmd == SHM_INFO) {
		err = security_shm_shmctl(NULL, cmd);
		if (err)
			return err;
	}

	switch (cmd) {
	case IPC_INFO:
static int shmctl_ipc_info(struct ipc_namespace *ns,
			   struct shminfo64 *shminfo)
{
		struct shminfo64 shminfo;

		memset(&shminfo, 0, sizeof(shminfo));
		shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni;
		shminfo.shmmax = ns->shm_ctlmax;
		shminfo.shmall = ns->shm_ctlall;

		shminfo.shmmin = SHMMIN;
		if (copy_shminfo_to_user(buf, &shminfo, version))
			return -EFAULT;

	int err = security_shm_shmctl(NULL, IPC_INFO);
	if (!err) {
		memset(shminfo, 0, sizeof(*shminfo));
		shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni;
		shminfo->shmmax = ns->shm_ctlmax;
		shminfo->shmall = ns->shm_ctlall;
		shminfo->shmmin = SHMMIN;
		down_read(&shm_ids(ns).rwsem);
		err = ipc_get_maxid(&shm_ids(ns));
		up_read(&shm_ids(ns).rwsem);

		if (err < 0)
			err = 0;
		goto out;
	}
	case SHM_INFO:
	{
		struct shm_info shm_info;
	return err;
}

		memset(&shm_info, 0, sizeof(shm_info));
static int shmctl_shm_info(struct ipc_namespace *ns,
			   struct shm_info *shm_info)
{
	int err = security_shm_shmctl(NULL, SHM_INFO);
	if (!err) {
		memset(shm_info, 0, sizeof(*shm_info));
		down_read(&shm_ids(ns).rwsem);
		shm_info.used_ids = shm_ids(ns).in_use;
		shm_get_stat(ns, &shm_info.shm_rss, &shm_info.shm_swp);
		shm_info.shm_tot = ns->shm_tot;
		shm_info.swap_attempts = 0;
		shm_info.swap_successes = 0;
		shm_info->used_ids = shm_ids(ns).in_use;
		shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp);
		shm_info->shm_tot = ns->shm_tot;
		shm_info->swap_attempts = 0;
		shm_info->swap_successes = 0;
		err = ipc_get_maxid(&shm_ids(ns));
		up_read(&shm_ids(ns).rwsem);
		if (copy_to_user(buf, &shm_info, sizeof(shm_info))) {
			err = -EFAULT;
			goto out;
		if (err < 0)
			err = 0;
	}

		err = err < 0 ? 0 : err;
		goto out;
	return err;
}
	case SHM_STAT:
	case IPC_STAT:

static int shmctl_stat(struct ipc_namespace *ns, int shmid,
			int cmd, struct shmid64_ds *tbuf)
{
		struct shmid64_ds tbuf;
	struct shmid_kernel *shp;
	int result;
	int err;

	rcu_read_lock();
	if (cmd == SHM_STAT) {
@@ -955,58 +933,28 @@ static int shmctl_nolock(struct ipc_namespace *ns, int shmid,
	if (err)
		goto out_unlock;

		memset(&tbuf, 0, sizeof(tbuf));
		kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
		tbuf.shm_segsz	= shp->shm_segsz;
		tbuf.shm_atime	= shp->shm_atim;
		tbuf.shm_dtime	= shp->shm_dtim;
		tbuf.shm_ctime	= shp->shm_ctim;
		tbuf.shm_cpid	= shp->shm_cprid;
		tbuf.shm_lpid	= shp->shm_lprid;
		tbuf.shm_nattch	= shp->shm_nattch;
	memset(tbuf, 0, sizeof(*tbuf));
	kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm);
	tbuf->shm_segsz	= shp->shm_segsz;
	tbuf->shm_atime	= shp->shm_atim;
	tbuf->shm_dtime	= shp->shm_dtim;
	tbuf->shm_ctime	= shp->shm_ctim;
	tbuf->shm_cpid	= shp->shm_cprid;
	tbuf->shm_lpid	= shp->shm_lprid;
	tbuf->shm_nattch = shp->shm_nattch;
	rcu_read_unlock();

		if (copy_shmid_to_user(buf, &tbuf, version))
			err = -EFAULT;
		else
			err = result;
		goto out;
	}
	default:
		return -EINVAL;
	}
	return result;

out_unlock:
	rcu_read_unlock();
out:
	return err;
}

SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd)
{
	struct shmid_kernel *shp;
	int err, version;
	struct ipc_namespace *ns;

	if (cmd < 0 || shmid < 0)
		return -EINVAL;

	version = ipc_parse_version(&cmd);
	ns = current->nsproxy->ipc_ns;

	switch (cmd) {
	case IPC_INFO:
	case SHM_INFO:
	case SHM_STAT:
	case IPC_STAT:
		return shmctl_nolock(ns, shmid, cmd, version, buf);
	case IPC_RMID:
	case IPC_SET:
		return shmctl_down(ns, shmid, cmd, buf, version);
	case SHM_LOCK:
	case SHM_UNLOCK:
	{
	struct file *shm_file;
	int err;

	rcu_read_lock();
	shp = shm_obtain_object_check(ns, shmid);
@@ -1070,10 +1018,6 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)

	fput(shm_file);
	return err;
	}
	default:
		return -EINVAL;
	}

out_unlock0:
	ipc_unlock_object(&shp->shm_perm);
@@ -1082,6 +1026,59 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
	return err;
}

SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
{
	int err, version;
	struct ipc_namespace *ns;
	struct shmid64_ds tbuf;

	if (cmd < 0 || shmid < 0)
		return -EINVAL;

	version = ipc_parse_version(&cmd);
	ns = current->nsproxy->ipc_ns;

	switch (cmd) {
	case IPC_INFO: {
		struct shminfo64 shminfo;
		err = shmctl_ipc_info(ns, &shminfo);
		if (err < 0)
			return err;
		if (copy_shminfo_to_user(buf, &shminfo, version))
			err = -EFAULT;
		return err;
	}
	case SHM_INFO: {
		struct shm_info shm_info;
		err = shmctl_shm_info(ns, &shm_info);
		if (err < 0)
			return err;
		if (copy_to_user(buf, &shm_info, sizeof(shm_info)))
			err = -EFAULT;
		return err;
	}
	case SHM_STAT:
	case IPC_STAT: {
		err = shmctl_stat(ns, shmid, cmd, &tbuf);
		if (err < 0)
			return err;
		if (copy_shmid_to_user(buf, &tbuf, version))
			err = -EFAULT;
		return err;
	}
	case IPC_SET:
		if (copy_shmid_from_user(&tbuf, buf, version))
			return -EFAULT;
	case IPC_RMID:
		return shmctl_down(ns, shmid, cmd, &tbuf);
	case SHM_LOCK:
	case SHM_UNLOCK:
		return shmctl_do_lock(ns, shmid, cmd);
	default:
		return -EINVAL;
	}
}

/*
 * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
 *