944 lines · 20.9 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 <manfred@colorfullife.com> * * support for audit of ipc object properties and permission changes * Dustin Kirkland <dustin.kirkland@us.ibm.com> * * namespaces support * OpenVZ, SWsoft Inc. * Pavel Emelianov <xemul@openvz.org> */ #include <linux/capability.h>#include <linux/msg.h>#include <linux/spinlock.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/proc_fs.h>#include <linux/list.h>#include <linux/security.h>#include <linux/sched.h>#include <linux/syscalls.h>#include <linux/audit.h>#include <linux/seq_file.h>#include <linux/rwsem.h>#include <linux/nsproxy.h>#include <linux/ipc_namespace.h> #include <asm/current.h>#include <asm/uaccess.h>#include "util.h" /* * 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 #define msg_ids(ns)	((ns)->ids[IPC_MSG_IDS]) #define msg_unlock(msq)		ipc_unlock(&(msq)->q_perm) static void freeque(struct ipc_namespace *, struct kern_ipc_perm *);static int newque(struct ipc_namespace *, struct ipc_params *);#ifdef CONFIG_PROC_FSstatic int sysvipc_msg_proc_show(struct seq_file *s, void *it);#endif /* * Scale msgmni with the available lowmem size: the memory dedicated to msg * queues should occupy at most 1/MSG_MEM_SCALE of lowmem. * Also take into account the number of nsproxies created so far. * This should be done staying within the (MSGMNI , IPCMNI/nr_ipc_ns) range. */void recompute_msgmni(struct ipc_namespace *ns){	struct sysinfo i;	unsigned long allowed;	int nb_ns; 	si_meminfo(&i);	allowed = (((i.totalram - i.totalhigh) / MSG_MEM_SCALE) * i.mem_unit)		/ MSGMNB;	nb_ns = atomic_read(&nr_ipc_ns);	allowed /= nb_ns; 	if (allowed < MSGMNI) {		ns->msg_ctlmni = MSGMNI;		return;	} 	if (allowed > IPCMNI / nb_ns) {		ns->msg_ctlmni = IPCMNI / nb_ns;		return;	} 	ns->msg_ctlmni = allowed;} void msg_init_ns(struct ipc_namespace *ns){	ns->msg_ctlmax = MSGMAX;	ns->msg_ctlmnb = MSGMNB; 	recompute_msgmni(ns); 	atomic_set(&ns->msg_bytes, 0);	atomic_set(&ns->msg_hdrs, 0);	ipc_init_ids(&ns->ids[IPC_MSG_IDS]);} #ifdef CONFIG_IPC_NSvoid msg_exit_ns(struct ipc_namespace *ns){	free_ipcs(ns, &msg_ids(ns), freeque);	idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr);}#endif void __init msg_init(void){	msg_init_ns(&init_ipc_ns); 	printk(KERN_INFO "msgmni has been set to %d\n",		init_ipc_ns.msg_ctlmni); 	ipc_init_proc_interface("sysvipc/msg",				"       key      msqid perms      cbytes       qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n",				IPC_MSG_IDS, sysvipc_msg_proc_show);} /* * msg_lock_(check_) routines are called in the paths where the rw_mutex * is not held. */static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id){	struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id); 	if (IS_ERR(ipcp))		return (struct msg_queue *)ipcp; 	return container_of(ipcp, struct msg_queue, q_perm);} static inline struct msg_queue *msg_lock_check(struct ipc_namespace *ns,						int id){	struct kern_ipc_perm *ipcp = ipc_lock_check(&msg_ids(ns), id); 	if (IS_ERR(ipcp))		return (struct msg_queue *)ipcp; 	return container_of(ipcp, struct msg_queue, q_perm);} static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s){	ipc_rmid(&msg_ids(ns), &s->q_perm);} /** * newque - Create a new msg queue * @ns: namespace * @params: ptr to the structure that contains the key and msgflg * * Called with msg_ids.rw_mutex held (writer) */static int newque(struct ipc_namespace *ns, struct ipc_params *params){	struct msg_queue *msq;	int id, retval;	key_t key = params->key;	int msgflg = params->flg; 	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;	} 	/*	 * ipc_addid() locks msq	 */	id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);	if (id < 0) {		security_msg_queue_free(msq);		ipc_rcu_putref(msq);		return id;	} 	msq->q_stime = msq->q_rtime = 0;	msq->q_ctime = get_seconds();	msq->q_cbytes = msq->q_qnum = 0;	msq->q_qbytes = ns->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 msq->q_perm.id;} 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 IDR, and cleans up all the * messages associated with this queue. * * msg_ids.rw_mutex (writer) and the spinlock for this message queue are held * before freeque() is called. msg_ids.rw_mutex remains locked on exit. */static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp){	struct list_head *tmp;	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm); 	expunge_all(msq, -EIDRM);	ss_wakeup(&msq->q_senders, 1);	msg_rmid(ns, msq);	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(&ns->msg_hdrs);		free_msg(msg);	}	atomic_sub(msq->q_cbytes, &ns->msg_bytes);	security_msg_queue_free(msq);	ipc_rcu_putref(msq);} /* * Called with msg_ids.rw_mutex and ipcp locked. */static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg){	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm); 	return security_msg_queue_associate(msq, msgflg);} SYSCALL_DEFINE2(msgget, key_t, key, int, msgflg){	struct ipc_namespace *ns;	struct ipc_ops msg_ops;	struct ipc_params msg_params; 	ns = current->nsproxy->ipc_ns; 	msg_ops.getnew = newque;	msg_ops.associate = msg_security;	msg_ops.more_checks = NULL; 	msg_params.key = key;	msg_params.flg = msgflg; 	return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);} static inline unsigned longcopy_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 > USHORT_MAX)			out.msg_cbytes	= USHORT_MAX;		else			out.msg_cbytes	= in->msg_cbytes;		out.msg_lcbytes		= in->msg_cbytes; 		if (in->msg_qnum > USHORT_MAX)			out.msg_qnum	= USHORT_MAX;		else			out.msg_qnum	= in->msg_qnum; 		if (in->msg_qbytes > USHORT_MAX)			out.msg_qbytes	= USHORT_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;	}} static inline unsigned longcopy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version){	switch(version) {	case IPC_64:		if (copy_from_user(out, buf, sizeof(*out)))			return -EFAULT;		return 0;	case IPC_OLD:	{		struct msqid_ds tbuf_old; 		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))			return -EFAULT; 		out->msg_perm.uid      	= tbuf_old.msg_perm.uid;		out->msg_perm.gid      	= tbuf_old.msg_perm.gid;		out->msg_perm.mode     	= tbuf_old.msg_perm.mode; 		if (tbuf_old.msg_qbytes == 0)			out->msg_qbytes	= tbuf_old.msg_lqbytes;		else			out->msg_qbytes	= tbuf_old.msg_qbytes; 		return 0;	}	default:		return -EINVAL;	}} /* * 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 msqid64_ds uninitialized_var(msqid64);	struct msg_queue *msq;	int err; 	if (cmd == IPC_SET) {		if (copy_msqid_from_user(&msqid64, buf, version))			return -EFAULT;	} 	ipcp = ipcctl_pre_down(&msg_ids(ns), msqid, cmd,			       &msqid64.msg_perm, msqid64.msg_qbytes);	if (IS_ERR(ipcp))		return PTR_ERR(ipcp); 	msq = container_of(ipcp, struct msg_queue, q_perm); 	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 (msqid64.msg_qbytes > ns->msg_ctlmnb &&		    !capable(CAP_SYS_RESOURCE)) {			err = -EPERM;			goto out_unlock;		} 		msq->q_qbytes = msqid64.msg_qbytes; 		ipc_update_perm(&msqid64.msg_perm, ipcp);		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;} SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf){	struct msg_queue *msq;	int err, version;	struct ipc_namespace *ns; 	if (msqid < 0 || cmd < 0)		return -EINVAL; 	version = ipc_parse_version(&cmd);	ns = current->nsproxy->ipc_ns; 	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 = ns->msg_ctlmni;

Lines 1–500 of 944.