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

Commit a0d092fc authored by Pierre Peiffer's avatar Pierre Peiffer Committed by Linus Torvalds
Browse files

IPC/message queues: introduce msgctl_down



Currently, sys_msgctl is not easy to read.

This patch tries to improve that by introducing the msgctl_down function to
handle all commands requiring the rwmutex to be taken in write mode (ie
IPC_SET and IPC_RMID for now).  It is the equivalent function of semctl_down
for message queues.

This greatly changes the readability of sys_msgctl and also harmonizes the way
these commands are handled among all IPCs.

Signed-off-by: default avatarPierre Peiffer <pierre.peiffer@bull.net>
Acked-by: default avatarSerge Hallyn <serue@us.ibm.com>
Cc: Nadia Derbey <Nadia.Derbey@bull.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 8d4cc8b5
Loading
Loading
Loading
Loading
+89 −73
Original line number Original line Diff line number Diff line
@@ -436,10 +436,95 @@ copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version)
	}
	}
}
}


asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
/*
 * This function handles some msgctl commands which require the rw_mutex
 * to be held in write mode.
 * NOTE: no locks must be held, the rw_mutex is taken inside this function.
 */
static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
		       struct msqid_ds __user *buf, int version)
{
{
	struct kern_ipc_perm *ipcp;
	struct kern_ipc_perm *ipcp;
	struct msq_setbuf uninitialized_var(setbuf);
	struct msq_setbuf setbuf;
	struct msg_queue *msq;
	int err;

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

	down_write(&msg_ids(ns).rw_mutex);
	msq = msg_lock_check_down(ns, msqid);
	if (IS_ERR(msq)) {
		err = PTR_ERR(msq);
		goto out_up;
	}

	ipcp = &msq->q_perm;

	err = audit_ipc_obj(ipcp);
	if (err)
		goto out_unlock;

	if (cmd == IPC_SET) {
		err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid,
					 setbuf.mode);
		if (err)
			goto out_unlock;
	}

	if (current->euid != ipcp->cuid &&
	    current->euid != ipcp->uid &&
	    !capable(CAP_SYS_ADMIN)) {
		/* We _could_ check for CAP_CHOWN above, but we don't */
		err = -EPERM;
		goto out_unlock;
	}

	err = security_msg_queue_msgctl(msq, cmd);
	if (err)
		goto out_unlock;

	switch (cmd) {
	case IPC_RMID:
		freeque(ns, ipcp);
		goto out_up;
	case IPC_SET:
		if (setbuf.qbytes > ns->msg_ctlmnb &&
		    !capable(CAP_SYS_RESOURCE)) {
			err = -EPERM;
			goto out_unlock;
		}

		msq->q_qbytes = setbuf.qbytes;

		ipcp->uid = setbuf.uid;
		ipcp->gid = setbuf.gid;
		ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
			     (S_IRWXUGO & setbuf.mode);
		msq->q_ctime = get_seconds();
		/* sleeping receivers might be excluded by
		 * stricter permissions.
		 */
		expunge_all(msq, -EAGAIN);
		/* sleeping senders might be able to send
		 * due to a larger queue size.
		 */
		ss_wakeup(&msq->q_senders, 0);
		break;
	default:
		err = -EINVAL;
	}
out_unlock:
	msg_unlock(msq);
out_up:
	up_write(&msg_ids(ns).rw_mutex);
	return err;
}

asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
{
	struct msg_queue *msq;
	struct msg_queue *msq;
	int err, version;
	int err, version;
	struct ipc_namespace *ns;
	struct ipc_namespace *ns;
@@ -535,82 +620,13 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
		return success_return;
		return success_return;
	}
	}
	case IPC_SET:
	case IPC_SET:
		if (!buf)
			return -EFAULT;
		if (copy_msqid_from_user(&setbuf, buf, version))
			return -EFAULT;
		break;
	case IPC_RMID:
	case IPC_RMID:
		break;
		err = msgctl_down(ns, msqid, cmd, buf, version);
		return err;
	default:
	default:
		return  -EINVAL;
		return  -EINVAL;
	}
	}


	down_write(&msg_ids(ns).rw_mutex);
	msq = msg_lock_check_down(ns, msqid);
	if (IS_ERR(msq)) {
		err = PTR_ERR(msq);
		goto out_up;
	}

	ipcp = &msq->q_perm;

	err = audit_ipc_obj(ipcp);
	if (err)
		goto out_unlock_up;
	if (cmd == IPC_SET) {
		err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid,
					 setbuf.mode);
		if (err)
			goto out_unlock_up;
	}

	err = -EPERM;
	if (current->euid != ipcp->cuid &&
	    current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
		/* We _could_ check for CAP_CHOWN above, but we don't */
		goto out_unlock_up;

	err = security_msg_queue_msgctl(msq, cmd);
	if (err)
		goto out_unlock_up;

	switch (cmd) {
	case IPC_SET:
	{
		err = -EPERM;
		if (setbuf.qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
			goto out_unlock_up;

		msq->q_qbytes = setbuf.qbytes;

		ipcp->uid = setbuf.uid;
		ipcp->gid = setbuf.gid;
		ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
			     (S_IRWXUGO & setbuf.mode);
		msq->q_ctime = get_seconds();
		/* sleeping receivers might be excluded by
		 * stricter permissions.
		 */
		expunge_all(msq, -EAGAIN);
		/* sleeping senders might be able to send
		 * due to a larger queue size.
		 */
		ss_wakeup(&msq->q_senders, 0);
		msg_unlock(msq);
		break;
	}
	case IPC_RMID:
		freeque(ns, &msq->q_perm);
		break;
	}
	err = 0;
out_up:
	up_write(&msg_ids(ns).rw_mutex);
	return err;
out_unlock_up:
	msg_unlock(msq);
	goto out_up;
out_unlock:
out_unlock:
	msg_unlock(msq);
	msg_unlock(msq);
	return err;
	return err;