xref: /openbmc/linux/ipc/msg.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/ipc/msg.c
41da177e4SLinus Torvalds  * Copyright (C) 1992 Krishna Balasubramanian
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Removed all the remaining kerneld mess
71da177e4SLinus Torvalds  * Catch the -EFAULT stuff properly
81da177e4SLinus Torvalds  * Use GFP_KERNEL for messages as in 1.2
91da177e4SLinus Torvalds  * Fixed up the unchecked user space derefs
101da177e4SLinus Torvalds  * Copyright (C) 1998 Alan Cox & Andi Kleen
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * /proc/sysvipc/msg support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  * mostly rewritten, threaded and wake-one semantics added
151da177e4SLinus Torvalds  * MSGMAX limit removed, sysctl's added
16624dffcbSChristian Kujau  * (c) 1999 Manfred Spraul <manfred@colorfullife.com>
17073115d6SSteve Grubb  *
18073115d6SSteve Grubb  * support for audit of ipc object properties and permission changes
19073115d6SSteve Grubb  * Dustin Kirkland <dustin.kirkland@us.ibm.com>
201e786937SKirill Korotaev  *
211e786937SKirill Korotaev  * namespaces support
221e786937SKirill Korotaev  * OpenVZ, SWsoft Inc.
231e786937SKirill Korotaev  * Pavel Emelianov <xemul@openvz.org>
241da177e4SLinus Torvalds  */
251da177e4SLinus Torvalds 
26c59ede7bSRandy.Dunlap #include <linux/capability.h>
271da177e4SLinus Torvalds #include <linux/msg.h>
281da177e4SLinus Torvalds #include <linux/spinlock.h>
291da177e4SLinus Torvalds #include <linux/init.h>
30f7bf3df8SNadia Derbey #include <linux/mm.h>
311da177e4SLinus Torvalds #include <linux/proc_fs.h>
321da177e4SLinus Torvalds #include <linux/list.h>
331da177e4SLinus Torvalds #include <linux/security.h>
3484f001e1SIngo Molnar #include <linux/sched/wake_q.h>
351da177e4SLinus Torvalds #include <linux/syscalls.h>
361da177e4SLinus Torvalds #include <linux/audit.h>
3719b4946cSMike Waychison #include <linux/seq_file.h>
383e148c79SNadia Derbey #include <linux/rwsem.h>
391e786937SKirill Korotaev #include <linux/nsproxy.h>
40ae5e1b22SPavel Emelyanov #include <linux/ipc_namespace.h>
410eb71a9dSNeilBrown #include <linux/rhashtable.h>
4272d1e611SJiebin Sun #include <linux/percpu_counter.h>
435f921ae9SIngo Molnar 
441da177e4SLinus Torvalds #include <asm/current.h>
457153e402SPaul McQuade #include <linux/uaccess.h>
461da177e4SLinus Torvalds #include "util.h"
471da177e4SLinus Torvalds 
4834b56df9SEric W. Biederman /* one msq_queue structure for each present queue on the system */
4934b56df9SEric W. Biederman struct msg_queue {
5034b56df9SEric W. Biederman 	struct kern_ipc_perm q_perm;
5134b56df9SEric W. Biederman 	time64_t q_stime;		/* last msgsnd time */
5234b56df9SEric W. Biederman 	time64_t q_rtime;		/* last msgrcv time */
5334b56df9SEric W. Biederman 	time64_t q_ctime;		/* last change time */
5434b56df9SEric W. Biederman 	unsigned long q_cbytes;		/* current number of bytes on queue */
5534b56df9SEric W. Biederman 	unsigned long q_qnum;		/* number of messages in queue */
5634b56df9SEric W. Biederman 	unsigned long q_qbytes;		/* max number of bytes on queue */
5739a4940eSEric W. Biederman 	struct pid *q_lspid;		/* pid of last msgsnd */
5839a4940eSEric W. Biederman 	struct pid *q_lrpid;		/* last receive pid */
5934b56df9SEric W. Biederman 
6034b56df9SEric W. Biederman 	struct list_head q_messages;
6134b56df9SEric W. Biederman 	struct list_head q_receivers;
6234b56df9SEric W. Biederman 	struct list_head q_senders;
6334b56df9SEric W. Biederman } __randomize_layout;
6434b56df9SEric W. Biederman 
650d97a82bSManfred Spraul /*
660d97a82bSManfred Spraul  * MSG_BARRIER Locking:
670d97a82bSManfred Spraul  *
680d97a82bSManfred Spraul  * Similar to the optimization used in ipc/mqueue.c, one syscall return path
690d97a82bSManfred Spraul  * does not acquire any locks when it sees that a message exists in
700d97a82bSManfred Spraul  * msg_receiver.r_msg. Therefore r_msg is set using smp_store_release()
710d97a82bSManfred Spraul  * and accessed using READ_ONCE()+smp_acquire__after_ctrl_dep(). In addition,
720d97a82bSManfred Spraul  * wake_q_add_safe() is used. See ipc/mqueue.c for more details
730d97a82bSManfred Spraul  */
740d97a82bSManfred Spraul 
754bb6657dSDavidlohr Bueso /* one msg_receiver structure for each sleeping receiver */
761da177e4SLinus Torvalds struct msg_receiver {
771da177e4SLinus Torvalds 	struct list_head	r_list;
781da177e4SLinus Torvalds 	struct task_struct	*r_tsk;
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	int			r_mode;
811da177e4SLinus Torvalds 	long			r_msgtype;
821da177e4SLinus Torvalds 	long			r_maxsize;
831da177e4SLinus Torvalds 
84ee51636cSSebastian Andrzej Siewior 	struct msg_msg		*r_msg;
851da177e4SLinus Torvalds };
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds /* one msg_sender for each sleeping sender */
881da177e4SLinus Torvalds struct msg_sender {
891da177e4SLinus Torvalds 	struct list_head	list;
901da177e4SLinus Torvalds 	struct task_struct	*tsk;
91ed27f912SDavidlohr Bueso 	size_t                  msgsz;
921da177e4SLinus Torvalds };
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds #define SEARCH_ANY		1
951da177e4SLinus Torvalds #define SEARCH_EQUAL		2
961da177e4SLinus Torvalds #define SEARCH_NOTEQUAL		3
971da177e4SLinus Torvalds #define SEARCH_LESSEQUAL	4
988ac6ed58SPeter Hurley #define SEARCH_NUMBER		5
991da177e4SLinus Torvalds 
100ed2ddbf8SPierre Peiffer #define msg_ids(ns)	((ns)->ids[IPC_MSG_IDS])
1011e786937SKirill Korotaev 
msq_obtain_object(struct ipc_namespace * ns,int id)102a5001a0dSDavidlohr Bueso static inline struct msg_queue *msq_obtain_object(struct ipc_namespace *ns, int id)
103a5001a0dSDavidlohr Bueso {
10455b7ae50SDavidlohr Bueso 	struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&msg_ids(ns), id);
105a5001a0dSDavidlohr Bueso 
106a5001a0dSDavidlohr Bueso 	if (IS_ERR(ipcp))
107a5001a0dSDavidlohr Bueso 		return ERR_CAST(ipcp);
108a5001a0dSDavidlohr Bueso 
109a5001a0dSDavidlohr Bueso 	return container_of(ipcp, struct msg_queue, q_perm);
110a5001a0dSDavidlohr Bueso }
111a5001a0dSDavidlohr Bueso 
msq_obtain_object_check(struct ipc_namespace * ns,int id)112a5001a0dSDavidlohr Bueso static inline struct msg_queue *msq_obtain_object_check(struct ipc_namespace *ns,
113a5001a0dSDavidlohr Bueso 							int id)
114a5001a0dSDavidlohr Bueso {
115a5001a0dSDavidlohr Bueso 	struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&msg_ids(ns), id);
116a5001a0dSDavidlohr Bueso 
117a5001a0dSDavidlohr Bueso 	if (IS_ERR(ipcp))
118a5001a0dSDavidlohr Bueso 		return ERR_CAST(ipcp);
119a5001a0dSDavidlohr Bueso 
120a5001a0dSDavidlohr Bueso 	return container_of(ipcp, struct msg_queue, q_perm);
121a5001a0dSDavidlohr Bueso }
122a5001a0dSDavidlohr Bueso 
msg_rmid(struct ipc_namespace * ns,struct msg_queue * s)1237ca7e564SNadia Derbey static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
1247ca7e564SNadia Derbey {
1257ca7e564SNadia Derbey 	ipc_rmid(&msg_ids(ns), &s->q_perm);
1267ca7e564SNadia Derbey }
1277ca7e564SNadia Derbey 
msg_rcu_free(struct rcu_head * head)12853dad6d3SDavidlohr Bueso static void msg_rcu_free(struct rcu_head *head)
12953dad6d3SDavidlohr Bueso {
130dba4cdd3SManfred Spraul 	struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu);
131dba4cdd3SManfred Spraul 	struct msg_queue *msq = container_of(p, struct msg_queue, q_perm);
13253dad6d3SDavidlohr Bueso 
133d8c6e854SEric W. Biederman 	security_msg_queue_free(&msq->q_perm);
134bc8136a5SVasily Averin 	kfree(msq);
13552f90890SKees Cook }
13652f90890SKees Cook 
137f4566f04SNadia Derbey /**
138f4566f04SNadia Derbey  * newque - Create a new msg queue
139f4566f04SNadia Derbey  * @ns: namespace
140f4566f04SNadia Derbey  * @params: ptr to the structure that contains the key and msgflg
141f4566f04SNadia Derbey  *
142d9a605e4SDavidlohr Bueso  * Called with msg_ids.rwsem held (writer)
143f4566f04SNadia Derbey  */
newque(struct ipc_namespace * ns,struct ipc_params * params)1447748dbfaSNadia Derbey static int newque(struct ipc_namespace *ns, struct ipc_params *params)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	struct msg_queue *msq;
14751c23b7bSManfred Spraul 	int retval;
1487748dbfaSNadia Derbey 	key_t key = params->key;
1497748dbfaSNadia Derbey 	int msgflg = params->flg;
1501da177e4SLinus Torvalds 
15118319498SVasily Averin 	msq = kmalloc(sizeof(*msq), GFP_KERNEL_ACCOUNT);
152fb259c31SKees Cook 	if (unlikely(!msq))
1531da177e4SLinus Torvalds 		return -ENOMEM;
1541da177e4SLinus Torvalds 
1555a06a363SIngo Molnar 	msq->q_perm.mode = msgflg & S_IRWXUGO;
1561da177e4SLinus Torvalds 	msq->q_perm.key = key;
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 	msq->q_perm.security = NULL;
159d8c6e854SEric W. Biederman 	retval = security_msg_queue_alloc(&msq->q_perm);
1601da177e4SLinus Torvalds 	if (retval) {
161bc8136a5SVasily Averin 		kfree(msq);
1621da177e4SLinus Torvalds 		return retval;
1631da177e4SLinus Torvalds 	}
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	msq->q_stime = msq->q_rtime = 0;
16650578ea9SDeepa Dinamani 	msq->q_ctime = ktime_get_real_seconds();
1671da177e4SLinus Torvalds 	msq->q_cbytes = msq->q_qnum = 0;
1681e786937SKirill Korotaev 	msq->q_qbytes = ns->msg_ctlmnb;
16939a4940eSEric W. Biederman 	msq->q_lspid = msq->q_lrpid = NULL;
1701da177e4SLinus Torvalds 	INIT_LIST_HEAD(&msq->q_messages);
1711da177e4SLinus Torvalds 	INIT_LIST_HEAD(&msq->q_receivers);
1721da177e4SLinus Torvalds 	INIT_LIST_HEAD(&msq->q_senders);
1737ca7e564SNadia Derbey 
174b9a53227SLinus Torvalds 	/* ipc_addid() locks msq upon success. */
17551c23b7bSManfred Spraul 	retval = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
17651c23b7bSManfred Spraul 	if (retval < 0) {
17739cfffd7SManfred Spraul 		ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
17851c23b7bSManfred Spraul 		return retval;
179b9a53227SLinus Torvalds 	}
180b9a53227SLinus Torvalds 
181cf9d5d78SDavidlohr Bueso 	ipc_unlock_object(&msq->q_perm);
182dbfcd91fSDavidlohr Bueso 	rcu_read_unlock();
1831da177e4SLinus Torvalds 
1847ca7e564SNadia Derbey 	return msq->q_perm.id;
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
msg_fits_inqueue(struct msg_queue * msq,size_t msgsz)187ed27f912SDavidlohr Bueso static inline bool msg_fits_inqueue(struct msg_queue *msq, size_t msgsz)
188ed27f912SDavidlohr Bueso {
189ed27f912SDavidlohr Bueso 	return msgsz + msq->q_cbytes <= msq->q_qbytes &&
190ed27f912SDavidlohr Bueso 		1 + msq->q_qnum <= msq->q_qbytes;
191ed27f912SDavidlohr Bueso }
192ed27f912SDavidlohr Bueso 
ss_add(struct msg_queue * msq,struct msg_sender * mss,size_t msgsz)193ed27f912SDavidlohr Bueso static inline void ss_add(struct msg_queue *msq,
194ed27f912SDavidlohr Bueso 			  struct msg_sender *mss, size_t msgsz)
1951da177e4SLinus Torvalds {
1961da177e4SLinus Torvalds 	mss->tsk = current;
197ed27f912SDavidlohr Bueso 	mss->msgsz = msgsz;
1980d97a82bSManfred Spraul 	/*
1990d97a82bSManfred Spraul 	 * No memory barrier required: we did ipc_lock_object(),
2000d97a82bSManfred Spraul 	 * and the waker obtains that lock before calling wake_q_add().
2010d97a82bSManfred Spraul 	 */
202f75a2f35SDavidlohr Bueso 	__set_current_state(TASK_INTERRUPTIBLE);
2031da177e4SLinus Torvalds 	list_add_tail(&mss->list, &msq->q_senders);
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds 
ss_del(struct msg_sender * mss)2061da177e4SLinus Torvalds static inline void ss_del(struct msg_sender *mss)
2071da177e4SLinus Torvalds {
208ed27f912SDavidlohr Bueso 	if (mss->list.next)
2091da177e4SLinus Torvalds 		list_del(&mss->list);
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
ss_wakeup(struct msg_queue * msq,struct wake_q_head * wake_q,bool kill)212ed27f912SDavidlohr Bueso static void ss_wakeup(struct msg_queue *msq,
213d0d6a2a9SDavidlohr Bueso 		      struct wake_q_head *wake_q, bool kill)
2141da177e4SLinus Torvalds {
21541239fe8SNikola Pajkovsky 	struct msg_sender *mss, *t;
216ed27f912SDavidlohr Bueso 	struct task_struct *stop_tsk = NULL;
217ed27f912SDavidlohr Bueso 	struct list_head *h = &msq->q_senders;
2181da177e4SLinus Torvalds 
21941239fe8SNikola Pajkovsky 	list_for_each_entry_safe(mss, t, h, list) {
2201da177e4SLinus Torvalds 		if (kill)
2211da177e4SLinus Torvalds 			mss->list.next = NULL;
222ed27f912SDavidlohr Bueso 
223ed27f912SDavidlohr Bueso 		/*
224ed27f912SDavidlohr Bueso 		 * Stop at the first task we don't wakeup,
225ed27f912SDavidlohr Bueso 		 * we've already iterated the original
226ed27f912SDavidlohr Bueso 		 * sender queue.
227ed27f912SDavidlohr Bueso 		 */
228ed27f912SDavidlohr Bueso 		else if (stop_tsk == mss->tsk)
229ed27f912SDavidlohr Bueso 			break;
230ed27f912SDavidlohr Bueso 		/*
231ed27f912SDavidlohr Bueso 		 * We are not in an EIDRM scenario here, therefore
232ed27f912SDavidlohr Bueso 		 * verify that we really need to wakeup the task.
233ed27f912SDavidlohr Bueso 		 * To maintain current semantics and wakeup order,
234ed27f912SDavidlohr Bueso 		 * move the sender to the tail on behalf of the
235ed27f912SDavidlohr Bueso 		 * blocked task.
236ed27f912SDavidlohr Bueso 		 */
237ed27f912SDavidlohr Bueso 		else if (!msg_fits_inqueue(msq, mss->msgsz)) {
238ed27f912SDavidlohr Bueso 			if (!stop_tsk)
239ed27f912SDavidlohr Bueso 				stop_tsk = mss->tsk;
240ed27f912SDavidlohr Bueso 
241ed27f912SDavidlohr Bueso 			list_move_tail(&mss->list, &msq->q_senders);
242ed27f912SDavidlohr Bueso 			continue;
243ed27f912SDavidlohr Bueso 		}
244ed27f912SDavidlohr Bueso 
245e3658538SDavidlohr Bueso 		wake_q_add(wake_q, mss->tsk);
2461da177e4SLinus Torvalds 	}
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds 
expunge_all(struct msg_queue * msq,int res,struct wake_q_head * wake_q)249ee51636cSSebastian Andrzej Siewior static void expunge_all(struct msg_queue *msq, int res,
250ee51636cSSebastian Andrzej Siewior 			struct wake_q_head *wake_q)
2511da177e4SLinus Torvalds {
25241239fe8SNikola Pajkovsky 	struct msg_receiver *msr, *t;
2531da177e4SLinus Torvalds 
25441239fe8SNikola Pajkovsky 	list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
255a11ddb37SVarad Gautam 		struct task_struct *r_tsk;
256a11ddb37SVarad Gautam 
257a11ddb37SVarad Gautam 		r_tsk = get_task_struct(msr->r_tsk);
2580d97a82bSManfred Spraul 
2590d97a82bSManfred Spraul 		/* see MSG_BARRIER for purpose/pairing */
2600d97a82bSManfred Spraul 		smp_store_release(&msr->r_msg, ERR_PTR(res));
261a11ddb37SVarad Gautam 		wake_q_add_safe(wake_q, r_tsk);
2621da177e4SLinus Torvalds 	}
2631da177e4SLinus Torvalds }
2645a06a363SIngo Molnar 
2651da177e4SLinus Torvalds /*
2661da177e4SLinus Torvalds  * freeque() wakes up waiters on the sender and receiver waiting queue,
267f4566f04SNadia Derbey  * removes the message queue from message queue ID IDR, and cleans up all the
268f4566f04SNadia Derbey  * messages associated with this queue.
2691da177e4SLinus Torvalds  *
270d9a605e4SDavidlohr Bueso  * msg_ids.rwsem (writer) and the spinlock for this message queue are held
271d9a605e4SDavidlohr Bueso  * before freeque() is called. msg_ids.rwsem remains locked on exit.
2721da177e4SLinus Torvalds  */
freeque(struct ipc_namespace * ns,struct kern_ipc_perm * ipcp)27301b8b07aSPierre Peiffer static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
2744b78e201SJules Irenge 	__releases(RCU)
2754b78e201SJules Irenge 	__releases(&msq->q_perm)
2761da177e4SLinus Torvalds {
27741239fe8SNikola Pajkovsky 	struct msg_msg *msg, *t;
27801b8b07aSPierre Peiffer 	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
279194a6b5bSWaiman Long 	DEFINE_WAKE_Q(wake_q);
2801da177e4SLinus Torvalds 
281ee51636cSSebastian Andrzej Siewior 	expunge_all(msq, -EIDRM, &wake_q);
282ed27f912SDavidlohr Bueso 	ss_wakeup(msq, &wake_q, true);
2837ca7e564SNadia Derbey 	msg_rmid(ns, msq);
2844718787dSDavidlohr Bueso 	ipc_unlock_object(&msq->q_perm);
285ee51636cSSebastian Andrzej Siewior 	wake_up_q(&wake_q);
2864718787dSDavidlohr Bueso 	rcu_read_unlock();
2871da177e4SLinus Torvalds 
28841239fe8SNikola Pajkovsky 	list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) {
28972d1e611SJiebin Sun 		percpu_counter_sub_local(&ns->percpu_msg_hdrs, 1);
2901da177e4SLinus Torvalds 		free_msg(msg);
2911da177e4SLinus Torvalds 	}
29272d1e611SJiebin Sun 	percpu_counter_sub_local(&ns->percpu_msg_bytes, msq->q_cbytes);
29339a4940eSEric W. Biederman 	ipc_update_pid(&msq->q_lspid, NULL);
29439a4940eSEric W. Biederman 	ipc_update_pid(&msq->q_lrpid, NULL);
295dba4cdd3SManfred Spraul 	ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
ksys_msgget(key_t key,int msgflg)2983d65661aSDominik Brodowski long ksys_msgget(key_t key, int msgflg)
2991da177e4SLinus Torvalds {
3001e786937SKirill Korotaev 	struct ipc_namespace *ns;
301eb66ec44SMathias Krause 	static const struct ipc_ops msg_ops = {
302eb66ec44SMathias Krause 		.getnew = newque,
30350ab44b1SEric W. Biederman 		.associate = security_msg_queue_associate,
304eb66ec44SMathias Krause 	};
3057748dbfaSNadia Derbey 	struct ipc_params msg_params;
3061da177e4SLinus Torvalds 
3071e786937SKirill Korotaev 	ns = current->nsproxy->ipc_ns;
3081e786937SKirill Korotaev 
3097748dbfaSNadia Derbey 	msg_params.key = key;
3107748dbfaSNadia Derbey 	msg_params.flg = msgflg;
3117ca7e564SNadia Derbey 
3127748dbfaSNadia Derbey 	return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
SYSCALL_DEFINE2(msgget,key_t,key,int,msgflg)3153d65661aSDominik Brodowski SYSCALL_DEFINE2(msgget, key_t, key, int, msgflg)
3163d65661aSDominik Brodowski {
3173d65661aSDominik Brodowski 	return ksys_msgget(key, msgflg);
3183d65661aSDominik Brodowski }
3193d65661aSDominik Brodowski 
3205a06a363SIngo Molnar static inline unsigned long
copy_msqid_to_user(void __user * buf,struct msqid64_ds * in,int version)3215a06a363SIngo Molnar copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
3221da177e4SLinus Torvalds {
3231da177e4SLinus Torvalds 	switch (version) {
3241da177e4SLinus Torvalds 	case IPC_64:
3251da177e4SLinus Torvalds 		return copy_to_user(buf, in, sizeof(*in));
3261da177e4SLinus Torvalds 	case IPC_OLD:
3271da177e4SLinus Torvalds 	{
3281da177e4SLinus Torvalds 		struct msqid_ds out;
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 		memset(&out, 0, sizeof(out));
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 		ipc64_perm_to_ipc_perm(&in->msg_perm, &out.msg_perm);
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 		out.msg_stime		= in->msg_stime;
3351da177e4SLinus Torvalds 		out.msg_rtime		= in->msg_rtime;
3361da177e4SLinus Torvalds 		out.msg_ctime		= in->msg_ctime;
3371da177e4SLinus Torvalds 
3384be929beSAlexey Dobriyan 		if (in->msg_cbytes > USHRT_MAX)
3394be929beSAlexey Dobriyan 			out.msg_cbytes	= USHRT_MAX;
3401da177e4SLinus Torvalds 		else
3411da177e4SLinus Torvalds 			out.msg_cbytes	= in->msg_cbytes;
3421da177e4SLinus Torvalds 		out.msg_lcbytes		= in->msg_cbytes;
3431da177e4SLinus Torvalds 
3444be929beSAlexey Dobriyan 		if (in->msg_qnum > USHRT_MAX)
3454be929beSAlexey Dobriyan 			out.msg_qnum	= USHRT_MAX;
3461da177e4SLinus Torvalds 		else
3471da177e4SLinus Torvalds 			out.msg_qnum	= in->msg_qnum;
3481da177e4SLinus Torvalds 
3494be929beSAlexey Dobriyan 		if (in->msg_qbytes > USHRT_MAX)
3504be929beSAlexey Dobriyan 			out.msg_qbytes	= USHRT_MAX;
3511da177e4SLinus Torvalds 		else
3521da177e4SLinus Torvalds 			out.msg_qbytes	= in->msg_qbytes;
3531da177e4SLinus Torvalds 		out.msg_lqbytes		= in->msg_qbytes;
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds 		out.msg_lspid		= in->msg_lspid;
3561da177e4SLinus Torvalds 		out.msg_lrpid		= in->msg_lrpid;
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 		return copy_to_user(buf, &out, sizeof(out));
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 	default:
3611da177e4SLinus Torvalds 		return -EINVAL;
3621da177e4SLinus Torvalds 	}
3631da177e4SLinus Torvalds }
3641da177e4SLinus Torvalds 
3655a06a363SIngo Molnar static inline unsigned long
copy_msqid_from_user(struct msqid64_ds * out,void __user * buf,int version)366016d7132SPierre Peiffer copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version)
3671da177e4SLinus Torvalds {
3681da177e4SLinus Torvalds 	switch (version) {
3691da177e4SLinus Torvalds 	case IPC_64:
370016d7132SPierre Peiffer 		if (copy_from_user(out, buf, sizeof(*out)))
3711da177e4SLinus Torvalds 			return -EFAULT;
3721da177e4SLinus Torvalds 		return 0;
3731da177e4SLinus Torvalds 	case IPC_OLD:
3741da177e4SLinus Torvalds 	{
3751da177e4SLinus Torvalds 		struct msqid_ds tbuf_old;
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
3781da177e4SLinus Torvalds 			return -EFAULT;
3791da177e4SLinus Torvalds 
380016d7132SPierre Peiffer 		out->msg_perm.uid	= tbuf_old.msg_perm.uid;
381016d7132SPierre Peiffer 		out->msg_perm.gid	= tbuf_old.msg_perm.gid;
382016d7132SPierre Peiffer 		out->msg_perm.mode	= tbuf_old.msg_perm.mode;
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 		if (tbuf_old.msg_qbytes == 0)
385016d7132SPierre Peiffer 			out->msg_qbytes	= tbuf_old.msg_lqbytes;
3861da177e4SLinus Torvalds 		else
387016d7132SPierre Peiffer 			out->msg_qbytes	= tbuf_old.msg_qbytes;
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 		return 0;
3901da177e4SLinus Torvalds 	}
3911da177e4SLinus Torvalds 	default:
3921da177e4SLinus Torvalds 		return -EINVAL;
3931da177e4SLinus Torvalds 	}
3941da177e4SLinus Torvalds }
3951da177e4SLinus Torvalds 
396a0d092fcSPierre Peiffer /*
397d9a605e4SDavidlohr Bueso  * This function handles some msgctl commands which require the rwsem
398a0d092fcSPierre Peiffer  * to be held in write mode.
399d9a605e4SDavidlohr Bueso  * NOTE: no locks must be held, the rwsem is taken inside this function.
400a0d092fcSPierre Peiffer  */
msgctl_down(struct ipc_namespace * ns,int msqid,int cmd,struct ipc64_perm * perm,int msg_qbytes)401a0d092fcSPierre Peiffer static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
402889b3317SLu Shuaibing 			struct ipc64_perm *perm, int msg_qbytes)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds 	struct kern_ipc_perm *ipcp;
405a0d092fcSPierre Peiffer 	struct msg_queue *msq;
406a0d092fcSPierre Peiffer 	int err;
407a0d092fcSPierre Peiffer 
408d9a605e4SDavidlohr Bueso 	down_write(&msg_ids(ns).rwsem);
4097b4cc5d8SDavidlohr Bueso 	rcu_read_lock();
4107b4cc5d8SDavidlohr Bueso 
4114241c1a3SManfred Spraul 	ipcp = ipcctl_obtain_check(ns, &msg_ids(ns), msqid, cmd,
412889b3317SLu Shuaibing 				      perm, msg_qbytes);
4137b4cc5d8SDavidlohr Bueso 	if (IS_ERR(ipcp)) {
4147b4cc5d8SDavidlohr Bueso 		err = PTR_ERR(ipcp);
4157b4cc5d8SDavidlohr Bueso 		goto out_unlock1;
4167b4cc5d8SDavidlohr Bueso 	}
417a0d092fcSPierre Peiffer 
418a5f75e7fSPierre Peiffer 	msq = container_of(ipcp, struct msg_queue, q_perm);
419a0d092fcSPierre Peiffer 
420d8c6e854SEric W. Biederman 	err = security_msg_queue_msgctl(&msq->q_perm, cmd);
421a0d092fcSPierre Peiffer 	if (err)
42215724ecbSDavidlohr Bueso 		goto out_unlock1;
423a0d092fcSPierre Peiffer 
424a0d092fcSPierre Peiffer 	switch (cmd) {
425a0d092fcSPierre Peiffer 	case IPC_RMID:
42615724ecbSDavidlohr Bueso 		ipc_lock_object(&msq->q_perm);
4277b4cc5d8SDavidlohr Bueso 		/* freeque unlocks the ipc object and rcu */
428a0d092fcSPierre Peiffer 		freeque(ns, ipcp);
429a0d092fcSPierre Peiffer 		goto out_up;
430a0d092fcSPierre Peiffer 	case IPC_SET:
431e3658538SDavidlohr Bueso 	{
432194a6b5bSWaiman Long 		DEFINE_WAKE_Q(wake_q);
433e3658538SDavidlohr Bueso 
434889b3317SLu Shuaibing 		if (msg_qbytes > ns->msg_ctlmnb &&
435a0d092fcSPierre Peiffer 		    !capable(CAP_SYS_RESOURCE)) {
436a0d092fcSPierre Peiffer 			err = -EPERM;
43715724ecbSDavidlohr Bueso 			goto out_unlock1;
438a0d092fcSPierre Peiffer 		}
439a0d092fcSPierre Peiffer 
44015724ecbSDavidlohr Bueso 		ipc_lock_object(&msq->q_perm);
441889b3317SLu Shuaibing 		err = ipc_update_perm(perm, ipcp);
4421efdb69bSEric W. Biederman 		if (err)
4437b4cc5d8SDavidlohr Bueso 			goto out_unlock0;
4441efdb69bSEric W. Biederman 
445889b3317SLu Shuaibing 		msq->q_qbytes = msg_qbytes;
446a0d092fcSPierre Peiffer 
44750578ea9SDeepa Dinamani 		msq->q_ctime = ktime_get_real_seconds();
448e3658538SDavidlohr Bueso 		/*
449e3658538SDavidlohr Bueso 		 * Sleeping receivers might be excluded by
450a0d092fcSPierre Peiffer 		 * stricter permissions.
451a0d092fcSPierre Peiffer 		 */
452ee51636cSSebastian Andrzej Siewior 		expunge_all(msq, -EAGAIN, &wake_q);
453e3658538SDavidlohr Bueso 		/*
454e3658538SDavidlohr Bueso 		 * Sleeping senders might be able to send
455a0d092fcSPierre Peiffer 		 * due to a larger queue size.
456a0d092fcSPierre Peiffer 		 */
457ed27f912SDavidlohr Bueso 		ss_wakeup(msq, &wake_q, false);
458e3658538SDavidlohr Bueso 		ipc_unlock_object(&msq->q_perm);
459e3658538SDavidlohr Bueso 		wake_up_q(&wake_q);
460e3658538SDavidlohr Bueso 
461e3658538SDavidlohr Bueso 		goto out_unlock1;
462e3658538SDavidlohr Bueso 	}
463a0d092fcSPierre Peiffer 	default:
464a0d092fcSPierre Peiffer 		err = -EINVAL;
46515724ecbSDavidlohr Bueso 		goto out_unlock1;
466a0d092fcSPierre Peiffer 	}
4677b4cc5d8SDavidlohr Bueso 
4687b4cc5d8SDavidlohr Bueso out_unlock0:
4697b4cc5d8SDavidlohr Bueso 	ipc_unlock_object(&msq->q_perm);
4707b4cc5d8SDavidlohr Bueso out_unlock1:
4717b4cc5d8SDavidlohr Bueso 	rcu_read_unlock();
472a0d092fcSPierre Peiffer out_up:
473d9a605e4SDavidlohr Bueso 	up_write(&msg_ids(ns).rwsem);
474a0d092fcSPierre Peiffer 	return err;
475a0d092fcSPierre Peiffer }
476a0d092fcSPierre Peiffer 
msgctl_info(struct ipc_namespace * ns,int msqid,int cmd,struct msginfo * msginfo)477156d9ed1SAl Viro static int msgctl_info(struct ipc_namespace *ns, int msqid,
478156d9ed1SAl Viro 			 int cmd, struct msginfo *msginfo)
479a0d092fcSPierre Peiffer {
4802cafed30SDavidlohr Bueso 	int err;
48127c331a1SManfred Spraul 	int max_idx;
4825a06a363SIngo Molnar 
4835a06a363SIngo Molnar 	/*
4845a06a363SIngo Molnar 	 * We must not return kernel stack data.
4851da177e4SLinus Torvalds 	 * due to padding, it's not enough
4861da177e4SLinus Torvalds 	 * to set all member fields.
4871da177e4SLinus Torvalds 	 */
4881da177e4SLinus Torvalds 	err = security_msg_queue_msgctl(NULL, cmd);
4891da177e4SLinus Torvalds 	if (err)
4901da177e4SLinus Torvalds 		return err;
4911da177e4SLinus Torvalds 
492156d9ed1SAl Viro 	memset(msginfo, 0, sizeof(*msginfo));
493156d9ed1SAl Viro 	msginfo->msgmni = ns->msg_ctlmni;
494156d9ed1SAl Viro 	msginfo->msgmax = ns->msg_ctlmax;
495156d9ed1SAl Viro 	msginfo->msgmnb = ns->msg_ctlmnb;
496156d9ed1SAl Viro 	msginfo->msgssz = MSGSSZ;
497156d9ed1SAl Viro 	msginfo->msgseg = MSGSEG;
498d9a605e4SDavidlohr Bueso 	down_read(&msg_ids(ns).rwsem);
49972d1e611SJiebin Sun 	if (cmd == MSG_INFO)
500156d9ed1SAl Viro 		msginfo->msgpool = msg_ids(ns).in_use;
50172d1e611SJiebin Sun 	max_idx = ipc_get_maxidx(&msg_ids(ns));
50272d1e611SJiebin Sun 	up_read(&msg_ids(ns).rwsem);
50372d1e611SJiebin Sun 	if (cmd == MSG_INFO) {
50472d1e611SJiebin Sun 		msginfo->msgmap = min_t(int,
50572d1e611SJiebin Sun 				     percpu_counter_sum(&ns->percpu_msg_hdrs),
50672d1e611SJiebin Sun 				     INT_MAX);
50772d1e611SJiebin Sun 		msginfo->msgtql = min_t(int,
50872d1e611SJiebin Sun 		                     percpu_counter_sum(&ns->percpu_msg_bytes),
50972d1e611SJiebin Sun 				     INT_MAX);
5101da177e4SLinus Torvalds 	} else {
511156d9ed1SAl Viro 		msginfo->msgmap = MSGMAP;
512156d9ed1SAl Viro 		msginfo->msgpool = MSGPOOL;
513156d9ed1SAl Viro 		msginfo->msgtql = MSGTQL;
5141da177e4SLinus Torvalds 	}
51527c331a1SManfred Spraul 	return (max_idx < 0) ? 0 : max_idx;
5161da177e4SLinus Torvalds }
5172cafed30SDavidlohr Bueso 
msgctl_stat(struct ipc_namespace * ns,int msqid,int cmd,struct msqid64_ds * p)518156d9ed1SAl Viro static int msgctl_stat(struct ipc_namespace *ns, int msqid,
519156d9ed1SAl Viro 			 int cmd, struct msqid64_ds *p)
5201da177e4SLinus Torvalds {
521156d9ed1SAl Viro 	struct msg_queue *msq;
52287ad4b0dSPhilippe Mikoyan 	int err;
5235a06a363SIngo Molnar 
524156d9ed1SAl Viro 	memset(p, 0, sizeof(*p));
525ac0ba20eSDavidlohr Bueso 
526ac0ba20eSDavidlohr Bueso 	rcu_read_lock();
52723c8cec8SDavidlohr Bueso 	if (cmd == MSG_STAT || cmd == MSG_STAT_ANY) {
528ac0ba20eSDavidlohr Bueso 		msq = msq_obtain_object(ns, msqid);
529ac0ba20eSDavidlohr Bueso 		if (IS_ERR(msq)) {
530ac0ba20eSDavidlohr Bueso 			err = PTR_ERR(msq);
531ac0ba20eSDavidlohr Bueso 			goto out_unlock;
532ac0ba20eSDavidlohr Bueso 		}
53323c8cec8SDavidlohr Bueso 	} else { /* IPC_STAT */
534ac0ba20eSDavidlohr Bueso 		msq = msq_obtain_object_check(ns, msqid);
535ac0ba20eSDavidlohr Bueso 		if (IS_ERR(msq)) {
536ac0ba20eSDavidlohr Bueso 			err = PTR_ERR(msq);
537ac0ba20eSDavidlohr Bueso 			goto out_unlock;
538ac0ba20eSDavidlohr Bueso 		}
5391da177e4SLinus Torvalds 	}
540ac0ba20eSDavidlohr Bueso 
54123c8cec8SDavidlohr Bueso 	/* see comment for SHM_STAT_ANY */
54223c8cec8SDavidlohr Bueso 	if (cmd == MSG_STAT_ANY)
54323c8cec8SDavidlohr Bueso 		audit_ipc_obj(&msq->q_perm);
54423c8cec8SDavidlohr Bueso 	else {
5451da177e4SLinus Torvalds 		err = -EACCES;
546b0e77598SSerge E. Hallyn 		if (ipcperms(ns, &msq->q_perm, S_IRUGO))
5471da177e4SLinus Torvalds 			goto out_unlock;
54823c8cec8SDavidlohr Bueso 	}
5491da177e4SLinus Torvalds 
550d8c6e854SEric W. Biederman 	err = security_msg_queue_msgctl(&msq->q_perm, cmd);
5511da177e4SLinus Torvalds 	if (err)
5521da177e4SLinus Torvalds 		goto out_unlock;
5531da177e4SLinus Torvalds 
55487ad4b0dSPhilippe Mikoyan 	ipc_lock_object(&msq->q_perm);
55587ad4b0dSPhilippe Mikoyan 
55687ad4b0dSPhilippe Mikoyan 	if (!ipc_valid_object(&msq->q_perm)) {
55787ad4b0dSPhilippe Mikoyan 		ipc_unlock_object(&msq->q_perm);
55887ad4b0dSPhilippe Mikoyan 		err = -EIDRM;
55987ad4b0dSPhilippe Mikoyan 		goto out_unlock;
56087ad4b0dSPhilippe Mikoyan 	}
56187ad4b0dSPhilippe Mikoyan 
562156d9ed1SAl Viro 	kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm);
563156d9ed1SAl Viro 	p->msg_stime  = msq->q_stime;
564156d9ed1SAl Viro 	p->msg_rtime  = msq->q_rtime;
565156d9ed1SAl Viro 	p->msg_ctime  = msq->q_ctime;
566c2ab975cSArnd Bergmann #ifndef CONFIG_64BIT
567c2ab975cSArnd Bergmann 	p->msg_stime_high = msq->q_stime >> 32;
568c2ab975cSArnd Bergmann 	p->msg_rtime_high = msq->q_rtime >> 32;
569c2ab975cSArnd Bergmann 	p->msg_ctime_high = msq->q_ctime >> 32;
570c2ab975cSArnd Bergmann #endif
571156d9ed1SAl Viro 	p->msg_cbytes = msq->q_cbytes;
572156d9ed1SAl Viro 	p->msg_qnum   = msq->q_qnum;
573156d9ed1SAl Viro 	p->msg_qbytes = msq->q_qbytes;
57439a4940eSEric W. Biederman 	p->msg_lspid  = pid_vnr(msq->q_lspid);
57539a4940eSEric W. Biederman 	p->msg_lrpid  = pid_vnr(msq->q_lrpid);
576ac0ba20eSDavidlohr Bueso 
577615c999cSManfred Spraul 	if (cmd == IPC_STAT) {
578615c999cSManfred Spraul 		/*
579615c999cSManfred Spraul 		 * As defined in SUS:
580615c999cSManfred Spraul 		 * Return 0 on success
581615c999cSManfred Spraul 		 */
582615c999cSManfred Spraul 		err = 0;
583615c999cSManfred Spraul 	} else {
584615c999cSManfred Spraul 		/*
585615c999cSManfred Spraul 		 * MSG_STAT and MSG_STAT_ANY (both Linux specific)
586615c999cSManfred Spraul 		 * Return the full id, including the sequence number
587615c999cSManfred Spraul 		 */
588615c999cSManfred Spraul 		err = msq->q_perm.id;
589615c999cSManfred Spraul 	}
5902cafed30SDavidlohr Bueso 
591615c999cSManfred Spraul 	ipc_unlock_object(&msq->q_perm);
5921da177e4SLinus Torvalds out_unlock:
593ac0ba20eSDavidlohr Bueso 	rcu_read_unlock();
5941da177e4SLinus Torvalds 	return err;
5951da177e4SLinus Torvalds }
5961da177e4SLinus Torvalds 
ksys_msgctl(int msqid,int cmd,struct msqid_ds __user * buf,int version)597275f2214SArnd Bergmann static long ksys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf, int version)
5982cafed30SDavidlohr Bueso {
5992cafed30SDavidlohr Bueso 	struct ipc_namespace *ns;
600156d9ed1SAl Viro 	struct msqid64_ds msqid64;
601156d9ed1SAl Viro 	int err;
6022cafed30SDavidlohr Bueso 
6032cafed30SDavidlohr Bueso 	if (msqid < 0 || cmd < 0)
6042cafed30SDavidlohr Bueso 		return -EINVAL;
6052cafed30SDavidlohr Bueso 
6062cafed30SDavidlohr Bueso 	ns = current->nsproxy->ipc_ns;
6072cafed30SDavidlohr Bueso 
6082cafed30SDavidlohr Bueso 	switch (cmd) {
6092cafed30SDavidlohr Bueso 	case IPC_INFO:
610156d9ed1SAl Viro 	case MSG_INFO: {
611156d9ed1SAl Viro 		struct msginfo msginfo;
612156d9ed1SAl Viro 		err = msgctl_info(ns, msqid, cmd, &msginfo);
613156d9ed1SAl Viro 		if (err < 0)
614156d9ed1SAl Viro 			return err;
615156d9ed1SAl Viro 		if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
616156d9ed1SAl Viro 			err = -EFAULT;
617156d9ed1SAl Viro 		return err;
618156d9ed1SAl Viro 	}
6192cafed30SDavidlohr Bueso 	case MSG_STAT:	/* msqid is an index rather than a msg queue id */
62023c8cec8SDavidlohr Bueso 	case MSG_STAT_ANY:
6212cafed30SDavidlohr Bueso 	case IPC_STAT:
622156d9ed1SAl Viro 		err = msgctl_stat(ns, msqid, cmd, &msqid64);
623156d9ed1SAl Viro 		if (err < 0)
624156d9ed1SAl Viro 			return err;
625156d9ed1SAl Viro 		if (copy_msqid_to_user(buf, &msqid64, version))
626156d9ed1SAl Viro 			err = -EFAULT;
627156d9ed1SAl Viro 		return err;
6282cafed30SDavidlohr Bueso 	case IPC_SET:
629156d9ed1SAl Viro 		if (copy_msqid_from_user(&msqid64, buf, version))
630156d9ed1SAl Viro 			return -EFAULT;
631889b3317SLu Shuaibing 		return msgctl_down(ns, msqid, cmd, &msqid64.msg_perm,
632889b3317SLu Shuaibing 				   msqid64.msg_qbytes);
6332cafed30SDavidlohr Bueso 	case IPC_RMID:
634889b3317SLu Shuaibing 		return msgctl_down(ns, msqid, cmd, NULL, 0);
6352cafed30SDavidlohr Bueso 	default:
6362cafed30SDavidlohr Bueso 		return  -EINVAL;
6372cafed30SDavidlohr Bueso 	}
6382cafed30SDavidlohr Bueso }
6392cafed30SDavidlohr Bueso 
SYSCALL_DEFINE3(msgctl,int,msqid,int,cmd,struct msqid_ds __user *,buf)640e340db56SDominik Brodowski SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
641e340db56SDominik Brodowski {
642275f2214SArnd Bergmann 	return ksys_msgctl(msqid, cmd, buf, IPC_64);
643e340db56SDominik Brodowski }
644e340db56SDominik Brodowski 
645275f2214SArnd Bergmann #ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION
ksys_old_msgctl(int msqid,int cmd,struct msqid_ds __user * buf)646275f2214SArnd Bergmann long ksys_old_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
647275f2214SArnd Bergmann {
648275f2214SArnd Bergmann 	int version = ipc_parse_version(&cmd);
649275f2214SArnd Bergmann 
650275f2214SArnd Bergmann 	return ksys_msgctl(msqid, cmd, buf, version);
651275f2214SArnd Bergmann }
652275f2214SArnd Bergmann 
SYSCALL_DEFINE3(old_msgctl,int,msqid,int,cmd,struct msqid_ds __user *,buf)653275f2214SArnd Bergmann SYSCALL_DEFINE3(old_msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
654275f2214SArnd Bergmann {
655275f2214SArnd Bergmann 	return ksys_old_msgctl(msqid, cmd, buf);
656275f2214SArnd Bergmann }
657275f2214SArnd Bergmann #endif
658275f2214SArnd Bergmann 
65946939168SAl Viro #ifdef CONFIG_COMPAT
66046939168SAl Viro 
66146939168SAl Viro struct compat_msqid_ds {
66246939168SAl Viro 	struct compat_ipc_perm msg_perm;
66346939168SAl Viro 	compat_uptr_t msg_first;
66446939168SAl Viro 	compat_uptr_t msg_last;
6659afc5eeeSArnd Bergmann 	old_time32_t msg_stime;
6669afc5eeeSArnd Bergmann 	old_time32_t msg_rtime;
6679afc5eeeSArnd Bergmann 	old_time32_t msg_ctime;
66846939168SAl Viro 	compat_ulong_t msg_lcbytes;
66946939168SAl Viro 	compat_ulong_t msg_lqbytes;
67046939168SAl Viro 	unsigned short msg_cbytes;
67146939168SAl Viro 	unsigned short msg_qnum;
67246939168SAl Viro 	unsigned short msg_qbytes;
67346939168SAl Viro 	compat_ipc_pid_t msg_lspid;
67446939168SAl Viro 	compat_ipc_pid_t msg_lrpid;
67546939168SAl Viro };
67646939168SAl Viro 
copy_compat_msqid_from_user(struct msqid64_ds * out,void __user * buf,int version)67746939168SAl Viro static int copy_compat_msqid_from_user(struct msqid64_ds *out, void __user *buf,
67846939168SAl Viro 					int version)
67946939168SAl Viro {
68046939168SAl Viro 	memset(out, 0, sizeof(*out));
68146939168SAl Viro 	if (version == IPC_64) {
6826aa211e8SLinus Torvalds 		struct compat_msqid64_ds __user *p = buf;
68328327faeSAl Viro 		if (get_compat_ipc64_perm(&out->msg_perm, &p->msg_perm))
68446939168SAl Viro 			return -EFAULT;
68546939168SAl Viro 		if (get_user(out->msg_qbytes, &p->msg_qbytes))
68646939168SAl Viro 			return -EFAULT;
68746939168SAl Viro 	} else {
6886aa211e8SLinus Torvalds 		struct compat_msqid_ds __user *p = buf;
68928327faeSAl Viro 		if (get_compat_ipc_perm(&out->msg_perm, &p->msg_perm))
69046939168SAl Viro 			return -EFAULT;
69146939168SAl Viro 		if (get_user(out->msg_qbytes, &p->msg_qbytes))
69246939168SAl Viro 			return -EFAULT;
69346939168SAl Viro 	}
69446939168SAl Viro 	return 0;
69546939168SAl Viro }
69646939168SAl Viro 
copy_compat_msqid_to_user(void __user * buf,struct msqid64_ds * in,int version)69746939168SAl Viro static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in,
69846939168SAl Viro 					int version)
69946939168SAl Viro {
70046939168SAl Viro 	if (version == IPC_64) {
70146939168SAl Viro 		struct compat_msqid64_ds v;
70246939168SAl Viro 		memset(&v, 0, sizeof(v));
70328327faeSAl Viro 		to_compat_ipc64_perm(&v.msg_perm, &in->msg_perm);
704c2ab975cSArnd Bergmann 		v.msg_stime	 = lower_32_bits(in->msg_stime);
705c2ab975cSArnd Bergmann 		v.msg_stime_high = upper_32_bits(in->msg_stime);
706c2ab975cSArnd Bergmann 		v.msg_rtime	 = lower_32_bits(in->msg_rtime);
707c2ab975cSArnd Bergmann 		v.msg_rtime_high = upper_32_bits(in->msg_rtime);
708c2ab975cSArnd Bergmann 		v.msg_ctime	 = lower_32_bits(in->msg_ctime);
709c2ab975cSArnd Bergmann 		v.msg_ctime_high = upper_32_bits(in->msg_ctime);
71046939168SAl Viro 		v.msg_cbytes = in->msg_cbytes;
71146939168SAl Viro 		v.msg_qnum = in->msg_qnum;
71246939168SAl Viro 		v.msg_qbytes = in->msg_qbytes;
71346939168SAl Viro 		v.msg_lspid = in->msg_lspid;
71446939168SAl Viro 		v.msg_lrpid = in->msg_lrpid;
71546939168SAl Viro 		return copy_to_user(buf, &v, sizeof(v));
71646939168SAl Viro 	} else {
71746939168SAl Viro 		struct compat_msqid_ds v;
71846939168SAl Viro 		memset(&v, 0, sizeof(v));
71928327faeSAl Viro 		to_compat_ipc_perm(&v.msg_perm, &in->msg_perm);
72046939168SAl Viro 		v.msg_stime = in->msg_stime;
72146939168SAl Viro 		v.msg_rtime = in->msg_rtime;
72246939168SAl Viro 		v.msg_ctime = in->msg_ctime;
72346939168SAl Viro 		v.msg_cbytes = in->msg_cbytes;
72446939168SAl Viro 		v.msg_qnum = in->msg_qnum;
72546939168SAl Viro 		v.msg_qbytes = in->msg_qbytes;
72646939168SAl Viro 		v.msg_lspid = in->msg_lspid;
72746939168SAl Viro 		v.msg_lrpid = in->msg_lrpid;
72846939168SAl Viro 		return copy_to_user(buf, &v, sizeof(v));
72946939168SAl Viro 	}
73046939168SAl Viro }
73146939168SAl Viro 
compat_ksys_msgctl(int msqid,int cmd,void __user * uptr,int version)732275f2214SArnd Bergmann static long compat_ksys_msgctl(int msqid, int cmd, void __user *uptr, int version)
73346939168SAl Viro {
73446939168SAl Viro 	struct ipc_namespace *ns;
73546939168SAl Viro 	int err;
73646939168SAl Viro 	struct msqid64_ds msqid64;
73746939168SAl Viro 
73846939168SAl Viro 	ns = current->nsproxy->ipc_ns;
73946939168SAl Viro 
74046939168SAl Viro 	if (msqid < 0 || cmd < 0)
74146939168SAl Viro 		return -EINVAL;
74246939168SAl Viro 
74346939168SAl Viro 	switch (cmd & (~IPC_64)) {
74446939168SAl Viro 	case IPC_INFO:
74546939168SAl Viro 	case MSG_INFO: {
74646939168SAl Viro 		struct msginfo msginfo;
74746939168SAl Viro 		err = msgctl_info(ns, msqid, cmd, &msginfo);
74846939168SAl Viro 		if (err < 0)
74946939168SAl Viro 			return err;
75046939168SAl Viro 		if (copy_to_user(uptr, &msginfo, sizeof(struct msginfo)))
75146939168SAl Viro 			err = -EFAULT;
75246939168SAl Viro 		return err;
75346939168SAl Viro 	}
75446939168SAl Viro 	case IPC_STAT:
75546939168SAl Viro 	case MSG_STAT:
75623c8cec8SDavidlohr Bueso 	case MSG_STAT_ANY:
75746939168SAl Viro 		err = msgctl_stat(ns, msqid, cmd, &msqid64);
75846939168SAl Viro 		if (err < 0)
75946939168SAl Viro 			return err;
76046939168SAl Viro 		if (copy_compat_msqid_to_user(uptr, &msqid64, version))
76146939168SAl Viro 			err = -EFAULT;
76246939168SAl Viro 		return err;
76346939168SAl Viro 	case IPC_SET:
76446939168SAl Viro 		if (copy_compat_msqid_from_user(&msqid64, uptr, version))
76546939168SAl Viro 			return -EFAULT;
766889b3317SLu Shuaibing 		return msgctl_down(ns, msqid, cmd, &msqid64.msg_perm, msqid64.msg_qbytes);
76746939168SAl Viro 	case IPC_RMID:
768889b3317SLu Shuaibing 		return msgctl_down(ns, msqid, cmd, NULL, 0);
76946939168SAl Viro 	default:
77046939168SAl Viro 		return -EINVAL;
77146939168SAl Viro 	}
77246939168SAl Viro }
773e340db56SDominik Brodowski 
COMPAT_SYSCALL_DEFINE3(msgctl,int,msqid,int,cmd,void __user *,uptr)774e340db56SDominik Brodowski COMPAT_SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, void __user *, uptr)
775e340db56SDominik Brodowski {
776275f2214SArnd Bergmann 	return compat_ksys_msgctl(msqid, cmd, uptr, IPC_64);
777e340db56SDominik Brodowski }
778275f2214SArnd Bergmann 
779275f2214SArnd Bergmann #ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION
compat_ksys_old_msgctl(int msqid,int cmd,void __user * uptr)780275f2214SArnd Bergmann long compat_ksys_old_msgctl(int msqid, int cmd, void __user *uptr)
781275f2214SArnd Bergmann {
782275f2214SArnd Bergmann 	int version = compat_ipc_parse_version(&cmd);
783275f2214SArnd Bergmann 
784275f2214SArnd Bergmann 	return compat_ksys_msgctl(msqid, cmd, uptr, version);
785275f2214SArnd Bergmann }
786275f2214SArnd Bergmann 
COMPAT_SYSCALL_DEFINE3(old_msgctl,int,msqid,int,cmd,void __user *,uptr)787275f2214SArnd Bergmann COMPAT_SYSCALL_DEFINE3(old_msgctl, int, msqid, int, cmd, void __user *, uptr)
788275f2214SArnd Bergmann {
789275f2214SArnd Bergmann 	return compat_ksys_old_msgctl(msqid, cmd, uptr);
790275f2214SArnd Bergmann }
791275f2214SArnd Bergmann #endif
79246939168SAl Viro #endif
79346939168SAl Viro 
testmsg(struct msg_msg * msg,long type,int mode)7941da177e4SLinus Torvalds static int testmsg(struct msg_msg *msg, long type, int mode)
7951da177e4SLinus Torvalds {
79646c0a8caSPaul McQuade 	switch (mode) {
7971da177e4SLinus Torvalds 	case SEARCH_ANY:
7988ac6ed58SPeter Hurley 	case SEARCH_NUMBER:
7991da177e4SLinus Torvalds 		return 1;
8001da177e4SLinus Torvalds 	case SEARCH_LESSEQUAL:
8011da177e4SLinus Torvalds 		if (msg->m_type <= type)
8021da177e4SLinus Torvalds 			return 1;
8031da177e4SLinus Torvalds 		break;
8041da177e4SLinus Torvalds 	case SEARCH_EQUAL:
8051da177e4SLinus Torvalds 		if (msg->m_type == type)
8061da177e4SLinus Torvalds 			return 1;
8071da177e4SLinus Torvalds 		break;
8081da177e4SLinus Torvalds 	case SEARCH_NOTEQUAL:
8091da177e4SLinus Torvalds 		if (msg->m_type != type)
8101da177e4SLinus Torvalds 			return 1;
8111da177e4SLinus Torvalds 		break;
8121da177e4SLinus Torvalds 	}
8131da177e4SLinus Torvalds 	return 0;
8141da177e4SLinus Torvalds }
8151da177e4SLinus Torvalds 
pipelined_send(struct msg_queue * msq,struct msg_msg * msg,struct wake_q_head * wake_q)816ee51636cSSebastian Andrzej Siewior static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg,
817ee51636cSSebastian Andrzej Siewior 				 struct wake_q_head *wake_q)
8181da177e4SLinus Torvalds {
81941239fe8SNikola Pajkovsky 	struct msg_receiver *msr, *t;
8201da177e4SLinus Torvalds 
82141239fe8SNikola Pajkovsky 	list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
8221da177e4SLinus Torvalds 		if (testmsg(msg, msr->r_msgtype, msr->r_mode) &&
823d8c6e854SEric W. Biederman 		    !security_msg_queue_msgrcv(&msq->q_perm, msg, msr->r_tsk,
8245a06a363SIngo Molnar 					       msr->r_msgtype, msr->r_mode)) {
8255a06a363SIngo Molnar 
8261da177e4SLinus Torvalds 			list_del(&msr->r_list);
8271da177e4SLinus Torvalds 			if (msr->r_maxsize < msg->m_ts) {
828ee51636cSSebastian Andrzej Siewior 				wake_q_add(wake_q, msr->r_tsk);
8290d97a82bSManfred Spraul 
8300d97a82bSManfred Spraul 				/* See expunge_all regarding memory barrier */
8310d97a82bSManfred Spraul 				smp_store_release(&msr->r_msg, ERR_PTR(-E2BIG));
8321da177e4SLinus Torvalds 			} else {
83339a4940eSEric W. Biederman 				ipc_update_pid(&msq->q_lrpid, task_pid(msr->r_tsk));
8342a70b787SArnd Bergmann 				msq->q_rtime = ktime_get_real_seconds();
8355a06a363SIngo Molnar 
836ee51636cSSebastian Andrzej Siewior 				wake_q_add(wake_q, msr->r_tsk);
8370d97a82bSManfred Spraul 
8380d97a82bSManfred Spraul 				/* See expunge_all regarding memory barrier */
8390d97a82bSManfred Spraul 				smp_store_release(&msr->r_msg, msg);
8401da177e4SLinus Torvalds 				return 1;
8411da177e4SLinus Torvalds 			}
8421da177e4SLinus Torvalds 		}
8431da177e4SLinus Torvalds 	}
844ffa571daSDavidlohr Bueso 
8451da177e4SLinus Torvalds 	return 0;
8461da177e4SLinus Torvalds }
8471da177e4SLinus Torvalds 
do_msgsnd(int msqid,long mtype,void __user * mtext,size_t msgsz,int msgflg)8489b1404c2SAl Viro static long do_msgsnd(int msqid, long mtype, void __user *mtext,
849651971cbSsuzuki 		size_t msgsz, int msgflg)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	struct msg_queue *msq;
8521da177e4SLinus Torvalds 	struct msg_msg *msg;
8531da177e4SLinus Torvalds 	int err;
8541e786937SKirill Korotaev 	struct ipc_namespace *ns;
855194a6b5bSWaiman Long 	DEFINE_WAKE_Q(wake_q);
8561da177e4SLinus Torvalds 
8571e786937SKirill Korotaev 	ns = current->nsproxy->ipc_ns;
8581e786937SKirill Korotaev 
8591e786937SKirill Korotaev 	if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)
8601da177e4SLinus Torvalds 		return -EINVAL;
8611da177e4SLinus Torvalds 	if (mtype < 1)
8621da177e4SLinus Torvalds 		return -EINVAL;
8631da177e4SLinus Torvalds 
864651971cbSsuzuki 	msg = load_msg(mtext, msgsz);
8651da177e4SLinus Torvalds 	if (IS_ERR(msg))
8661da177e4SLinus Torvalds 		return PTR_ERR(msg);
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds 	msg->m_type = mtype;
8691da177e4SLinus Torvalds 	msg->m_ts = msgsz;
8701da177e4SLinus Torvalds 
8713dd1f784SDavidlohr Bueso 	rcu_read_lock();
8723dd1f784SDavidlohr Bueso 	msq = msq_obtain_object_check(ns, msqid);
873023a5355SNadia Derbey 	if (IS_ERR(msq)) {
874023a5355SNadia Derbey 		err = PTR_ERR(msq);
8753dd1f784SDavidlohr Bueso 		goto out_unlock1;
876023a5355SNadia Derbey 	}
8771da177e4SLinus Torvalds 
878bebcb928SManfred Spraul 	ipc_lock_object(&msq->q_perm);
879bebcb928SManfred Spraul 
8801da177e4SLinus Torvalds 	for (;;) {
8811da177e4SLinus Torvalds 		struct msg_sender s;
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 		err = -EACCES;
884b0e77598SSerge E. Hallyn 		if (ipcperms(ns, &msq->q_perm, S_IWUGO))
885bebcb928SManfred Spraul 			goto out_unlock0;
8861da177e4SLinus Torvalds 
8874271b05aSDavidlohr Bueso 		/* raced with RMID? */
8880f3d2b01SRafael Aquini 		if (!ipc_valid_object(&msq->q_perm)) {
8894271b05aSDavidlohr Bueso 			err = -EIDRM;
8904271b05aSDavidlohr Bueso 			goto out_unlock0;
8914271b05aSDavidlohr Bueso 		}
8924271b05aSDavidlohr Bueso 
893d8c6e854SEric W. Biederman 		err = security_msg_queue_msgsnd(&msq->q_perm, msg, msgflg);
8941da177e4SLinus Torvalds 		if (err)
895bebcb928SManfred Spraul 			goto out_unlock0;
8961da177e4SLinus Torvalds 
897ed27f912SDavidlohr Bueso 		if (msg_fits_inqueue(msq, msgsz))
8981da177e4SLinus Torvalds 			break;
8991da177e4SLinus Torvalds 
9001da177e4SLinus Torvalds 		/* queue full, wait: */
9011da177e4SLinus Torvalds 		if (msgflg & IPC_NOWAIT) {
9021da177e4SLinus Torvalds 			err = -EAGAIN;
903bebcb928SManfred Spraul 			goto out_unlock0;
9041da177e4SLinus Torvalds 		}
9053dd1f784SDavidlohr Bueso 
906ffa571daSDavidlohr Bueso 		/* enqueue the sender and prepare to block */
907ed27f912SDavidlohr Bueso 		ss_add(msq, &s, msgsz);
9086062a8dcSRik van Riel 
909dba4cdd3SManfred Spraul 		if (!ipc_rcu_getref(&msq->q_perm)) {
9106062a8dcSRik van Riel 			err = -EIDRM;
9113dd1f784SDavidlohr Bueso 			goto out_unlock0;
9126062a8dcSRik van Riel 		}
9136062a8dcSRik van Riel 
9143dd1f784SDavidlohr Bueso 		ipc_unlock_object(&msq->q_perm);
9153dd1f784SDavidlohr Bueso 		rcu_read_unlock();
9161da177e4SLinus Torvalds 		schedule();
9171da177e4SLinus Torvalds 
9183dd1f784SDavidlohr Bueso 		rcu_read_lock();
9193dd1f784SDavidlohr Bueso 		ipc_lock_object(&msq->q_perm);
9203dd1f784SDavidlohr Bueso 
921dba4cdd3SManfred Spraul 		ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
9220f3d2b01SRafael Aquini 		/* raced with RMID? */
9230f3d2b01SRafael Aquini 		if (!ipc_valid_object(&msq->q_perm)) {
9241da177e4SLinus Torvalds 			err = -EIDRM;
9253dd1f784SDavidlohr Bueso 			goto out_unlock0;
9261da177e4SLinus Torvalds 		}
9271da177e4SLinus Torvalds 		ss_del(&s);
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 		if (signal_pending(current)) {
9301da177e4SLinus Torvalds 			err = -ERESTARTNOHAND;
9313dd1f784SDavidlohr Bueso 			goto out_unlock0;
9321da177e4SLinus Torvalds 		}
9331da177e4SLinus Torvalds 
9343dd1f784SDavidlohr Bueso 	}
935ed27f912SDavidlohr Bueso 
93639a4940eSEric W. Biederman 	ipc_update_pid(&msq->q_lspid, task_tgid(current));
9372a70b787SArnd Bergmann 	msq->q_stime = ktime_get_real_seconds();
9381da177e4SLinus Torvalds 
939ee51636cSSebastian Andrzej Siewior 	if (!pipelined_send(msq, msg, &wake_q)) {
9401da177e4SLinus Torvalds 		/* no one is waiting for this message, enqueue it */
9411da177e4SLinus Torvalds 		list_add_tail(&msg->m_list, &msq->q_messages);
9421da177e4SLinus Torvalds 		msq->q_cbytes += msgsz;
9431da177e4SLinus Torvalds 		msq->q_qnum++;
94472d1e611SJiebin Sun 		percpu_counter_add_local(&ns->percpu_msg_bytes, msgsz);
94572d1e611SJiebin Sun 		percpu_counter_add_local(&ns->percpu_msg_hdrs, 1);
9461da177e4SLinus Torvalds 	}
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds 	err = 0;
9491da177e4SLinus Torvalds 	msg = NULL;
9501da177e4SLinus Torvalds 
9513dd1f784SDavidlohr Bueso out_unlock0:
9523dd1f784SDavidlohr Bueso 	ipc_unlock_object(&msq->q_perm);
953ee51636cSSebastian Andrzej Siewior 	wake_up_q(&wake_q);
9543dd1f784SDavidlohr Bueso out_unlock1:
9553dd1f784SDavidlohr Bueso 	rcu_read_unlock();
9561da177e4SLinus Torvalds 	if (msg != NULL)
9571da177e4SLinus Torvalds 		free_msg(msg);
9581da177e4SLinus Torvalds 	return err;
9591da177e4SLinus Torvalds }
9601da177e4SLinus Torvalds 
ksys_msgsnd(int msqid,struct msgbuf __user * msgp,size_t msgsz,int msgflg)96131c213f2SDominik Brodowski long ksys_msgsnd(int msqid, struct msgbuf __user *msgp, size_t msgsz,
96231c213f2SDominik Brodowski 		 int msgflg)
963651971cbSsuzuki {
964651971cbSsuzuki 	long mtype;
965651971cbSsuzuki 
966651971cbSsuzuki 	if (get_user(mtype, &msgp->mtype))
967651971cbSsuzuki 		return -EFAULT;
968651971cbSsuzuki 	return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
969651971cbSsuzuki }
970651971cbSsuzuki 
SYSCALL_DEFINE4(msgsnd,int,msqid,struct msgbuf __user *,msgp,size_t,msgsz,int,msgflg)97131c213f2SDominik Brodowski SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
97231c213f2SDominik Brodowski 		int, msgflg)
97331c213f2SDominik Brodowski {
97431c213f2SDominik Brodowski 	return ksys_msgsnd(msqid, msgp, msgsz, msgflg);
97531c213f2SDominik Brodowski }
97631c213f2SDominik Brodowski 
9779b1404c2SAl Viro #ifdef CONFIG_COMPAT
9789b1404c2SAl Viro 
9799b1404c2SAl Viro struct compat_msgbuf {
9809b1404c2SAl Viro 	compat_long_t mtype;
9819b1404c2SAl Viro 	char mtext[1];
9829b1404c2SAl Viro };
9839b1404c2SAl Viro 
compat_ksys_msgsnd(int msqid,compat_uptr_t msgp,compat_ssize_t msgsz,int msgflg)98431c213f2SDominik Brodowski long compat_ksys_msgsnd(int msqid, compat_uptr_t msgp,
98531c213f2SDominik Brodowski 		       compat_ssize_t msgsz, int msgflg)
9869b1404c2SAl Viro {
9879b1404c2SAl Viro 	struct compat_msgbuf __user *up = compat_ptr(msgp);
9889b1404c2SAl Viro 	compat_long_t mtype;
9899b1404c2SAl Viro 
9909b1404c2SAl Viro 	if (get_user(mtype, &up->mtype))
9919b1404c2SAl Viro 		return -EFAULT;
9929b1404c2SAl Viro 	return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg);
9939b1404c2SAl Viro }
99431c213f2SDominik Brodowski 
COMPAT_SYSCALL_DEFINE4(msgsnd,int,msqid,compat_uptr_t,msgp,compat_ssize_t,msgsz,int,msgflg)99531c213f2SDominik Brodowski COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp,
99631c213f2SDominik Brodowski 		       compat_ssize_t, msgsz, int, msgflg)
99731c213f2SDominik Brodowski {
99831c213f2SDominik Brodowski 	return compat_ksys_msgsnd(msqid, msgp, msgsz, msgflg);
99931c213f2SDominik Brodowski }
10009b1404c2SAl Viro #endif
10019b1404c2SAl Viro 
convert_mode(long * msgtyp,int msgflg)10021da177e4SLinus Torvalds static inline int convert_mode(long *msgtyp, int msgflg)
10031da177e4SLinus Torvalds {
10048ac6ed58SPeter Hurley 	if (msgflg & MSG_COPY)
10058ac6ed58SPeter Hurley 		return SEARCH_NUMBER;
10061da177e4SLinus Torvalds 	/*
10071da177e4SLinus Torvalds 	 *  find message of correct type.
10081da177e4SLinus Torvalds 	 *  msgtyp = 0 => get first.
10091da177e4SLinus Torvalds 	 *  msgtyp > 0 => get first message of matching type.
10101da177e4SLinus Torvalds 	 *  msgtyp < 0 => get message with least type must be < abs(msgtype).
10111da177e4SLinus Torvalds 	 */
10121da177e4SLinus Torvalds 	if (*msgtyp == 0)
10131da177e4SLinus Torvalds 		return SEARCH_ANY;
10141da177e4SLinus Torvalds 	if (*msgtyp < 0) {
101599989835SJiri Slaby 		if (*msgtyp == LONG_MIN) /* -LONG_MIN is undefined */
101699989835SJiri Slaby 			*msgtyp = LONG_MAX;
101799989835SJiri Slaby 		else
10185a06a363SIngo Molnar 			*msgtyp = -*msgtyp;
10191da177e4SLinus Torvalds 		return SEARCH_LESSEQUAL;
10201da177e4SLinus Torvalds 	}
10211da177e4SLinus Torvalds 	if (msgflg & MSG_EXCEPT)
10221da177e4SLinus Torvalds 		return SEARCH_NOTEQUAL;
10231da177e4SLinus Torvalds 	return SEARCH_EQUAL;
10241da177e4SLinus Torvalds }
10251da177e4SLinus Torvalds 
do_msg_fill(void __user * dest,struct msg_msg * msg,size_t bufsz)1026f9dd87f4SStanislav Kinsbursky static long do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
1027f9dd87f4SStanislav Kinsbursky {
1028f9dd87f4SStanislav Kinsbursky 	struct msgbuf __user *msgp = dest;
1029f9dd87f4SStanislav Kinsbursky 	size_t msgsz;
1030f9dd87f4SStanislav Kinsbursky 
1031f9dd87f4SStanislav Kinsbursky 	if (put_user(msg->m_type, &msgp->mtype))
1032f9dd87f4SStanislav Kinsbursky 		return -EFAULT;
1033f9dd87f4SStanislav Kinsbursky 
1034f9dd87f4SStanislav Kinsbursky 	msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz;
1035f9dd87f4SStanislav Kinsbursky 	if (store_msg(msgp->mtext, msg, msgsz))
1036f9dd87f4SStanislav Kinsbursky 		return -EFAULT;
1037f9dd87f4SStanislav Kinsbursky 	return msgsz;
1038f9dd87f4SStanislav Kinsbursky }
1039f9dd87f4SStanislav Kinsbursky 
10404a674f34SStanislav Kinsbursky #ifdef CONFIG_CHECKPOINT_RESTORE
10413fcfe786SStanislav Kinsbursky /*
10423fcfe786SStanislav Kinsbursky  * This function creates new kernel message structure, large enough to store
10433fcfe786SStanislav Kinsbursky  * bufsz message bytes.
10443fcfe786SStanislav Kinsbursky  */
prepare_copy(void __user * buf,size_t bufsz)10458ac6ed58SPeter Hurley static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz)
10464a674f34SStanislav Kinsbursky {
10474a674f34SStanislav Kinsbursky 	struct msg_msg *copy;
10484a674f34SStanislav Kinsbursky 
10494a674f34SStanislav Kinsbursky 	/*
10504a674f34SStanislav Kinsbursky 	 * Create dummy message to copy real message to.
10514a674f34SStanislav Kinsbursky 	 */
10524a674f34SStanislav Kinsbursky 	copy = load_msg(buf, bufsz);
10534a674f34SStanislav Kinsbursky 	if (!IS_ERR(copy))
10544a674f34SStanislav Kinsbursky 		copy->m_ts = bufsz;
10554a674f34SStanislav Kinsbursky 	return copy;
10564a674f34SStanislav Kinsbursky }
10574a674f34SStanislav Kinsbursky 
free_copy(struct msg_msg * copy)105885398aa8SStanislav Kinsbursky static inline void free_copy(struct msg_msg *copy)
10594a674f34SStanislav Kinsbursky {
106085398aa8SStanislav Kinsbursky 	if (copy)
10614a674f34SStanislav Kinsbursky 		free_msg(copy);
10624a674f34SStanislav Kinsbursky }
10634a674f34SStanislav Kinsbursky #else
prepare_copy(void __user * buf,size_t bufsz)10648ac6ed58SPeter Hurley static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz)
1065b30efe27SStanislav Kinsbursky {
1066b30efe27SStanislav Kinsbursky 	return ERR_PTR(-ENOSYS);
1067b30efe27SStanislav Kinsbursky }
1068b30efe27SStanislav Kinsbursky 
free_copy(struct msg_msg * copy)106985398aa8SStanislav Kinsbursky static inline void free_copy(struct msg_msg *copy)
107085398aa8SStanislav Kinsbursky {
107185398aa8SStanislav Kinsbursky }
10724a674f34SStanislav Kinsbursky #endif
10734a674f34SStanislav Kinsbursky 
find_msg(struct msg_queue * msq,long * msgtyp,int mode)1074daaf74cfSPeter Hurley static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
1075daaf74cfSPeter Hurley {
1076368ae537SSvenning Sørensen 	struct msg_msg *msg, *found = NULL;
1077daaf74cfSPeter Hurley 	long count = 0;
1078daaf74cfSPeter Hurley 
1079daaf74cfSPeter Hurley 	list_for_each_entry(msg, &msq->q_messages, m_list) {
1080daaf74cfSPeter Hurley 		if (testmsg(msg, *msgtyp, mode) &&
1081d8c6e854SEric W. Biederman 		    !security_msg_queue_msgrcv(&msq->q_perm, msg, current,
1082daaf74cfSPeter Hurley 					       *msgtyp, mode)) {
1083daaf74cfSPeter Hurley 			if (mode == SEARCH_LESSEQUAL && msg->m_type != 1) {
1084daaf74cfSPeter Hurley 				*msgtyp = msg->m_type - 1;
1085368ae537SSvenning Sørensen 				found = msg;
1086daaf74cfSPeter Hurley 			} else if (mode == SEARCH_NUMBER) {
1087daaf74cfSPeter Hurley 				if (*msgtyp == count)
1088daaf74cfSPeter Hurley 					return msg;
1089daaf74cfSPeter Hurley 			} else
1090daaf74cfSPeter Hurley 				return msg;
1091daaf74cfSPeter Hurley 			count++;
1092daaf74cfSPeter Hurley 		}
1093daaf74cfSPeter Hurley 	}
1094daaf74cfSPeter Hurley 
1095368ae537SSvenning Sørensen 	return found ?: ERR_PTR(-EAGAIN);
1096daaf74cfSPeter Hurley }
1097daaf74cfSPeter Hurley 
do_msgrcv(int msqid,void __user * buf,size_t bufsz,long msgtyp,int msgflg,long (* msg_handler)(void __user *,struct msg_msg *,size_t))10989b1404c2SAl Viro static long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
1099f9dd87f4SStanislav Kinsbursky 	       long (*msg_handler)(void __user *, struct msg_msg *, size_t))
11001da177e4SLinus Torvalds {
11011da177e4SLinus Torvalds 	int mode;
110241a0d523SDavidlohr Bueso 	struct msg_queue *msq;
11031e786937SKirill Korotaev 	struct ipc_namespace *ns;
110441a0d523SDavidlohr Bueso 	struct msg_msg *msg, *copy = NULL;
1105194a6b5bSWaiman Long 	DEFINE_WAKE_Q(wake_q);
11061da177e4SLinus Torvalds 
110788b9e456SPeter Hurley 	ns = current->nsproxy->ipc_ns;
110888b9e456SPeter Hurley 
1109f9dd87f4SStanislav Kinsbursky 	if (msqid < 0 || (long) bufsz < 0)
11101da177e4SLinus Torvalds 		return -EINVAL;
111141a0d523SDavidlohr Bueso 
11124a674f34SStanislav Kinsbursky 	if (msgflg & MSG_COPY) {
11134f87dac3SMichael Kerrisk 		if ((msgflg & MSG_EXCEPT) || !(msgflg & IPC_NOWAIT))
11144f87dac3SMichael Kerrisk 			return -EINVAL;
11158ac6ed58SPeter Hurley 		copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax));
11164a674f34SStanislav Kinsbursky 		if (IS_ERR(copy))
11174a674f34SStanislav Kinsbursky 			return PTR_ERR(copy);
11184a674f34SStanislav Kinsbursky 	}
11191da177e4SLinus Torvalds 	mode = convert_mode(&msgtyp, msgflg);
11201da177e4SLinus Torvalds 
112141a0d523SDavidlohr Bueso 	rcu_read_lock();
112241a0d523SDavidlohr Bueso 	msq = msq_obtain_object_check(ns, msqid);
11234a674f34SStanislav Kinsbursky 	if (IS_ERR(msq)) {
112441a0d523SDavidlohr Bueso 		rcu_read_unlock();
112585398aa8SStanislav Kinsbursky 		free_copy(copy);
1126023a5355SNadia Derbey 		return PTR_ERR(msq);
11274a674f34SStanislav Kinsbursky 	}
11281da177e4SLinus Torvalds 
11291da177e4SLinus Torvalds 	for (;;) {
11301da177e4SLinus Torvalds 		struct msg_receiver msr_d;
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 		msg = ERR_PTR(-EACCES);
1133b0e77598SSerge E. Hallyn 		if (ipcperms(ns, &msq->q_perm, S_IRUGO))
113441a0d523SDavidlohr Bueso 			goto out_unlock1;
11351da177e4SLinus Torvalds 
113641a0d523SDavidlohr Bueso 		ipc_lock_object(&msq->q_perm);
11374271b05aSDavidlohr Bueso 
11384271b05aSDavidlohr Bueso 		/* raced with RMID? */
11390f3d2b01SRafael Aquini 		if (!ipc_valid_object(&msq->q_perm)) {
11404271b05aSDavidlohr Bueso 			msg = ERR_PTR(-EIDRM);
11414271b05aSDavidlohr Bueso 			goto out_unlock0;
11424271b05aSDavidlohr Bueso 		}
11434271b05aSDavidlohr Bueso 
1144daaf74cfSPeter Hurley 		msg = find_msg(msq, &msgtyp, mode);
11451da177e4SLinus Torvalds 		if (!IS_ERR(msg)) {
11465a06a363SIngo Molnar 			/*
11475a06a363SIngo Molnar 			 * Found a suitable message.
11485a06a363SIngo Molnar 			 * Unlink it from the queue.
11495a06a363SIngo Molnar 			 */
1150f9dd87f4SStanislav Kinsbursky 			if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
11511da177e4SLinus Torvalds 				msg = ERR_PTR(-E2BIG);
115241a0d523SDavidlohr Bueso 				goto out_unlock0;
11531da177e4SLinus Torvalds 			}
11543fcfe786SStanislav Kinsbursky 			/*
11553fcfe786SStanislav Kinsbursky 			 * If we are copying, then do not unlink message and do
11563fcfe786SStanislav Kinsbursky 			 * not update queue parameters.
11573fcfe786SStanislav Kinsbursky 			 */
1158852028afSPeter Hurley 			if (msgflg & MSG_COPY) {
1159852028afSPeter Hurley 				msg = copy_msg(msg, copy);
116041a0d523SDavidlohr Bueso 				goto out_unlock0;
1161852028afSPeter Hurley 			}
116241a0d523SDavidlohr Bueso 
11631da177e4SLinus Torvalds 			list_del(&msg->m_list);
11641da177e4SLinus Torvalds 			msq->q_qnum--;
11652a70b787SArnd Bergmann 			msq->q_rtime = ktime_get_real_seconds();
116639a4940eSEric W. Biederman 			ipc_update_pid(&msq->q_lrpid, task_tgid(current));
11671da177e4SLinus Torvalds 			msq->q_cbytes -= msg->m_ts;
116872d1e611SJiebin Sun 			percpu_counter_sub_local(&ns->percpu_msg_bytes, msg->m_ts);
116972d1e611SJiebin Sun 			percpu_counter_sub_local(&ns->percpu_msg_hdrs, 1);
1170ed27f912SDavidlohr Bueso 			ss_wakeup(msq, &wake_q, false);
117141a0d523SDavidlohr Bueso 
117241a0d523SDavidlohr Bueso 			goto out_unlock0;
11731da177e4SLinus Torvalds 		}
117441a0d523SDavidlohr Bueso 
11751da177e4SLinus Torvalds 		/* No message waiting. Wait for a message */
11761da177e4SLinus Torvalds 		if (msgflg & IPC_NOWAIT) {
11771da177e4SLinus Torvalds 			msg = ERR_PTR(-ENOMSG);
117841a0d523SDavidlohr Bueso 			goto out_unlock0;
11791da177e4SLinus Torvalds 		}
118041a0d523SDavidlohr Bueso 
11811da177e4SLinus Torvalds 		list_add_tail(&msr_d.r_list, &msq->q_receivers);
11821da177e4SLinus Torvalds 		msr_d.r_tsk = current;
11831da177e4SLinus Torvalds 		msr_d.r_msgtype = msgtyp;
11841da177e4SLinus Torvalds 		msr_d.r_mode = mode;
11851da177e4SLinus Torvalds 		if (msgflg & MSG_NOERROR)
11861da177e4SLinus Torvalds 			msr_d.r_maxsize = INT_MAX;
11871da177e4SLinus Torvalds 		else
1188f9dd87f4SStanislav Kinsbursky 			msr_d.r_maxsize = bufsz;
11890d97a82bSManfred Spraul 
11900d97a82bSManfred Spraul 		/* memory barrier not require due to ipc_lock_object() */
11910d97a82bSManfred Spraul 		WRITE_ONCE(msr_d.r_msg, ERR_PTR(-EAGAIN));
11920d97a82bSManfred Spraul 
11930d97a82bSManfred Spraul 		/* memory barrier not required, we own ipc_lock_object() */
1194f75a2f35SDavidlohr Bueso 		__set_current_state(TASK_INTERRUPTIBLE);
11951da177e4SLinus Torvalds 
119641a0d523SDavidlohr Bueso 		ipc_unlock_object(&msq->q_perm);
119741a0d523SDavidlohr Bueso 		rcu_read_unlock();
11981da177e4SLinus Torvalds 		schedule();
11991da177e4SLinus Torvalds 
1200ee51636cSSebastian Andrzej Siewior 		/*
1201ee51636cSSebastian Andrzej Siewior 		 * Lockless receive, part 1:
1202ee51636cSSebastian Andrzej Siewior 		 * We don't hold a reference to the queue and getting a
1203ee51636cSSebastian Andrzej Siewior 		 * reference would defeat the idea of a lockless operation,
1204ee51636cSSebastian Andrzej Siewior 		 * thus the code relies on rcu to guarantee the existence of
1205ee51636cSSebastian Andrzej Siewior 		 * msq:
12061da177e4SLinus Torvalds 		 * Prior to destruction, expunge_all(-EIRDM) changes r_msg.
12071da177e4SLinus Torvalds 		 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
12081da177e4SLinus Torvalds 		 */
12091da177e4SLinus Torvalds 		rcu_read_lock();
12101da177e4SLinus Torvalds 
1211ff35e5efSDavidlohr Bueso 		/*
1212ee51636cSSebastian Andrzej Siewior 		 * Lockless receive, part 2:
1213ee51636cSSebastian Andrzej Siewior 		 * The work in pipelined_send() and expunge_all():
1214ee51636cSSebastian Andrzej Siewior 		 * - Set pointer to message
1215ee51636cSSebastian Andrzej Siewior 		 * - Queue the receiver task for later wakeup
1216ee51636cSSebastian Andrzej Siewior 		 * - Wake up the process after the lock is dropped.
1217ee51636cSSebastian Andrzej Siewior 		 *
1218ee51636cSSebastian Andrzej Siewior 		 * Should the process wake up before this wakeup (due to a
1219ee51636cSSebastian Andrzej Siewior 		 * signal) it will either see the message and continue ...
1220ff35e5efSDavidlohr Bueso 		 */
1221ee51636cSSebastian Andrzej Siewior 		msg = READ_ONCE(msr_d.r_msg);
12220d97a82bSManfred Spraul 		if (msg != ERR_PTR(-EAGAIN)) {
12230d97a82bSManfred Spraul 			/* see MSG_BARRIER for purpose/pairing */
12240d97a82bSManfred Spraul 			smp_acquire__after_ctrl_dep();
12250d97a82bSManfred Spraul 
122641a0d523SDavidlohr Bueso 			goto out_unlock1;
12270d97a82bSManfred Spraul 		}
12281da177e4SLinus Torvalds 
1229ee51636cSSebastian Andrzej Siewior 		 /*
1230ee51636cSSebastian Andrzej Siewior 		  * ... or see -EAGAIN, acquire the lock to check the message
1231ee51636cSSebastian Andrzej Siewior 		  * again.
12321da177e4SLinus Torvalds 		  */
123341a0d523SDavidlohr Bueso 		ipc_lock_object(&msq->q_perm);
12341da177e4SLinus Torvalds 
12350d97a82bSManfred Spraul 		msg = READ_ONCE(msr_d.r_msg);
12361da177e4SLinus Torvalds 		if (msg != ERR_PTR(-EAGAIN))
123741a0d523SDavidlohr Bueso 			goto out_unlock0;
12381da177e4SLinus Torvalds 
12391da177e4SLinus Torvalds 		list_del(&msr_d.r_list);
12401da177e4SLinus Torvalds 		if (signal_pending(current)) {
12411da177e4SLinus Torvalds 			msg = ERR_PTR(-ERESTARTNOHAND);
124241a0d523SDavidlohr Bueso 			goto out_unlock0;
12431da177e4SLinus Torvalds 		}
124441a0d523SDavidlohr Bueso 
124541a0d523SDavidlohr Bueso 		ipc_unlock_object(&msq->q_perm);
12461da177e4SLinus Torvalds 	}
124741a0d523SDavidlohr Bueso 
124841a0d523SDavidlohr Bueso out_unlock0:
124941a0d523SDavidlohr Bueso 	ipc_unlock_object(&msq->q_perm);
1250e3658538SDavidlohr Bueso 	wake_up_q(&wake_q);
125141a0d523SDavidlohr Bueso out_unlock1:
125241a0d523SDavidlohr Bueso 	rcu_read_unlock();
12534a674f34SStanislav Kinsbursky 	if (IS_ERR(msg)) {
125485398aa8SStanislav Kinsbursky 		free_copy(copy);
12551da177e4SLinus Torvalds 		return PTR_ERR(msg);
12564a674f34SStanislav Kinsbursky 	}
12571da177e4SLinus Torvalds 
1258f9dd87f4SStanislav Kinsbursky 	bufsz = msg_handler(buf, msg, bufsz);
12591da177e4SLinus Torvalds 	free_msg(msg);
12605a06a363SIngo Molnar 
1261f9dd87f4SStanislav Kinsbursky 	return bufsz;
12621da177e4SLinus Torvalds }
12631da177e4SLinus Torvalds 
ksys_msgrcv(int msqid,struct msgbuf __user * msgp,size_t msgsz,long msgtyp,int msgflg)1264078faac9SDominik Brodowski long ksys_msgrcv(int msqid, struct msgbuf __user *msgp, size_t msgsz,
1265078faac9SDominik Brodowski 		 long msgtyp, int msgflg)
1266078faac9SDominik Brodowski {
1267078faac9SDominik Brodowski 	return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill);
1268078faac9SDominik Brodowski }
1269078faac9SDominik Brodowski 
SYSCALL_DEFINE5(msgrcv,int,msqid,struct msgbuf __user *,msgp,size_t,msgsz,long,msgtyp,int,msgflg)1270e48fbb69SHeiko Carstens SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
1271e48fbb69SHeiko Carstens 		long, msgtyp, int, msgflg)
1272651971cbSsuzuki {
1273078faac9SDominik Brodowski 	return ksys_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
1274651971cbSsuzuki }
1275651971cbSsuzuki 
12769b1404c2SAl Viro #ifdef CONFIG_COMPAT
compat_do_msg_fill(void __user * dest,struct msg_msg * msg,size_t bufsz)12779b1404c2SAl Viro static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
12789b1404c2SAl Viro {
12799b1404c2SAl Viro 	struct compat_msgbuf __user *msgp = dest;
12809b1404c2SAl Viro 	size_t msgsz;
12819b1404c2SAl Viro 
12829b1404c2SAl Viro 	if (put_user(msg->m_type, &msgp->mtype))
12839b1404c2SAl Viro 		return -EFAULT;
12849b1404c2SAl Viro 
12859b1404c2SAl Viro 	msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz;
12869b1404c2SAl Viro 	if (store_msg(msgp->mtext, msg, msgsz))
12879b1404c2SAl Viro 		return -EFAULT;
12889b1404c2SAl Viro 	return msgsz;
12899b1404c2SAl Viro }
12909b1404c2SAl Viro 
compat_ksys_msgrcv(int msqid,compat_uptr_t msgp,compat_ssize_t msgsz,compat_long_t msgtyp,int msgflg)1291078faac9SDominik Brodowski long compat_ksys_msgrcv(int msqid, compat_uptr_t msgp, compat_ssize_t msgsz,
1292078faac9SDominik Brodowski 			compat_long_t msgtyp, int msgflg)
12939b1404c2SAl Viro {
12949b1404c2SAl Viro 	return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp,
12959b1404c2SAl Viro 			 msgflg, compat_do_msg_fill);
12969b1404c2SAl Viro }
1297078faac9SDominik Brodowski 
COMPAT_SYSCALL_DEFINE5(msgrcv,int,msqid,compat_uptr_t,msgp,compat_ssize_t,msgsz,compat_long_t,msgtyp,int,msgflg)1298078faac9SDominik Brodowski COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp,
1299078faac9SDominik Brodowski 		       compat_ssize_t, msgsz, compat_long_t, msgtyp,
1300078faac9SDominik Brodowski 		       int, msgflg)
1301078faac9SDominik Brodowski {
1302078faac9SDominik Brodowski 	return compat_ksys_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
1303078faac9SDominik Brodowski }
13049b1404c2SAl Viro #endif
13053440a6bdSDavidlohr Bueso 
msg_init_ns(struct ipc_namespace * ns)130672d1e611SJiebin Sun int msg_init_ns(struct ipc_namespace *ns)
13073440a6bdSDavidlohr Bueso {
130872d1e611SJiebin Sun 	int ret;
130972d1e611SJiebin Sun 
13103440a6bdSDavidlohr Bueso 	ns->msg_ctlmax = MSGMAX;
13113440a6bdSDavidlohr Bueso 	ns->msg_ctlmnb = MSGMNB;
13120050ee05SManfred Spraul 	ns->msg_ctlmni = MSGMNI;
13133440a6bdSDavidlohr Bueso 
131472d1e611SJiebin Sun 	ret = percpu_counter_init(&ns->percpu_msg_bytes, 0, GFP_KERNEL);
131572d1e611SJiebin Sun 	if (ret)
131672d1e611SJiebin Sun 		goto fail_msg_bytes;
131772d1e611SJiebin Sun 	ret = percpu_counter_init(&ns->percpu_msg_hdrs, 0, GFP_KERNEL);
131872d1e611SJiebin Sun 	if (ret)
131972d1e611SJiebin Sun 		goto fail_msg_hdrs;
1320eae04d25SDavidlohr Bueso 	ipc_init_ids(&ns->ids[IPC_MSG_IDS]);
132172d1e611SJiebin Sun 	return 0;
132272d1e611SJiebin Sun 
132372d1e611SJiebin Sun fail_msg_hdrs:
132472d1e611SJiebin Sun 	percpu_counter_destroy(&ns->percpu_msg_bytes);
132572d1e611SJiebin Sun fail_msg_bytes:
132672d1e611SJiebin Sun 	return ret;
13273440a6bdSDavidlohr Bueso }
13283440a6bdSDavidlohr Bueso 
13293440a6bdSDavidlohr Bueso #ifdef CONFIG_IPC_NS
msg_exit_ns(struct ipc_namespace * ns)13303440a6bdSDavidlohr Bueso void msg_exit_ns(struct ipc_namespace *ns)
13313440a6bdSDavidlohr Bueso {
13323440a6bdSDavidlohr Bueso 	free_ipcs(ns, &msg_ids(ns), freeque);
13333440a6bdSDavidlohr Bueso 	idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr);
13340cfb6aeeSGuillaume Knispel 	rhashtable_destroy(&ns->ids[IPC_MSG_IDS].key_ht);
1335*64b4c411SAndrew Morton 	percpu_counter_destroy(&ns->percpu_msg_bytes);
1336*64b4c411SAndrew Morton 	percpu_counter_destroy(&ns->percpu_msg_hdrs);
13373440a6bdSDavidlohr Bueso }
13383440a6bdSDavidlohr Bueso #endif
13393440a6bdSDavidlohr Bueso 
13401da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
sysvipc_msg_proc_show(struct seq_file * s,void * it)134119b4946cSMike Waychison static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
13421da177e4SLinus Torvalds {
134339a4940eSEric W. Biederman 	struct pid_namespace *pid_ns = ipc_seq_pid_ns(s);
13441efdb69bSEric W. Biederman 	struct user_namespace *user_ns = seq_user_ns(s);
1345ade9f91bSKees Cook 	struct kern_ipc_perm *ipcp = it;
1346ade9f91bSKees Cook 	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
13471da177e4SLinus Torvalds 
13487f032d6eSJoe Perches 	seq_printf(s,
134950578ea9SDeepa Dinamani 		   "%10d %10d  %4o  %10lu %10lu %5u %5u %5u %5u %5u %5u %10llu %10llu %10llu\n",
13501da177e4SLinus Torvalds 		   msq->q_perm.key,
13517ca7e564SNadia Derbey 		   msq->q_perm.id,
13521da177e4SLinus Torvalds 		   msq->q_perm.mode,
13531da177e4SLinus Torvalds 		   msq->q_cbytes,
13541da177e4SLinus Torvalds 		   msq->q_qnum,
135539a4940eSEric W. Biederman 		   pid_nr_ns(msq->q_lspid, pid_ns),
135639a4940eSEric W. Biederman 		   pid_nr_ns(msq->q_lrpid, pid_ns),
13571efdb69bSEric W. Biederman 		   from_kuid_munged(user_ns, msq->q_perm.uid),
13581efdb69bSEric W. Biederman 		   from_kgid_munged(user_ns, msq->q_perm.gid),
13591efdb69bSEric W. Biederman 		   from_kuid_munged(user_ns, msq->q_perm.cuid),
13601efdb69bSEric W. Biederman 		   from_kgid_munged(user_ns, msq->q_perm.cgid),
13611da177e4SLinus Torvalds 		   msq->q_stime,
13621da177e4SLinus Torvalds 		   msq->q_rtime,
13631da177e4SLinus Torvalds 		   msq->q_ctime);
13647f032d6eSJoe Perches 
13657f032d6eSJoe Perches 	return 0;
13661da177e4SLinus Torvalds }
13671da177e4SLinus Torvalds #endif
13683440a6bdSDavidlohr Bueso 
msg_init(void)1369eae04d25SDavidlohr Bueso void __init msg_init(void)
13703440a6bdSDavidlohr Bueso {
1371eae04d25SDavidlohr Bueso 	msg_init_ns(&init_ipc_ns);
13723440a6bdSDavidlohr Bueso 
13733440a6bdSDavidlohr Bueso 	ipc_init_proc_interface("sysvipc/msg",
13743440a6bdSDavidlohr Bueso 				"       key      msqid perms      cbytes       qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n",
13753440a6bdSDavidlohr Bueso 				IPC_MSG_IDS, sysvipc_msg_proc_show);
13763440a6bdSDavidlohr Bueso }
1377