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