mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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: Pierre Peiffer <pierre.peiffer@bull.net> Acked-by: Serge Hallyn <serue@us.ibm.com> Cc: Nadia Derbey <Nadia.Derbey@bull.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									8d4cc8b5c5
								
							
						
					
					
						commit
						a0d092fc2d
					
				
					 1 changed files with 89 additions and 73 deletions
				
			
		
							
								
								
									
										162
									
								
								ipc/msg.c
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								ipc/msg.c
									
									
									
									
									
								
							| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue