859 lines · 18.8 KB · C
RawDownload
/* * linux/ipc/msg.c * Copyright (C) 1992 Krishna Balasubramanian  * * Removed all the remaining kerneld mess * Catch the -EFAULT stuff properly * Use GFP_KERNEL for messages as in 1.2 * Fixed up the unchecked user space derefs * Copyright (C) 1998 Alan Cox & Andi Kleen * * /proc/sysvipc/msg support (c) 1999 Dragos Acostachioaie <dragos@iname.com> * * mostly rewritten, threaded and wake-one semantics added * MSGMAX limit removed, sysctl's added * (c) 1999 Manfred Spraul <manfreds@colorfullife.com> */ #include <linux/config.h>#include <linux/slab.h>#include <linux/msg.h>#include <linux/spinlock.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/list.h>#include <linux/security.h>#include <linux/sched.h>#include <linux/syscalls.h>#include <asm/current.h>#include <asm/uaccess.h>#include "util.h" /* sysctl: */int msg_ctlmax = MSGMAX;int msg_ctlmnb = MSGMNB;int msg_ctlmni = MSGMNI; /* one msg_receiver structure for each sleeping receiver */struct msg_receiver {	struct list_head r_list;	struct task_struct* r_tsk; 	int r_mode;	long r_msgtype;	long r_maxsize; 	struct msg_msg* volatile r_msg;}; /* one msg_sender for each sleeping sender */struct msg_sender {	struct list_head list;	struct task_struct* tsk;}; #define SEARCH_ANY		1#define SEARCH_EQUAL		2#define SEARCH_NOTEQUAL		3#define SEARCH_LESSEQUAL	4 static atomic_t msg_bytes = ATOMIC_INIT(0);static atomic_t msg_hdrs = ATOMIC_INIT(0); static struct ipc_ids msg_ids; #define msg_lock(id)	((struct msg_queue*)ipc_lock(&msg_ids,id))#define msg_unlock(msq)	ipc_unlock(&(msq)->q_perm)#define msg_rmid(id)	((struct msg_queue*)ipc_rmid(&msg_ids,id))#define msg_checkid(msq, msgid)	\	ipc_checkid(&msg_ids,&msq->q_perm,msgid)#define msg_buildid(id, seq) \	ipc_buildid(&msg_ids, id, seq) static void freeque (struct msg_queue *msq, int id);static int newque (key_t key, int msgflg);#ifdef CONFIG_PROC_FSstatic int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);#endif void __init msg_init (void){	ipc_init_ids(&msg_ids,msg_ctlmni); #ifdef CONFIG_PROC_FS	create_proc_read_entry("sysvipc/msg", 0, NULL, sysvipc_msg_read_proc, NULL);#endif} static int newque (key_t key, int msgflg){	int id;	int retval;	struct msg_queue *msq; 	msq  = ipc_rcu_alloc(sizeof(*msq));	if (!msq) 		return -ENOMEM; 	msq->q_perm.mode = (msgflg & S_IRWXUGO);	msq->q_perm.key = key; 	msq->q_perm.security = NULL;	retval = security_msg_queue_alloc(msq);	if (retval) {		ipc_rcu_putref(msq);		return retval;	} 	id = ipc_addid(&msg_ids, &msq->q_perm, msg_ctlmni);	if(id == -1) {		security_msg_queue_free(msq);		ipc_rcu_putref(msq);		return -ENOSPC;	} 	msq->q_stime = msq->q_rtime = 0;	msq->q_ctime = get_seconds();	msq->q_cbytes = msq->q_qnum = 0;	msq->q_qbytes = msg_ctlmnb;	msq->q_lspid = msq->q_lrpid = 0;	INIT_LIST_HEAD(&msq->q_messages);	INIT_LIST_HEAD(&msq->q_receivers);	INIT_LIST_HEAD(&msq->q_senders);	msg_unlock(msq); 	return msg_buildid(id,msq->q_perm.seq);} static inline void ss_add(struct msg_queue* msq, struct msg_sender* mss){	mss->tsk=current;	current->state=TASK_INTERRUPTIBLE;	list_add_tail(&mss->list,&msq->q_senders);} static inline void ss_del(struct msg_sender* mss){	if(mss->list.next != NULL)		list_del(&mss->list);} static void ss_wakeup(struct list_head* h, int kill){	struct list_head *tmp; 	tmp = h->next;	while (tmp != h) {		struct msg_sender* mss;				mss = list_entry(tmp,struct msg_sender,list);		tmp = tmp->next;		if(kill)			mss->list.next=NULL;		wake_up_process(mss->tsk);	}} static void expunge_all(struct msg_queue* msq, int res){	struct list_head *tmp; 	tmp = msq->q_receivers.next;	while (tmp != &msq->q_receivers) {		struct msg_receiver* msr;				msr = list_entry(tmp,struct msg_receiver,r_list);		tmp = tmp->next;		msr->r_msg = NULL;		wake_up_process(msr->r_tsk);		smp_mb();		msr->r_msg = ERR_PTR(res);	}}/*  * freeque() wakes up waiters on the sender and receiver waiting queue,  * removes the message queue from message queue ID  * array, and cleans up all the messages associated with this queue. * * msg_ids.sem and the spinlock for this message queue is hold * before freeque() is called. msg_ids.sem remains locked on exit. */static void freeque (struct msg_queue *msq, int id){	struct list_head *tmp; 	expunge_all(msq,-EIDRM);	ss_wakeup(&msq->q_senders,1);	msq = msg_rmid(id);	msg_unlock(msq);			tmp = msq->q_messages.next;	while(tmp != &msq->q_messages) {		struct msg_msg* msg = list_entry(tmp,struct msg_msg,m_list);		tmp = tmp->next;		atomic_dec(&msg_hdrs);		free_msg(msg);	}	atomic_sub(msq->q_cbytes, &msg_bytes);	security_msg_queue_free(msq);	ipc_rcu_putref(msq);} asmlinkage long sys_msgget (key_t key, int msgflg){	int id, ret = -EPERM;	struct msg_queue *msq;		down(&msg_ids.sem);	if (key == IPC_PRIVATE) 		ret = newque(key, msgflg);	else if ((id = ipc_findkey(&msg_ids, key)) == -1) { /* key not used */		if (!(msgflg & IPC_CREAT))			ret = -ENOENT;		else			ret = newque(key, msgflg);	} else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {		ret = -EEXIST;	} else {		msq = msg_lock(id);		if(msq==NULL)			BUG();		if (ipcperms(&msq->q_perm, msgflg))			ret = -EACCES;		else {			int qid = msg_buildid(id, msq->q_perm.seq);		    	ret = security_msg_queue_associate(msq, msgflg);			if (!ret)				ret = qid;		}		msg_unlock(msq);	}	up(&msg_ids.sem);	return ret;} static inline unsigned long copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version){	switch(version) {	case IPC_64:		return copy_to_user (buf, in, sizeof(*in));	case IPC_OLD:	    {		struct msqid_ds out; 		memset(&out,0,sizeof(out)); 		ipc64_perm_to_ipc_perm(&in->msg_perm, &out.msg_perm); 		out.msg_stime		= in->msg_stime;		out.msg_rtime		= in->msg_rtime;		out.msg_ctime		= in->msg_ctime; 		if(in->msg_cbytes > USHRT_MAX)			out.msg_cbytes	= USHRT_MAX;		else			out.msg_cbytes	= in->msg_cbytes;		out.msg_lcbytes		= in->msg_cbytes; 		if(in->msg_qnum > USHRT_MAX)			out.msg_qnum	= USHRT_MAX;		else			out.msg_qnum	= in->msg_qnum; 		if(in->msg_qbytes > USHRT_MAX)			out.msg_qbytes	= USHRT_MAX;		else			out.msg_qbytes	= in->msg_qbytes;		out.msg_lqbytes		= in->msg_qbytes; 		out.msg_lspid		= in->msg_lspid;		out.msg_lrpid		= in->msg_lrpid; 		return copy_to_user (buf, &out, sizeof(out));	    }	default:		return -EINVAL;	}} struct msq_setbuf {	unsigned long	qbytes;	uid_t		uid;	gid_t		gid;	mode_t		mode;}; static inline unsigned long copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version){	switch(version) {	case IPC_64:	    {		struct msqid64_ds tbuf; 		if (copy_from_user (&tbuf, buf, sizeof (tbuf)))			return -EFAULT; 		out->qbytes		= tbuf.msg_qbytes;		out->uid		= tbuf.msg_perm.uid;		out->gid		= tbuf.msg_perm.gid;		out->mode		= tbuf.msg_perm.mode; 		return 0;	    }	case IPC_OLD:	    {		struct msqid_ds tbuf_old; 		if (copy_from_user (&tbuf_old, buf, sizeof (tbuf_old)))			return -EFAULT; 		out->uid		= tbuf_old.msg_perm.uid;		out->gid		= tbuf_old.msg_perm.gid;		out->mode		= tbuf_old.msg_perm.mode; 		if(tbuf_old.msg_qbytes == 0)			out->qbytes	= tbuf_old.msg_lqbytes;		else			out->qbytes	= tbuf_old.msg_qbytes; 		return 0;	    }	default:		return -EINVAL;	}} asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf){	int err, version;	struct msg_queue *msq;	struct msq_setbuf setbuf;	struct kern_ipc_perm *ipcp;		if (msqid < 0 || cmd < 0)		return -EINVAL; 	version = ipc_parse_version(&cmd); 	switch (cmd) {	case IPC_INFO: 	case MSG_INFO: 	{ 		struct msginfo msginfo;		int max_id;		if (!buf)			return -EFAULT;		/* We must not return kernel stack data.		 * due to padding, it's not enough		 * to set all member fields.		 */ 		err = security_msg_queue_msgctl(NULL, cmd);		if (err)			return err; 		memset(&msginfo,0,sizeof(msginfo));			msginfo.msgmni = msg_ctlmni;		msginfo.msgmax = msg_ctlmax;		msginfo.msgmnb = msg_ctlmnb;		msginfo.msgssz = MSGSSZ;		msginfo.msgseg = MSGSEG;		down(&msg_ids.sem);		if (cmd == MSG_INFO) {			msginfo.msgpool = msg_ids.in_use;			msginfo.msgmap = atomic_read(&msg_hdrs);			msginfo.msgtql = atomic_read(&msg_bytes);		} else {			msginfo.msgmap = MSGMAP;			msginfo.msgpool = MSGPOOL;			msginfo.msgtql = MSGTQL;		}		max_id = msg_ids.max_id;		up(&msg_ids.sem);		if (copy_to_user (buf, &msginfo, sizeof(struct msginfo)))			return -EFAULT;		return (max_id < 0) ? 0: max_id;	}	case MSG_STAT:	case IPC_STAT:	{		struct msqid64_ds tbuf;		int success_return;		if (!buf)			return -EFAULT;		if(cmd == MSG_STAT && msqid >= msg_ids.entries->size)			return -EINVAL; 		memset(&tbuf,0,sizeof(tbuf)); 		msq = msg_lock(msqid);		if (msq == NULL)			return -EINVAL; 		if(cmd == MSG_STAT) {			success_return = msg_buildid(msqid, msq->q_perm.seq);		} else {			err = -EIDRM;			if (msg_checkid(msq,msqid))				goto out_unlock;			success_return = 0;		}		err = -EACCES;		if (ipcperms (&msq->q_perm, S_IRUGO))			goto out_unlock; 		err = security_msg_queue_msgctl(msq, cmd);		if (err)			goto out_unlock; 		kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);		tbuf.msg_stime  = msq->q_stime;		tbuf.msg_rtime  = msq->q_rtime;		tbuf.msg_ctime  = msq->q_ctime;		tbuf.msg_cbytes = msq->q_cbytes;		tbuf.msg_qnum   = msq->q_qnum;		tbuf.msg_qbytes = msq->q_qbytes;		tbuf.msg_lspid  = msq->q_lspid;		tbuf.msg_lrpid  = msq->q_lrpid;		msg_unlock(msq);		if (copy_msqid_to_user(buf, &tbuf, version))			return -EFAULT;		return success_return;	}	case IPC_SET:		if (!buf)			return -EFAULT;		if (copy_msqid_from_user (&setbuf, buf, version))			return -EFAULT;		break;	case IPC_RMID:		break;	default:		return  -EINVAL;	} 	down(&msg_ids.sem);	msq = msg_lock(msqid);	err=-EINVAL;	if (msq == NULL)		goto out_up; 	err = -EIDRM;	if (msg_checkid(msq,msqid))		goto out_unlock_up;	ipcp = &msq->q_perm;	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 > 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 (msq, msqid); 		break;	}	err = 0;out_up:	up(&msg_ids.sem);	return err;out_unlock_up:	msg_unlock(msq);	goto out_up;out_unlock:	msg_unlock(msq);	return err;} static int testmsg(struct msg_msg* msg,long type,int mode){	switch(mode)	{		case SEARCH_ANY:

Lines 1–500 of 859.