1b2441318SGreg Kroah-Hartman// SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds/*
31da177e4SLinus Torvalds * linux/ipc/msg.c
45a06a363SIngo Molnar * 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>
425f921ae9SIngo Molnar
431da177e4SLinus Torvalds#include <asm/current.h>
447153e402SPaul McQuade#include <linux/uaccess.h>
451da177e4SLinus Torvalds#include "util.h"
461da177e4SLinus Torvalds
4734b56df9SEric W. Biederman/* one msq_queue structure for each present queue on the system */
4834b56df9SEric W. Biedermanstruct msg_queue {
4934b56df9SEric W. Biederman	struct kern_ipc_perm q_perm;
5034b56df9SEric W. Biederman	time64_t q_stime;		/* last msgsnd time */
5134b56df9SEric W. Biederman	time64_t q_rtime;		/* last msgrcv time */
5234b56df9SEric W. Biederman	time64_t q_ctime;		/* last change time */
5334b56df9SEric W. Biederman	unsigned long q_cbytes;		/* current number of bytes on queue */
5434b56df9SEric W. Biederman	unsigned long q_qnum;		/* number of messages in queue */
5534b56df9SEric W. Biederman	unsigned long q_qbytes;		/* max number of bytes on queue */
5639a4940eSEric W. Biederman	struct pid *q_lspid;		/* pid of last msgsnd */
5739a4940eSEric W. Biederman	struct pid *q_lrpid;		/* last receive pid */
5834b56df9SEric W. Biederman
5934b56df9SEric W. Biederman	struct list_head q_messages;
6034b56df9SEric W. Biederman	struct list_head q_receivers;
6134b56df9SEric W. Biederman	struct list_head q_senders;
6234b56df9SEric W. Biederman} __randomize_layout;
6334b56df9SEric W. Biederman
640d97a82bSManfred Spraul/*
650d97a82bSManfred Spraul * MSG_BARRIER Locking:
660d97a82bSManfred Spraul *
670d97a82bSManfred Spraul * Similar to the optimization used in ipc/mqueue.c, one syscall return path
680d97a82bSManfred Spraul * does not acquire any locks when it sees that a message exists in
690d97a82bSManfred Spraul * msg_receiver.r_msg. Therefore r_msg is set using smp_store_release()
700d97a82bSManfred Spraul * and accessed using READ_ONCE()+smp_acquire__after_ctrl_dep(). In addition,
710d97a82bSManfred Spraul * wake_q_add_safe() is used. See ipc/mqueue.c for more details
720d97a82bSManfred Spraul */
730d97a82bSManfred Spraul
744bb6657dSDavidlohr Bueso/* one msg_receiver structure for each sleeping receiver */
751da177e4SLinus Torvaldsstruct msg_receiver {
765a06a363SIngo Molnar	struct list_head	r_list;
775a06a363SIngo Molnar	struct task_struct	*r_tsk;
781da177e4SLinus Torvalds
795a06a363SIngo Molnar	int			r_mode;
805a06a363SIngo Molnar	long			r_msgtype;
815a06a363SIngo Molnar	long			r_maxsize;
821da177e4SLinus Torvalds
83ee51636cSSebastian Andrzej Siewior	struct msg_msg		*r_msg;
841da177e4SLinus Torvalds};
851da177e4SLinus Torvalds
861da177e4SLinus Torvalds/* one msg_sender for each sleeping sender */
871da177e4SLinus Torvaldsstruct msg_sender {
885a06a363SIngo Molnar	struct list_head	list;
895a06a363SIngo Molnar	struct task_struct	*tsk;
90ed27f912SDavidlohr Bueso	size_t                  msgsz;
911da177e4SLinus Torvalds};
921da177e4SLinus Torvalds
931da177e4SLinus Torvalds#define SEARCH_ANY		1
941da177e4SLinus Torvalds#define SEARCH_EQUAL		2
951da177e4SLinus Torvalds#define SEARCH_NOTEQUAL		3
961da177e4SLinus Torvalds#define SEARCH_LESSEQUAL	4
978ac6ed58SPeter Hurley#define SEARCH_NUMBER		5
981da177e4SLinus Torvalds
99ed2ddbf8SPierre Peiffer#define msg_ids(ns)	((ns)->ids[IPC_MSG_IDS])
1001da177e4SLinus Torvalds
101a5001a0dSDavidlohr Buesostatic inline struct msg_queue *msq_obtain_object(struct ipc_namespace *ns, int id)
102a5001a0dSDavidlohr Bueso{
10355b7ae50SDavidlohr Bueso	struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&msg_ids(ns), id);
104a5001a0dSDavidlohr Bueso
105a5001a0dSDavidlohr Bueso	if (IS_ERR(ipcp))
106a5001a0dSDavidlohr Bueso		return ERR_CAST(ipcp);
107a5001a0dSDavidlohr Bueso
108a5001a0dSDavidlohr Bueso	return container_of(ipcp, struct msg_queue, q_perm);
109a5001a0dSDavidlohr Bueso}
110a5001a0dSDavidlohr Bueso
111a5001a0dSDavidlohr Buesostatic inline struct msg_queue *msq_obtain_object_check(struct ipc_namespace *ns,
112a5001a0dSDavidlohr Bueso							int id)
113a5001a0dSDavidlohr Bueso{
114a5001a0dSDavidlohr Bueso	struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&msg_ids(ns), id);
115a5001a0dSDavidlohr Bueso
116a5001a0dSDavidlohr Bueso	if (IS_ERR(ipcp))
117a5001a0dSDavidlohr Bueso		return ERR_CAST(ipcp);
118a5001a0dSDavidlohr Bueso
119a5001a0dSDavidlohr Bueso	return container_of(ipcp, struct msg_queue, q_perm);
120a5001a0dSDavidlohr Bueso}
121a5001a0dSDavidlohr Bueso
1227ca7e564SNadia Derbeystatic inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
1237ca7e564SNadia Derbey{
1247ca7e564SNadia Derbey	ipc_rmid(&msg_ids(ns), &s->q_perm);
1257ca7e564SNadia Derbey}
1267ca7e564SNadia Derbey
12753dad6d3SDavidlohr Buesostatic void msg_rcu_free(struct rcu_head *head)
12853dad6d3SDavidlohr Bueso{
129dba4cdd3SManfred Spraul	struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu);
130dba4cdd3SManfred Spraul	struct msg_queue *msq = container_of(p, struct msg_queue, q_perm);
13153dad6d3SDavidlohr Bueso
132d8c6e854SEric W. Biederman	security_msg_queue_free(&msq->q_perm);
133bc8136a5SVasily Averin	kfree(msq);
13452f90890SKees Cook}
13552f90890SKees Cook
136f4566f04SNadia Derbey/**
137f4566f04SNadia Derbey * newque - Create a new msg queue
138f4566f04SNadia Derbey * @ns: namespace
139f4566f04SNadia Derbey * @params: ptr to the structure that contains the key and msgflg
140f4566f04SNadia Derbey *
141d9a605e4SDavidlohr Bueso * Called with msg_ids.rwsem held (writer)
142f4566f04SNadia Derbey */
1437748dbfaSNadia Derbeystatic int newque(struct ipc_namespace *ns, struct ipc_params *params)
1441da177e4SLinus Torvalds{
1451da177e4SLinus Torvalds	struct msg_queue *msq;
14651c23b7bSManfred Spraul	int retval;
1477748dbfaSNadia Derbey	key_t key = params->key;
1487748dbfaSNadia Derbey	int msgflg = params->flg;
1491da177e4SLinus Torvalds
15018319498SVasily Averin	msq = kmalloc(sizeof(*msq), GFP_KERNEL_ACCOUNT);
151fb259c31SKees Cook	if (unlikely(!msq))
1521da177e4SLinus Torvalds		return -ENOMEM;
1531da177e4SLinus Torvalds
1545a06a363SIngo Molnar	msq->q_perm.mode = msgflg & S_IRWXUGO;
1551da177e4SLinus Torvalds	msq->q_perm.key = key;
1561da177e4SLinus Torvalds
1571da177e4SLinus Torvalds	msq->q_perm.security = NULL;
158d8c6e854SEric W. Biederman	retval = security_msg_queue_alloc(&msq->q_perm);
1591da177e4SLinus Torvalds	if (retval) {
160bc8136a5SVasily Averin		kfree(msq);
1611da177e4SLinus Torvalds		return retval;
1621da177e4SLinus Torvalds	}
1631da177e4SLinus Torvalds
1641da177e4SLinus Torvalds	msq->q_stime = msq->q_rtime = 0;
16550578ea9SDeepa Dinamani	msq->q_ctime = ktime_get_real_seconds();
1661da177e4SLinus Torvalds	msq->q_cbytes = msq->q_qnum = 0;
1671e786937SKirill Korotaev	msq->q_qbytes = ns->msg_ctlmnb;
16839a4940eSEric W. Biederman	msq->q_lspid = msq->q_lrpid = NULL;
1691da177e4SLinus Torvalds	INIT_LIST_HEAD(&msq->q_messages);
1701da177e4SLinus Torvalds	INIT_LIST_HEAD(&msq->q_receivers);
1711da177e4SLinus Torvalds	INIT_LIST_HEAD(&msq->q_senders);
1727ca7e564SNadia Derbey
173b9a53227SLinus Torvalds	/* ipc_addid() locks msq upon success. */
17451c23b7bSManfred Spraul	retval = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
17551c23b7bSManfred Spraul	if (retval < 0) {
17639cfffd7SManfred Spraul		ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
17751c23b7bSManfred Spraul		return retval;
178b9a53227SLinus Torvalds	}
179b9a53227SLinus Torvalds
180cf9d5d78SDavidlohr Bueso	ipc_unlock_object(&msq->q_perm);
181dbfcd91fSDavidlohr Bueso	rcu_read_unlock();
1821da177e4SLinus Torvalds
1837ca7e564SNadia Derbey	return msq->q_perm.id;
1841da177e4SLinus Torvalds}
1851da177e4SLinus Torvalds
186ed27f912SDavidlohr Buesostatic inline bool msg_fits_inqueue(struct msg_queue *msq, size_t msgsz)
187ed27f912SDavidlohr Bueso{
188ed27f912SDavidlohr Bueso	return msgsz + msq->q_cbytes <= msq->q_qbytes &&
189ed27f912SDavidlohr Bueso		1 + msq->q_qnum <= msq->q_qbytes;
190ed27f912SDavidlohr Bueso}
191ed27f912SDavidlohr Bueso
192ed27f912SDavidlohr Buesostatic inline void ss_add(struct msg_queue *msq,
193ed27f912SDavidlohr Bueso			  struct msg_sender *mss, size_t msgsz)
1941da177e4SLinus Torvalds{
1955a06a363SIngo Molnar	mss->tsk = current;
196ed27f912SDavidlohr Bueso	mss->msgsz = msgsz;
1970d97a82bSManfred Spraul	/*
1980d97a82bSManfred Spraul	 * No memory barrier required: we did ipc_lock_object(),
1990d97a82bSManfred Spraul	 * and the waker obtains that lock before calling wake_q_add().
2000d97a82bSManfred Spraul	 */
201f75a2f35SDavidlohr Bueso	__set_current_state(TASK_INTERRUPTIBLE);
2025a06a363SIngo Molnar	list_add_tail(&mss->list, &msq->q_senders);
2031da177e4SLinus Torvalds}
2041da177e4SLinus Torvalds
2055a06a363SIngo Molnarstatic inline void ss_del(struct msg_sender *mss)
2061da177e4SLinus Torvalds{
207ed27f912SDavidlohr Bueso	if (mss->list.next)
2081da177e4SLinus Torvalds		list_del(&mss->list);
2091da177e4SLinus Torvalds}
2101da177e4SLinus Torvalds
211ed27f912SDavidlohr Buesostatic void ss_wakeup(struct msg_queue *msq,
212d0d6a2a9SDavidlohr Bueso		      struct wake_q_head *wake_q, bool kill)
2131da177e4SLinus Torvalds{
21441239fe8SNikola Pajkovsky	struct msg_sender *mss, *t;
215ed27f912SDavidlohr Bueso	struct task_struct *stop_tsk = NULL;
216ed27f912SDavidlohr Bueso	struct list_head *h = &msq->q_senders;
2171da177e4SLinus Torvalds
21841239fe8SNikola Pajkovsky	list_for_each_entry_safe(mss, t, h, list) {
2195a06a363SIngo Molnar		if (kill)
2205a06a363SIngo Molnar			mss->list.next = NULL;
221ed27f912SDavidlohr Bueso
222ed27f912SDavidlohr Bueso		/*
223ed27f912SDavidlohr Bueso		 * Stop at the first task we don't wakeup,
224ed27f912SDavidlohr Bueso		 * we've already iterated the original
225ed27f912SDavidlohr Bueso		 * sender queue.
226ed27f912SDavidlohr Bueso		 */
227ed27f912SDavidlohr Bueso		else if (stop_tsk == mss->tsk)
228ed27f912SDavidlohr Bueso			break;
229ed27f912SDavidlohr Bueso		/*
230ed27f912SDavidlohr Bueso		 * We are not in an EIDRM scenario here, therefore
231ed27f912SDavidlohr Bueso		 * verify that we really need to wakeup the task.
232ed27f912SDavidlohr Bueso		 * To maintain current semantics and wakeup order,
233ed27f912SDavidlohr Bueso		 * move the sender to the tail on behalf of the
234ed27f912SDavidlohr Bueso		 * blocked task.
235ed27f912SDavidlohr Bueso		 */
236ed27f912SDavidlohr Bueso		else if (!msg_fits_inqueue(msq, mss->msgsz)) {
237ed27f912SDavidlohr Bueso			if (!stop_tsk)
238ed27f912SDavidlohr Bueso				stop_tsk = mss->tsk;
239ed27f912SDavidlohr Bueso
240ed27f912SDavidlohr Bueso			list_move_tail(&mss->list, &msq->q_senders);
241ed27f912SDavidlohr Bueso			continue;
242ed27f912SDavidlohr Bueso		}
243ed27f912SDavidlohr Bueso
244e3658538SDavidlohr Bueso		wake_q_add(wake_q, mss->tsk);
2451da177e4SLinus Torvalds	}
2461da177e4SLinus Torvalds}
2471da177e4SLinus Torvalds
248ee51636cSSebastian Andrzej Siewiorstatic void expunge_all(struct msg_queue *msq, int res,
249ee51636cSSebastian Andrzej Siewior			struct wake_q_head *wake_q)
2501da177e4SLinus Torvalds{
25141239fe8SNikola Pajkovsky	struct msg_receiver *msr, *t;
2525a06a363SIngo Molnar
25341239fe8SNikola Pajkovsky	list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
254a11ddb37SVarad Gautam		struct task_struct *r_tsk;
255a11ddb37SVarad Gautam
256a11ddb37SVarad Gautam		r_tsk = get_task_struct(msr->r_tsk);
2570d97a82bSManfred Spraul
2580d97a82bSManfred Spraul		/* see MSG_BARRIER for purpose/pairing */
2590d97a82bSManfred Spraul		smp_store_release(&msr->r_msg, ERR_PTR(res));
260a11ddb37SVarad Gautam		wake_q_add_safe(wake_q, r_tsk);
2611da177e4SLinus Torvalds	}
2621da177e4SLinus Torvalds}
2635a06a363SIngo Molnar
2645a06a363SIngo Molnar/*
2655a06a363SIngo Molnar * freeque() wakes up waiters on the sender and receiver waiting queue,
266f4566f04SNadia Derbey * removes the message queue from message queue ID IDR, and cleans up all the
267f4566f04SNadia Derbey * messages associated with this queue.
2681da177e4SLinus Torvalds *
269d9a605e4SDavidlohr Bueso * msg_ids.rwsem (writer) and the spinlock for this message queue are held
270d9a605e4SDavidlohr Bueso * before freeque() is called. msg_ids.rwsem remains locked on exit.
2711da177e4SLinus Torvalds */
27201b8b07aSPierre Peifferstatic void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
2734b78e201SJules Irenge	__releases(RCU)
2744b78e201SJules Irenge	__releases(&msq->q_perm)
2751da177e4SLinus Torvalds{
27641239fe8SNikola Pajkovsky	struct msg_msg *msg, *t;
27701b8b07aSPierre Peiffer	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
278194a6b5bSWaiman Long	DEFINE_WAKE_Q(wake_q);
2791da177e4SLinus Torvalds
280ee51636cSSebastian Andrzej Siewior	expunge_all(msq, -EIDRM, &wake_q);
281ed27f912SDavidlohr Bueso	ss_wakeup(msq, &wake_q, true);
2827ca7e564SNadia Derbey	msg_rmid(ns, msq);
2834718787dSDavidlohr Bueso	ipc_unlock_object(&msq->q_perm);
284ee51636cSSebastian Andrzej Siewior	wake_up_q(&wake_q);
2854718787dSDavidlohr Bueso	rcu_read_unlock();
2865a06a363SIngo Molnar
28741239fe8SNikola Pajkovsky	list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) {
2883ac88a41SKirill Korotaev		atomic_dec(&ns->msg_hdrs);
2891da177e4SLinus Torvalds		free_msg(msg);
2901da177e4SLinus Torvalds	}
2913ac88a41SKirill Korotaev	atomic_sub(msq->q_cbytes, &ns->msg_bytes);
29239a4940eSEric W. Biederman	ipc_update_pid(&msq->q_lspid, NULL);
29339a4940eSEric W. Biederman	ipc_update_pid(&msq->q_lrpid, NULL);
294dba4cdd3SManfred Spraul	ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
2951da177e4SLinus Torvalds}
2961da177e4SLinus Torvalds
2973d65661aSDominik Brodowskilong ksys_msgget(key_t key, int msgflg)
2981da177e4SLinus Torvalds{
2991e786937SKirill Korotaev	struct ipc_namespace *ns;
300eb66ec44SMathias Krause	static const struct ipc_ops msg_ops = {
301eb66ec44SMathias Krause		.getnew = newque,
30250ab44b1SEric W. Biederman		.associate = security_msg_queue_associate,
303eb66ec44SMathias Krause	};
3047748dbfaSNadia Derbey	struct ipc_params msg_params;
3051e786937SKirill Korotaev
3061e786937SKirill Korotaev	ns = current->nsproxy->ipc_ns;
3077ca7e564SNadia Derbey
3087748dbfaSNadia Derbey	msg_params.key = key;
3097748dbfaSNadia Derbey	msg_params.flg = msgflg;
3105a06a363SIngo Molnar
3117748dbfaSNadia Derbey	return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);
3121da177e4SLinus Torvalds}
3131da177e4SLinus Torvalds
3143d65661aSDominik BrodowskiSYSCALL_DEFINE2(msgget, key_t, key, int, msgflg)
3153d65661aSDominik Brodowski{
3163d65661aSDominik Brodowski	return ksys_msgget(key, msgflg);
3173d65661aSDominik Brodowski}
3183d65661aSDominik Brodowski
3195a06a363SIngo Molnarstatic inline unsigned long
3205a06a363SIngo Molnarcopy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
3211da177e4SLinus Torvalds{
322239521f3SManfred Spraul	switch (version) {
3231da177e4SLinus Torvalds	case IPC_64:
3245a06a363SIngo Molnar		return copy_to_user(buf, in, sizeof(*in));
3251da177e4SLinus Torvalds	case IPC_OLD:
3265a06a363SIngo Molnar	{
3271da177e4SLinus Torvalds		struct msqid_ds out;
3281da177e4SLinus Torvalds
3295a06a363SIngo Molnar		memset(&out, 0, sizeof(out));
3301da177e4SLinus Torvalds
3311da177e4SLinus Torvalds		ipc64_perm_to_ipc_perm(&in->msg_perm, &out.msg_perm);
3321da177e4SLinus Torvalds
3331da177e4SLinus Torvalds		out.msg_stime		= in->msg_stime;
3341da177e4SLinus Torvalds		out.msg_rtime		= in->msg_rtime;
3351da177e4SLinus Torvalds		out.msg_ctime		= in->msg_ctime;
3361da177e4SLinus Torvalds
3374be929beSAlexey Dobriyan		if (in->msg_cbytes > USHRT_MAX)
3384be929beSAlexey Dobriyan			out.msg_cbytes	= USHRT_MAX;
3391da177e4SLinus Torvalds		else
3401da177e4SLinus Torvalds			out.msg_cbytes	= in->msg_cbytes;
3411da177e4SLinus Torvalds		out.msg_lcbytes		= in->msg_cbytes;
3421da177e4SLinus Torvalds
3434be929beSAlexey Dobriyan		if (in->msg_qnum > USHRT_MAX)
3444be929beSAlexey Dobriyan			out.msg_qnum	= USHRT_MAX;
3451da177e4SLinus Torvalds		else
3461da177e4SLinus Torvalds			out.msg_qnum	= in->msg_qnum;
3471da177e4SLinus Torvalds
3484be929beSAlexey Dobriyan		if (in->msg_qbytes > USHRT_MAX)
3494be929beSAlexey Dobriyan			out.msg_qbytes	= USHRT_MAX;
3501da177e4SLinus Torvalds		else
3511da177e4SLinus Torvalds			out.msg_qbytes	= in->msg_qbytes;
3521da177e4SLinus Torvalds		out.msg_lqbytes		= in->msg_qbytes;
3531da177e4SLinus Torvalds
3541da177e4SLinus Torvalds		out.msg_lspid		= in->msg_lspid;
3551da177e4SLinus Torvalds		out.msg_lrpid		= in->msg_lrpid;
3561da177e4SLinus Torvalds
3575a06a363SIngo Molnar		return copy_to_user(buf, &out, sizeof(out));
3585a06a363SIngo Molnar	}
3591da177e4SLinus Torvalds	default:
3601da177e4SLinus Torvalds		return -EINVAL;
3611da177e4SLinus Torvalds	}
3621da177e4SLinus Torvalds}
3631da177e4SLinus Torvalds
3645a06a363SIngo Molnarstatic inline unsigned long
365016d7132SPierre Peiffercopy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version)
3661da177e4SLinus Torvalds{
367239521f3SManfred Spraul	switch (version) {
3681da177e4SLinus Torvalds	case IPC_64:
369016d7132SPierre Peiffer		if (copy_from_user(out, buf, sizeof(*out)))
3701da177e4SLinus Torvalds			return -EFAULT;
3711da177e4SLinus Torvalds		return 0;
3721da177e4SLinus Torvalds	case IPC_OLD:
3735a06a363SIngo Molnar	{
3741da177e4SLinus Torvalds		struct msqid_ds tbuf_old;
3751da177e4SLinus Torvalds
3765a06a363SIngo Molnar		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
3771da177e4SLinus Torvalds			return -EFAULT;
3781da177e4SLinus Torvalds
379239521f3SManfred Spraul		out->msg_perm.uid	= tbuf_old.msg_perm.uid;
380239521f3SManfred Spraul		out->msg_perm.gid	= tbuf_old.msg_perm.gid;
381239521f3SManfred Spraul		out->msg_perm.mode	= tbuf_old.msg_perm.mode;
3821da177e4SLinus Torvalds
3835a06a363SIngo Molnar		if (tbuf_old.msg_qbytes == 0)
384016d7132SPierre Peiffer			out->msg_qbytes	= tbuf_old.msg_lqbytes;
3851da177e4SLinus Torvalds		else
386016d7132SPierre Peiffer			out->msg_qbytes	= tbuf_old.msg_qbytes;
3871da177e4SLinus Torvalds
3881da177e4SLinus Torvalds		return 0;
3895a06a363SIngo Molnar	}
3901da177e4SLinus Torvalds	default:
3911da177e4SLinus Torvalds		return -EINVAL;
3921da177e4SLinus Torvalds	}
3931da177e4SLinus Torvalds}
3941da177e4SLinus Torvalds
395a0d092fcSPierre Peiffer/*
396d9a605e4SDavidlohr Bueso * This function handles some msgctl commands which require the rwsem
397a0d092fcSPierre Peiffer * to be held in write mode.
398d9a605e4SDavidlohr Bueso * NOTE: no locks must be held, the rwsem is taken inside this function.
399a0d092fcSPierre Peiffer */
400a0d092fcSPierre Peifferstatic int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
401889b3317SLu Shuaibing			struct ipc64_perm *perm, int msg_qbytes)
4021da177e4SLinus Torvalds{
4031da177e4SLinus Torvalds	struct kern_ipc_perm *ipcp;
404a0d092fcSPierre Peiffer	struct msg_queue *msq;
405a0d092fcSPierre Peiffer	int err;
406a0d092fcSPierre Peiffer
407d9a605e4SDavidlohr Bueso	down_write(&msg_ids(ns).rwsem);
4087b4cc5d8SDavidlohr Bueso	rcu_read_lock();
4097b4cc5d8SDavidlohr Bueso
4104241c1a3SManfred Spraul	ipcp = ipcctl_obtain_check(ns, &msg_ids(ns), msqid, cmd,
411889b3317SLu Shuaibing				      perm, msg_qbytes);
4127b4cc5d8SDavidlohr Bueso	if (IS_ERR(ipcp)) {
4137b4cc5d8SDavidlohr Bueso		err = PTR_ERR(ipcp);
4147b4cc5d8SDavidlohr Bueso		goto out_unlock1;
4157b4cc5d8SDavidlohr Bueso	}
416a0d092fcSPierre Peiffer
417a5f75e7fSPierre Peiffer	msq = container_of(ipcp, struct msg_queue, q_perm);
418a0d092fcSPierre Peiffer
419d8c6e854SEric W. Biederman	err = security_msg_queue_msgctl(&msq->q_perm, cmd);
420a0d092fcSPierre Peiffer	if (err)
42115724ecbSDavidlohr Bueso		goto out_unlock1;
422a0d092fcSPierre Peiffer
423a0d092fcSPierre Peiffer	switch (cmd) {
424a0d092fcSPierre Peiffer	case IPC_RMID:
42515724ecbSDavidlohr Bueso		ipc_lock_object(&msq->q_perm);
4267b4cc5d8SDavidlohr Bueso		/* freeque unlocks the ipc object and rcu */
427a0d092fcSPierre Peiffer		freeque(ns, ipcp);
428a0d092fcSPierre Peiffer		goto out_up;
429a0d092fcSPierre Peiffer	case IPC_SET:
430e3658538SDavidlohr Bueso	{
431194a6b5bSWaiman Long		DEFINE_WAKE_Q(wake_q);
432e3658538SDavidlohr Bueso