162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/ipc/msg.c 462306a36Sopenharmony_ci * Copyright (C) 1992 Krishna Balasubramanian 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Removed all the remaining kerneld mess 762306a36Sopenharmony_ci * Catch the -EFAULT stuff properly 862306a36Sopenharmony_ci * Use GFP_KERNEL for messages as in 1.2 962306a36Sopenharmony_ci * Fixed up the unchecked user space derefs 1062306a36Sopenharmony_ci * Copyright (C) 1998 Alan Cox & Andi Kleen 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * /proc/sysvipc/msg support (c) 1999 Dragos Acostachioaie <dragos@iname.com> 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * mostly rewritten, threaded and wake-one semantics added 1562306a36Sopenharmony_ci * MSGMAX limit removed, sysctl's added 1662306a36Sopenharmony_ci * (c) 1999 Manfred Spraul <manfred@colorfullife.com> 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * support for audit of ipc object properties and permission changes 1962306a36Sopenharmony_ci * Dustin Kirkland <dustin.kirkland@us.ibm.com> 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * namespaces support 2262306a36Sopenharmony_ci * OpenVZ, SWsoft Inc. 2362306a36Sopenharmony_ci * Pavel Emelianov <xemul@openvz.org> 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/capability.h> 2762306a36Sopenharmony_ci#include <linux/msg.h> 2862306a36Sopenharmony_ci#include <linux/spinlock.h> 2962306a36Sopenharmony_ci#include <linux/init.h> 3062306a36Sopenharmony_ci#include <linux/mm.h> 3162306a36Sopenharmony_ci#include <linux/proc_fs.h> 3262306a36Sopenharmony_ci#include <linux/list.h> 3362306a36Sopenharmony_ci#include <linux/security.h> 3462306a36Sopenharmony_ci#include <linux/sched/wake_q.h> 3562306a36Sopenharmony_ci#include <linux/syscalls.h> 3662306a36Sopenharmony_ci#include <linux/audit.h> 3762306a36Sopenharmony_ci#include <linux/seq_file.h> 3862306a36Sopenharmony_ci#include <linux/rwsem.h> 3962306a36Sopenharmony_ci#include <linux/nsproxy.h> 4062306a36Sopenharmony_ci#include <linux/ipc_namespace.h> 4162306a36Sopenharmony_ci#include <linux/rhashtable.h> 4262306a36Sopenharmony_ci#include <linux/percpu_counter.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <asm/current.h> 4562306a36Sopenharmony_ci#include <linux/uaccess.h> 4662306a36Sopenharmony_ci#include "util.h" 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* one msq_queue structure for each present queue on the system */ 4962306a36Sopenharmony_cistruct msg_queue { 5062306a36Sopenharmony_ci struct kern_ipc_perm q_perm; 5162306a36Sopenharmony_ci time64_t q_stime; /* last msgsnd time */ 5262306a36Sopenharmony_ci time64_t q_rtime; /* last msgrcv time */ 5362306a36Sopenharmony_ci time64_t q_ctime; /* last change time */ 5462306a36Sopenharmony_ci unsigned long q_cbytes; /* current number of bytes on queue */ 5562306a36Sopenharmony_ci unsigned long q_qnum; /* number of messages in queue */ 5662306a36Sopenharmony_ci unsigned long q_qbytes; /* max number of bytes on queue */ 5762306a36Sopenharmony_ci struct pid *q_lspid; /* pid of last msgsnd */ 5862306a36Sopenharmony_ci struct pid *q_lrpid; /* last receive pid */ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci struct list_head q_messages; 6162306a36Sopenharmony_ci struct list_head q_receivers; 6262306a36Sopenharmony_ci struct list_head q_senders; 6362306a36Sopenharmony_ci} __randomize_layout; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * MSG_BARRIER Locking: 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Similar to the optimization used in ipc/mqueue.c, one syscall return path 6962306a36Sopenharmony_ci * does not acquire any locks when it sees that a message exists in 7062306a36Sopenharmony_ci * msg_receiver.r_msg. Therefore r_msg is set using smp_store_release() 7162306a36Sopenharmony_ci * and accessed using READ_ONCE()+smp_acquire__after_ctrl_dep(). In addition, 7262306a36Sopenharmony_ci * wake_q_add_safe() is used. See ipc/mqueue.c for more details 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* one msg_receiver structure for each sleeping receiver */ 7662306a36Sopenharmony_cistruct msg_receiver { 7762306a36Sopenharmony_ci struct list_head r_list; 7862306a36Sopenharmony_ci struct task_struct *r_tsk; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci int r_mode; 8162306a36Sopenharmony_ci long r_msgtype; 8262306a36Sopenharmony_ci long r_maxsize; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci struct msg_msg *r_msg; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* one msg_sender for each sleeping sender */ 8862306a36Sopenharmony_cistruct msg_sender { 8962306a36Sopenharmony_ci struct list_head list; 9062306a36Sopenharmony_ci struct task_struct *tsk; 9162306a36Sopenharmony_ci size_t msgsz; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define SEARCH_ANY 1 9562306a36Sopenharmony_ci#define SEARCH_EQUAL 2 9662306a36Sopenharmony_ci#define SEARCH_NOTEQUAL 3 9762306a36Sopenharmony_ci#define SEARCH_LESSEQUAL 4 9862306a36Sopenharmony_ci#define SEARCH_NUMBER 5 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic inline struct msg_queue *msq_obtain_object(struct ipc_namespace *ns, int id) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&msg_ids(ns), id); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (IS_ERR(ipcp)) 10762306a36Sopenharmony_ci return ERR_CAST(ipcp); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return container_of(ipcp, struct msg_queue, q_perm); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline struct msg_queue *msq_obtain_object_check(struct ipc_namespace *ns, 11362306a36Sopenharmony_ci int id) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&msg_ids(ns), id); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (IS_ERR(ipcp)) 11862306a36Sopenharmony_ci return ERR_CAST(ipcp); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return container_of(ipcp, struct msg_queue, q_perm); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci ipc_rmid(&msg_ids(ns), &s->q_perm); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void msg_rcu_free(struct rcu_head *head) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu); 13162306a36Sopenharmony_ci struct msg_queue *msq = container_of(p, struct msg_queue, q_perm); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci security_msg_queue_free(&msq->q_perm); 13462306a36Sopenharmony_ci kfree(msq); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * newque - Create a new msg queue 13962306a36Sopenharmony_ci * @ns: namespace 14062306a36Sopenharmony_ci * @params: ptr to the structure that contains the key and msgflg 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * Called with msg_ids.rwsem held (writer) 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic int newque(struct ipc_namespace *ns, struct ipc_params *params) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct msg_queue *msq; 14762306a36Sopenharmony_ci int retval; 14862306a36Sopenharmony_ci key_t key = params->key; 14962306a36Sopenharmony_ci int msgflg = params->flg; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci msq = kmalloc(sizeof(*msq), GFP_KERNEL_ACCOUNT); 15262306a36Sopenharmony_ci if (unlikely(!msq)) 15362306a36Sopenharmony_ci return -ENOMEM; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci msq->q_perm.mode = msgflg & S_IRWXUGO; 15662306a36Sopenharmony_ci msq->q_perm.key = key; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci msq->q_perm.security = NULL; 15962306a36Sopenharmony_ci retval = security_msg_queue_alloc(&msq->q_perm); 16062306a36Sopenharmony_ci if (retval) { 16162306a36Sopenharmony_ci kfree(msq); 16262306a36Sopenharmony_ci return retval; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci msq->q_stime = msq->q_rtime = 0; 16662306a36Sopenharmony_ci msq->q_ctime = ktime_get_real_seconds(); 16762306a36Sopenharmony_ci msq->q_cbytes = msq->q_qnum = 0; 16862306a36Sopenharmony_ci msq->q_qbytes = ns->msg_ctlmnb; 16962306a36Sopenharmony_ci msq->q_lspid = msq->q_lrpid = NULL; 17062306a36Sopenharmony_ci INIT_LIST_HEAD(&msq->q_messages); 17162306a36Sopenharmony_ci INIT_LIST_HEAD(&msq->q_receivers); 17262306a36Sopenharmony_ci INIT_LIST_HEAD(&msq->q_senders); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* ipc_addid() locks msq upon success. */ 17562306a36Sopenharmony_ci retval = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); 17662306a36Sopenharmony_ci if (retval < 0) { 17762306a36Sopenharmony_ci ipc_rcu_putref(&msq->q_perm, msg_rcu_free); 17862306a36Sopenharmony_ci return retval; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 18262306a36Sopenharmony_ci rcu_read_unlock(); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return msq->q_perm.id; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic inline bool msg_fits_inqueue(struct msg_queue *msq, size_t msgsz) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return msgsz + msq->q_cbytes <= msq->q_qbytes && 19062306a36Sopenharmony_ci 1 + msq->q_qnum <= msq->q_qbytes; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic inline void ss_add(struct msg_queue *msq, 19462306a36Sopenharmony_ci struct msg_sender *mss, size_t msgsz) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci mss->tsk = current; 19762306a36Sopenharmony_ci mss->msgsz = msgsz; 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * No memory barrier required: we did ipc_lock_object(), 20062306a36Sopenharmony_ci * and the waker obtains that lock before calling wake_q_add(). 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 20362306a36Sopenharmony_ci list_add_tail(&mss->list, &msq->q_senders); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic inline void ss_del(struct msg_sender *mss) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci if (mss->list.next) 20962306a36Sopenharmony_ci list_del(&mss->list); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void ss_wakeup(struct msg_queue *msq, 21362306a36Sopenharmony_ci struct wake_q_head *wake_q, bool kill) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct msg_sender *mss, *t; 21662306a36Sopenharmony_ci struct task_struct *stop_tsk = NULL; 21762306a36Sopenharmony_ci struct list_head *h = &msq->q_senders; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci list_for_each_entry_safe(mss, t, h, list) { 22062306a36Sopenharmony_ci if (kill) 22162306a36Sopenharmony_ci mss->list.next = NULL; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * Stop at the first task we don't wakeup, 22562306a36Sopenharmony_ci * we've already iterated the original 22662306a36Sopenharmony_ci * sender queue. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci else if (stop_tsk == mss->tsk) 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci /* 23162306a36Sopenharmony_ci * We are not in an EIDRM scenario here, therefore 23262306a36Sopenharmony_ci * verify that we really need to wakeup the task. 23362306a36Sopenharmony_ci * To maintain current semantics and wakeup order, 23462306a36Sopenharmony_ci * move the sender to the tail on behalf of the 23562306a36Sopenharmony_ci * blocked task. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci else if (!msg_fits_inqueue(msq, mss->msgsz)) { 23862306a36Sopenharmony_ci if (!stop_tsk) 23962306a36Sopenharmony_ci stop_tsk = mss->tsk; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci list_move_tail(&mss->list, &msq->q_senders); 24262306a36Sopenharmony_ci continue; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci wake_q_add(wake_q, mss->tsk); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void expunge_all(struct msg_queue *msq, int res, 25062306a36Sopenharmony_ci struct wake_q_head *wake_q) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct msg_receiver *msr, *t; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) { 25562306a36Sopenharmony_ci struct task_struct *r_tsk; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci r_tsk = get_task_struct(msr->r_tsk); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* see MSG_BARRIER for purpose/pairing */ 26062306a36Sopenharmony_ci smp_store_release(&msr->r_msg, ERR_PTR(res)); 26162306a36Sopenharmony_ci wake_q_add_safe(wake_q, r_tsk); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * freeque() wakes up waiters on the sender and receiver waiting queue, 26762306a36Sopenharmony_ci * removes the message queue from message queue ID IDR, and cleans up all the 26862306a36Sopenharmony_ci * messages associated with this queue. 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * msg_ids.rwsem (writer) and the spinlock for this message queue are held 27162306a36Sopenharmony_ci * before freeque() is called. msg_ids.rwsem remains locked on exit. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_cistatic void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 27462306a36Sopenharmony_ci __releases(RCU) 27562306a36Sopenharmony_ci __releases(&msq->q_perm) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct msg_msg *msg, *t; 27862306a36Sopenharmony_ci struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm); 27962306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci expunge_all(msq, -EIDRM, &wake_q); 28262306a36Sopenharmony_ci ss_wakeup(msq, &wake_q, true); 28362306a36Sopenharmony_ci msg_rmid(ns, msq); 28462306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 28562306a36Sopenharmony_ci wake_up_q(&wake_q); 28662306a36Sopenharmony_ci rcu_read_unlock(); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) { 28962306a36Sopenharmony_ci percpu_counter_sub_local(&ns->percpu_msg_hdrs, 1); 29062306a36Sopenharmony_ci free_msg(msg); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci percpu_counter_sub_local(&ns->percpu_msg_bytes, msq->q_cbytes); 29362306a36Sopenharmony_ci ipc_update_pid(&msq->q_lspid, NULL); 29462306a36Sopenharmony_ci ipc_update_pid(&msq->q_lrpid, NULL); 29562306a36Sopenharmony_ci ipc_rcu_putref(&msq->q_perm, msg_rcu_free); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cilong ksys_msgget(key_t key, int msgflg) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct ipc_namespace *ns; 30162306a36Sopenharmony_ci static const struct ipc_ops msg_ops = { 30262306a36Sopenharmony_ci .getnew = newque, 30362306a36Sopenharmony_ci .associate = security_msg_queue_associate, 30462306a36Sopenharmony_ci }; 30562306a36Sopenharmony_ci struct ipc_params msg_params; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci msg_params.key = key; 31062306a36Sopenharmony_ci msg_params.flg = msgflg; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciSYSCALL_DEFINE2(msgget, key_t, key, int, msgflg) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci return ksys_msgget(key, msgflg); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic inline unsigned long 32162306a36Sopenharmony_cicopy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci switch (version) { 32462306a36Sopenharmony_ci case IPC_64: 32562306a36Sopenharmony_ci return copy_to_user(buf, in, sizeof(*in)); 32662306a36Sopenharmony_ci case IPC_OLD: 32762306a36Sopenharmony_ci { 32862306a36Sopenharmony_ci struct msqid_ds out; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci memset(&out, 0, sizeof(out)); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci ipc64_perm_to_ipc_perm(&in->msg_perm, &out.msg_perm); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci out.msg_stime = in->msg_stime; 33562306a36Sopenharmony_ci out.msg_rtime = in->msg_rtime; 33662306a36Sopenharmony_ci out.msg_ctime = in->msg_ctime; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (in->msg_cbytes > USHRT_MAX) 33962306a36Sopenharmony_ci out.msg_cbytes = USHRT_MAX; 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci out.msg_cbytes = in->msg_cbytes; 34262306a36Sopenharmony_ci out.msg_lcbytes = in->msg_cbytes; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (in->msg_qnum > USHRT_MAX) 34562306a36Sopenharmony_ci out.msg_qnum = USHRT_MAX; 34662306a36Sopenharmony_ci else 34762306a36Sopenharmony_ci out.msg_qnum = in->msg_qnum; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (in->msg_qbytes > USHRT_MAX) 35062306a36Sopenharmony_ci out.msg_qbytes = USHRT_MAX; 35162306a36Sopenharmony_ci else 35262306a36Sopenharmony_ci out.msg_qbytes = in->msg_qbytes; 35362306a36Sopenharmony_ci out.msg_lqbytes = in->msg_qbytes; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci out.msg_lspid = in->msg_lspid; 35662306a36Sopenharmony_ci out.msg_lrpid = in->msg_lrpid; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return copy_to_user(buf, &out, sizeof(out)); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci default: 36162306a36Sopenharmony_ci return -EINVAL; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic inline unsigned long 36662306a36Sopenharmony_cicopy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci switch (version) { 36962306a36Sopenharmony_ci case IPC_64: 37062306a36Sopenharmony_ci if (copy_from_user(out, buf, sizeof(*out))) 37162306a36Sopenharmony_ci return -EFAULT; 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci case IPC_OLD: 37462306a36Sopenharmony_ci { 37562306a36Sopenharmony_ci struct msqid_ds tbuf_old; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 37862306a36Sopenharmony_ci return -EFAULT; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci out->msg_perm.uid = tbuf_old.msg_perm.uid; 38162306a36Sopenharmony_ci out->msg_perm.gid = tbuf_old.msg_perm.gid; 38262306a36Sopenharmony_ci out->msg_perm.mode = tbuf_old.msg_perm.mode; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (tbuf_old.msg_qbytes == 0) 38562306a36Sopenharmony_ci out->msg_qbytes = tbuf_old.msg_lqbytes; 38662306a36Sopenharmony_ci else 38762306a36Sopenharmony_ci out->msg_qbytes = tbuf_old.msg_qbytes; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci default: 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* 39762306a36Sopenharmony_ci * This function handles some msgctl commands which require the rwsem 39862306a36Sopenharmony_ci * to be held in write mode. 39962306a36Sopenharmony_ci * NOTE: no locks must be held, the rwsem is taken inside this function. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_cistatic int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, 40262306a36Sopenharmony_ci struct ipc64_perm *perm, int msg_qbytes) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct kern_ipc_perm *ipcp; 40562306a36Sopenharmony_ci struct msg_queue *msq; 40662306a36Sopenharmony_ci int err; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci down_write(&msg_ids(ns).rwsem); 40962306a36Sopenharmony_ci rcu_read_lock(); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ipcp = ipcctl_obtain_check(ns, &msg_ids(ns), msqid, cmd, 41262306a36Sopenharmony_ci perm, msg_qbytes); 41362306a36Sopenharmony_ci if (IS_ERR(ipcp)) { 41462306a36Sopenharmony_ci err = PTR_ERR(ipcp); 41562306a36Sopenharmony_ci goto out_unlock1; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci msq = container_of(ipcp, struct msg_queue, q_perm); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci err = security_msg_queue_msgctl(&msq->q_perm, cmd); 42162306a36Sopenharmony_ci if (err) 42262306a36Sopenharmony_ci goto out_unlock1; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci switch (cmd) { 42562306a36Sopenharmony_ci case IPC_RMID: 42662306a36Sopenharmony_ci ipc_lock_object(&msq->q_perm); 42762306a36Sopenharmony_ci /* freeque unlocks the ipc object and rcu */ 42862306a36Sopenharmony_ci freeque(ns, ipcp); 42962306a36Sopenharmony_ci goto out_up; 43062306a36Sopenharmony_ci case IPC_SET: 43162306a36Sopenharmony_ci { 43262306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (msg_qbytes > ns->msg_ctlmnb && 43562306a36Sopenharmony_ci !capable(CAP_SYS_RESOURCE)) { 43662306a36Sopenharmony_ci err = -EPERM; 43762306a36Sopenharmony_ci goto out_unlock1; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ipc_lock_object(&msq->q_perm); 44162306a36Sopenharmony_ci err = ipc_update_perm(perm, ipcp); 44262306a36Sopenharmony_ci if (err) 44362306a36Sopenharmony_ci goto out_unlock0; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci msq->q_qbytes = msg_qbytes; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci msq->q_ctime = ktime_get_real_seconds(); 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * Sleeping receivers might be excluded by 45062306a36Sopenharmony_ci * stricter permissions. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci expunge_all(msq, -EAGAIN, &wake_q); 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * Sleeping senders might be able to send 45562306a36Sopenharmony_ci * due to a larger queue size. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci ss_wakeup(msq, &wake_q, false); 45862306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 45962306a36Sopenharmony_ci wake_up_q(&wake_q); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci goto out_unlock1; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci default: 46462306a36Sopenharmony_ci err = -EINVAL; 46562306a36Sopenharmony_ci goto out_unlock1; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ciout_unlock0: 46962306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 47062306a36Sopenharmony_ciout_unlock1: 47162306a36Sopenharmony_ci rcu_read_unlock(); 47262306a36Sopenharmony_ciout_up: 47362306a36Sopenharmony_ci up_write(&msg_ids(ns).rwsem); 47462306a36Sopenharmony_ci return err; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int msgctl_info(struct ipc_namespace *ns, int msqid, 47862306a36Sopenharmony_ci int cmd, struct msginfo *msginfo) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci int err; 48162306a36Sopenharmony_ci int max_idx; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * We must not return kernel stack data. 48562306a36Sopenharmony_ci * due to padding, it's not enough 48662306a36Sopenharmony_ci * to set all member fields. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci err = security_msg_queue_msgctl(NULL, cmd); 48962306a36Sopenharmony_ci if (err) 49062306a36Sopenharmony_ci return err; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci memset(msginfo, 0, sizeof(*msginfo)); 49362306a36Sopenharmony_ci msginfo->msgmni = ns->msg_ctlmni; 49462306a36Sopenharmony_ci msginfo->msgmax = ns->msg_ctlmax; 49562306a36Sopenharmony_ci msginfo->msgmnb = ns->msg_ctlmnb; 49662306a36Sopenharmony_ci msginfo->msgssz = MSGSSZ; 49762306a36Sopenharmony_ci msginfo->msgseg = MSGSEG; 49862306a36Sopenharmony_ci down_read(&msg_ids(ns).rwsem); 49962306a36Sopenharmony_ci if (cmd == MSG_INFO) 50062306a36Sopenharmony_ci msginfo->msgpool = msg_ids(ns).in_use; 50162306a36Sopenharmony_ci max_idx = ipc_get_maxidx(&msg_ids(ns)); 50262306a36Sopenharmony_ci up_read(&msg_ids(ns).rwsem); 50362306a36Sopenharmony_ci if (cmd == MSG_INFO) { 50462306a36Sopenharmony_ci msginfo->msgmap = min_t(int, 50562306a36Sopenharmony_ci percpu_counter_sum(&ns->percpu_msg_hdrs), 50662306a36Sopenharmony_ci INT_MAX); 50762306a36Sopenharmony_ci msginfo->msgtql = min_t(int, 50862306a36Sopenharmony_ci percpu_counter_sum(&ns->percpu_msg_bytes), 50962306a36Sopenharmony_ci INT_MAX); 51062306a36Sopenharmony_ci } else { 51162306a36Sopenharmony_ci msginfo->msgmap = MSGMAP; 51262306a36Sopenharmony_ci msginfo->msgpool = MSGPOOL; 51362306a36Sopenharmony_ci msginfo->msgtql = MSGTQL; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci return (max_idx < 0) ? 0 : max_idx; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int msgctl_stat(struct ipc_namespace *ns, int msqid, 51962306a36Sopenharmony_ci int cmd, struct msqid64_ds *p) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct msg_queue *msq; 52262306a36Sopenharmony_ci int err; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci memset(p, 0, sizeof(*p)); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci rcu_read_lock(); 52762306a36Sopenharmony_ci if (cmd == MSG_STAT || cmd == MSG_STAT_ANY) { 52862306a36Sopenharmony_ci msq = msq_obtain_object(ns, msqid); 52962306a36Sopenharmony_ci if (IS_ERR(msq)) { 53062306a36Sopenharmony_ci err = PTR_ERR(msq); 53162306a36Sopenharmony_ci goto out_unlock; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } else { /* IPC_STAT */ 53462306a36Sopenharmony_ci msq = msq_obtain_object_check(ns, msqid); 53562306a36Sopenharmony_ci if (IS_ERR(msq)) { 53662306a36Sopenharmony_ci err = PTR_ERR(msq); 53762306a36Sopenharmony_ci goto out_unlock; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* see comment for SHM_STAT_ANY */ 54262306a36Sopenharmony_ci if (cmd == MSG_STAT_ANY) 54362306a36Sopenharmony_ci audit_ipc_obj(&msq->q_perm); 54462306a36Sopenharmony_ci else { 54562306a36Sopenharmony_ci err = -EACCES; 54662306a36Sopenharmony_ci if (ipcperms(ns, &msq->q_perm, S_IRUGO)) 54762306a36Sopenharmony_ci goto out_unlock; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci err = security_msg_queue_msgctl(&msq->q_perm, cmd); 55162306a36Sopenharmony_ci if (err) 55262306a36Sopenharmony_ci goto out_unlock; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ipc_lock_object(&msq->q_perm); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (!ipc_valid_object(&msq->q_perm)) { 55762306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 55862306a36Sopenharmony_ci err = -EIDRM; 55962306a36Sopenharmony_ci goto out_unlock; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm); 56362306a36Sopenharmony_ci p->msg_stime = msq->q_stime; 56462306a36Sopenharmony_ci p->msg_rtime = msq->q_rtime; 56562306a36Sopenharmony_ci p->msg_ctime = msq->q_ctime; 56662306a36Sopenharmony_ci#ifndef CONFIG_64BIT 56762306a36Sopenharmony_ci p->msg_stime_high = msq->q_stime >> 32; 56862306a36Sopenharmony_ci p->msg_rtime_high = msq->q_rtime >> 32; 56962306a36Sopenharmony_ci p->msg_ctime_high = msq->q_ctime >> 32; 57062306a36Sopenharmony_ci#endif 57162306a36Sopenharmony_ci p->msg_cbytes = msq->q_cbytes; 57262306a36Sopenharmony_ci p->msg_qnum = msq->q_qnum; 57362306a36Sopenharmony_ci p->msg_qbytes = msq->q_qbytes; 57462306a36Sopenharmony_ci p->msg_lspid = pid_vnr(msq->q_lspid); 57562306a36Sopenharmony_ci p->msg_lrpid = pid_vnr(msq->q_lrpid); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (cmd == IPC_STAT) { 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * As defined in SUS: 58062306a36Sopenharmony_ci * Return 0 on success 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci err = 0; 58362306a36Sopenharmony_ci } else { 58462306a36Sopenharmony_ci /* 58562306a36Sopenharmony_ci * MSG_STAT and MSG_STAT_ANY (both Linux specific) 58662306a36Sopenharmony_ci * Return the full id, including the sequence number 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci err = msq->q_perm.id; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 59262306a36Sopenharmony_ciout_unlock: 59362306a36Sopenharmony_ci rcu_read_unlock(); 59462306a36Sopenharmony_ci return err; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic long ksys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf, int version) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct ipc_namespace *ns; 60062306a36Sopenharmony_ci struct msqid64_ds msqid64; 60162306a36Sopenharmony_ci int err; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (msqid < 0 || cmd < 0) 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci switch (cmd) { 60962306a36Sopenharmony_ci case IPC_INFO: 61062306a36Sopenharmony_ci case MSG_INFO: { 61162306a36Sopenharmony_ci struct msginfo msginfo; 61262306a36Sopenharmony_ci err = msgctl_info(ns, msqid, cmd, &msginfo); 61362306a36Sopenharmony_ci if (err < 0) 61462306a36Sopenharmony_ci return err; 61562306a36Sopenharmony_ci if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) 61662306a36Sopenharmony_ci err = -EFAULT; 61762306a36Sopenharmony_ci return err; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci case MSG_STAT: /* msqid is an index rather than a msg queue id */ 62062306a36Sopenharmony_ci case MSG_STAT_ANY: 62162306a36Sopenharmony_ci case IPC_STAT: 62262306a36Sopenharmony_ci err = msgctl_stat(ns, msqid, cmd, &msqid64); 62362306a36Sopenharmony_ci if (err < 0) 62462306a36Sopenharmony_ci return err; 62562306a36Sopenharmony_ci if (copy_msqid_to_user(buf, &msqid64, version)) 62662306a36Sopenharmony_ci err = -EFAULT; 62762306a36Sopenharmony_ci return err; 62862306a36Sopenharmony_ci case IPC_SET: 62962306a36Sopenharmony_ci if (copy_msqid_from_user(&msqid64, buf, version)) 63062306a36Sopenharmony_ci return -EFAULT; 63162306a36Sopenharmony_ci return msgctl_down(ns, msqid, cmd, &msqid64.msg_perm, 63262306a36Sopenharmony_ci msqid64.msg_qbytes); 63362306a36Sopenharmony_ci case IPC_RMID: 63462306a36Sopenharmony_ci return msgctl_down(ns, msqid, cmd, NULL, 0); 63562306a36Sopenharmony_ci default: 63662306a36Sopenharmony_ci return -EINVAL; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ciSYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci return ksys_msgctl(msqid, cmd, buf, IPC_64); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION 64662306a36Sopenharmony_cilong ksys_old_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci int version = ipc_parse_version(&cmd); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return ksys_msgctl(msqid, cmd, buf, version); 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ciSYSCALL_DEFINE3(old_msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci return ksys_old_msgctl(msqid, cmd, buf); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci#endif 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistruct compat_msqid_ds { 66262306a36Sopenharmony_ci struct compat_ipc_perm msg_perm; 66362306a36Sopenharmony_ci compat_uptr_t msg_first; 66462306a36Sopenharmony_ci compat_uptr_t msg_last; 66562306a36Sopenharmony_ci old_time32_t msg_stime; 66662306a36Sopenharmony_ci old_time32_t msg_rtime; 66762306a36Sopenharmony_ci old_time32_t msg_ctime; 66862306a36Sopenharmony_ci compat_ulong_t msg_lcbytes; 66962306a36Sopenharmony_ci compat_ulong_t msg_lqbytes; 67062306a36Sopenharmony_ci unsigned short msg_cbytes; 67162306a36Sopenharmony_ci unsigned short msg_qnum; 67262306a36Sopenharmony_ci unsigned short msg_qbytes; 67362306a36Sopenharmony_ci compat_ipc_pid_t msg_lspid; 67462306a36Sopenharmony_ci compat_ipc_pid_t msg_lrpid; 67562306a36Sopenharmony_ci}; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int copy_compat_msqid_from_user(struct msqid64_ds *out, void __user *buf, 67862306a36Sopenharmony_ci int version) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci memset(out, 0, sizeof(*out)); 68162306a36Sopenharmony_ci if (version == IPC_64) { 68262306a36Sopenharmony_ci struct compat_msqid64_ds __user *p = buf; 68362306a36Sopenharmony_ci if (get_compat_ipc64_perm(&out->msg_perm, &p->msg_perm)) 68462306a36Sopenharmony_ci return -EFAULT; 68562306a36Sopenharmony_ci if (get_user(out->msg_qbytes, &p->msg_qbytes)) 68662306a36Sopenharmony_ci return -EFAULT; 68762306a36Sopenharmony_ci } else { 68862306a36Sopenharmony_ci struct compat_msqid_ds __user *p = buf; 68962306a36Sopenharmony_ci if (get_compat_ipc_perm(&out->msg_perm, &p->msg_perm)) 69062306a36Sopenharmony_ci return -EFAULT; 69162306a36Sopenharmony_ci if (get_user(out->msg_qbytes, &p->msg_qbytes)) 69262306a36Sopenharmony_ci return -EFAULT; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci return 0; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, 69862306a36Sopenharmony_ci int version) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci if (version == IPC_64) { 70162306a36Sopenharmony_ci struct compat_msqid64_ds v; 70262306a36Sopenharmony_ci memset(&v, 0, sizeof(v)); 70362306a36Sopenharmony_ci to_compat_ipc64_perm(&v.msg_perm, &in->msg_perm); 70462306a36Sopenharmony_ci v.msg_stime = lower_32_bits(in->msg_stime); 70562306a36Sopenharmony_ci v.msg_stime_high = upper_32_bits(in->msg_stime); 70662306a36Sopenharmony_ci v.msg_rtime = lower_32_bits(in->msg_rtime); 70762306a36Sopenharmony_ci v.msg_rtime_high = upper_32_bits(in->msg_rtime); 70862306a36Sopenharmony_ci v.msg_ctime = lower_32_bits(in->msg_ctime); 70962306a36Sopenharmony_ci v.msg_ctime_high = upper_32_bits(in->msg_ctime); 71062306a36Sopenharmony_ci v.msg_cbytes = in->msg_cbytes; 71162306a36Sopenharmony_ci v.msg_qnum = in->msg_qnum; 71262306a36Sopenharmony_ci v.msg_qbytes = in->msg_qbytes; 71362306a36Sopenharmony_ci v.msg_lspid = in->msg_lspid; 71462306a36Sopenharmony_ci v.msg_lrpid = in->msg_lrpid; 71562306a36Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 71662306a36Sopenharmony_ci } else { 71762306a36Sopenharmony_ci struct compat_msqid_ds v; 71862306a36Sopenharmony_ci memset(&v, 0, sizeof(v)); 71962306a36Sopenharmony_ci to_compat_ipc_perm(&v.msg_perm, &in->msg_perm); 72062306a36Sopenharmony_ci v.msg_stime = in->msg_stime; 72162306a36Sopenharmony_ci v.msg_rtime = in->msg_rtime; 72262306a36Sopenharmony_ci v.msg_ctime = in->msg_ctime; 72362306a36Sopenharmony_ci v.msg_cbytes = in->msg_cbytes; 72462306a36Sopenharmony_ci v.msg_qnum = in->msg_qnum; 72562306a36Sopenharmony_ci v.msg_qbytes = in->msg_qbytes; 72662306a36Sopenharmony_ci v.msg_lspid = in->msg_lspid; 72762306a36Sopenharmony_ci v.msg_lrpid = in->msg_lrpid; 72862306a36Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic long compat_ksys_msgctl(int msqid, int cmd, void __user *uptr, int version) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct ipc_namespace *ns; 73562306a36Sopenharmony_ci int err; 73662306a36Sopenharmony_ci struct msqid64_ds msqid64; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (msqid < 0 || cmd < 0) 74162306a36Sopenharmony_ci return -EINVAL; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci switch (cmd & (~IPC_64)) { 74462306a36Sopenharmony_ci case IPC_INFO: 74562306a36Sopenharmony_ci case MSG_INFO: { 74662306a36Sopenharmony_ci struct msginfo msginfo; 74762306a36Sopenharmony_ci err = msgctl_info(ns, msqid, cmd, &msginfo); 74862306a36Sopenharmony_ci if (err < 0) 74962306a36Sopenharmony_ci return err; 75062306a36Sopenharmony_ci if (copy_to_user(uptr, &msginfo, sizeof(struct msginfo))) 75162306a36Sopenharmony_ci err = -EFAULT; 75262306a36Sopenharmony_ci return err; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci case IPC_STAT: 75562306a36Sopenharmony_ci case MSG_STAT: 75662306a36Sopenharmony_ci case MSG_STAT_ANY: 75762306a36Sopenharmony_ci err = msgctl_stat(ns, msqid, cmd, &msqid64); 75862306a36Sopenharmony_ci if (err < 0) 75962306a36Sopenharmony_ci return err; 76062306a36Sopenharmony_ci if (copy_compat_msqid_to_user(uptr, &msqid64, version)) 76162306a36Sopenharmony_ci err = -EFAULT; 76262306a36Sopenharmony_ci return err; 76362306a36Sopenharmony_ci case IPC_SET: 76462306a36Sopenharmony_ci if (copy_compat_msqid_from_user(&msqid64, uptr, version)) 76562306a36Sopenharmony_ci return -EFAULT; 76662306a36Sopenharmony_ci return msgctl_down(ns, msqid, cmd, &msqid64.msg_perm, msqid64.msg_qbytes); 76762306a36Sopenharmony_ci case IPC_RMID: 76862306a36Sopenharmony_ci return msgctl_down(ns, msqid, cmd, NULL, 0); 76962306a36Sopenharmony_ci default: 77062306a36Sopenharmony_ci return -EINVAL; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, void __user *, uptr) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci return compat_ksys_msgctl(msqid, cmd, uptr, IPC_64); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION 78062306a36Sopenharmony_cilong compat_ksys_old_msgctl(int msqid, int cmd, void __user *uptr) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci int version = compat_ipc_parse_version(&cmd); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return compat_ksys_msgctl(msqid, cmd, uptr, version); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(old_msgctl, int, msqid, int, cmd, void __user *, uptr) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci return compat_ksys_old_msgctl(msqid, cmd, uptr); 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci#endif 79262306a36Sopenharmony_ci#endif 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic int testmsg(struct msg_msg *msg, long type, int mode) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci switch (mode) { 79762306a36Sopenharmony_ci case SEARCH_ANY: 79862306a36Sopenharmony_ci case SEARCH_NUMBER: 79962306a36Sopenharmony_ci return 1; 80062306a36Sopenharmony_ci case SEARCH_LESSEQUAL: 80162306a36Sopenharmony_ci if (msg->m_type <= type) 80262306a36Sopenharmony_ci return 1; 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci case SEARCH_EQUAL: 80562306a36Sopenharmony_ci if (msg->m_type == type) 80662306a36Sopenharmony_ci return 1; 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci case SEARCH_NOTEQUAL: 80962306a36Sopenharmony_ci if (msg->m_type != type) 81062306a36Sopenharmony_ci return 1; 81162306a36Sopenharmony_ci break; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg, 81762306a36Sopenharmony_ci struct wake_q_head *wake_q) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct msg_receiver *msr, *t; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) { 82262306a36Sopenharmony_ci if (testmsg(msg, msr->r_msgtype, msr->r_mode) && 82362306a36Sopenharmony_ci !security_msg_queue_msgrcv(&msq->q_perm, msg, msr->r_tsk, 82462306a36Sopenharmony_ci msr->r_msgtype, msr->r_mode)) { 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci list_del(&msr->r_list); 82762306a36Sopenharmony_ci if (msr->r_maxsize < msg->m_ts) { 82862306a36Sopenharmony_ci wake_q_add(wake_q, msr->r_tsk); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* See expunge_all regarding memory barrier */ 83162306a36Sopenharmony_ci smp_store_release(&msr->r_msg, ERR_PTR(-E2BIG)); 83262306a36Sopenharmony_ci } else { 83362306a36Sopenharmony_ci ipc_update_pid(&msq->q_lrpid, task_pid(msr->r_tsk)); 83462306a36Sopenharmony_ci msq->q_rtime = ktime_get_real_seconds(); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci wake_q_add(wake_q, msr->r_tsk); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* See expunge_all regarding memory barrier */ 83962306a36Sopenharmony_ci smp_store_release(&msr->r_msg, msg); 84062306a36Sopenharmony_ci return 1; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic long do_msgsnd(int msqid, long mtype, void __user *mtext, 84962306a36Sopenharmony_ci size_t msgsz, int msgflg) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct msg_queue *msq; 85262306a36Sopenharmony_ci struct msg_msg *msg; 85362306a36Sopenharmony_ci int err; 85462306a36Sopenharmony_ci struct ipc_namespace *ns; 85562306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0) 86062306a36Sopenharmony_ci return -EINVAL; 86162306a36Sopenharmony_ci if (mtype < 1) 86262306a36Sopenharmony_ci return -EINVAL; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci msg = load_msg(mtext, msgsz); 86562306a36Sopenharmony_ci if (IS_ERR(msg)) 86662306a36Sopenharmony_ci return PTR_ERR(msg); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci msg->m_type = mtype; 86962306a36Sopenharmony_ci msg->m_ts = msgsz; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci rcu_read_lock(); 87262306a36Sopenharmony_ci msq = msq_obtain_object_check(ns, msqid); 87362306a36Sopenharmony_ci if (IS_ERR(msq)) { 87462306a36Sopenharmony_ci err = PTR_ERR(msq); 87562306a36Sopenharmony_ci goto out_unlock1; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci ipc_lock_object(&msq->q_perm); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci for (;;) { 88162306a36Sopenharmony_ci struct msg_sender s; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci err = -EACCES; 88462306a36Sopenharmony_ci if (ipcperms(ns, &msq->q_perm, S_IWUGO)) 88562306a36Sopenharmony_ci goto out_unlock0; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* raced with RMID? */ 88862306a36Sopenharmony_ci if (!ipc_valid_object(&msq->q_perm)) { 88962306a36Sopenharmony_ci err = -EIDRM; 89062306a36Sopenharmony_ci goto out_unlock0; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci err = security_msg_queue_msgsnd(&msq->q_perm, msg, msgflg); 89462306a36Sopenharmony_ci if (err) 89562306a36Sopenharmony_ci goto out_unlock0; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (msg_fits_inqueue(msq, msgsz)) 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* queue full, wait: */ 90162306a36Sopenharmony_ci if (msgflg & IPC_NOWAIT) { 90262306a36Sopenharmony_ci err = -EAGAIN; 90362306a36Sopenharmony_ci goto out_unlock0; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* enqueue the sender and prepare to block */ 90762306a36Sopenharmony_ci ss_add(msq, &s, msgsz); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (!ipc_rcu_getref(&msq->q_perm)) { 91062306a36Sopenharmony_ci err = -EIDRM; 91162306a36Sopenharmony_ci goto out_unlock0; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 91562306a36Sopenharmony_ci rcu_read_unlock(); 91662306a36Sopenharmony_ci schedule(); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci rcu_read_lock(); 91962306a36Sopenharmony_ci ipc_lock_object(&msq->q_perm); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci ipc_rcu_putref(&msq->q_perm, msg_rcu_free); 92262306a36Sopenharmony_ci /* raced with RMID? */ 92362306a36Sopenharmony_ci if (!ipc_valid_object(&msq->q_perm)) { 92462306a36Sopenharmony_ci err = -EIDRM; 92562306a36Sopenharmony_ci goto out_unlock0; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci ss_del(&s); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (signal_pending(current)) { 93062306a36Sopenharmony_ci err = -ERESTARTNOHAND; 93162306a36Sopenharmony_ci goto out_unlock0; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci ipc_update_pid(&msq->q_lspid, task_tgid(current)); 93762306a36Sopenharmony_ci msq->q_stime = ktime_get_real_seconds(); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (!pipelined_send(msq, msg, &wake_q)) { 94062306a36Sopenharmony_ci /* no one is waiting for this message, enqueue it */ 94162306a36Sopenharmony_ci list_add_tail(&msg->m_list, &msq->q_messages); 94262306a36Sopenharmony_ci msq->q_cbytes += msgsz; 94362306a36Sopenharmony_ci msq->q_qnum++; 94462306a36Sopenharmony_ci percpu_counter_add_local(&ns->percpu_msg_bytes, msgsz); 94562306a36Sopenharmony_ci percpu_counter_add_local(&ns->percpu_msg_hdrs, 1); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci err = 0; 94962306a36Sopenharmony_ci msg = NULL; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ciout_unlock0: 95262306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 95362306a36Sopenharmony_ci wake_up_q(&wake_q); 95462306a36Sopenharmony_ciout_unlock1: 95562306a36Sopenharmony_ci rcu_read_unlock(); 95662306a36Sopenharmony_ci if (msg != NULL) 95762306a36Sopenharmony_ci free_msg(msg); 95862306a36Sopenharmony_ci return err; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cilong ksys_msgsnd(int msqid, struct msgbuf __user *msgp, size_t msgsz, 96262306a36Sopenharmony_ci int msgflg) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci long mtype; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (get_user(mtype, &msgp->mtype)) 96762306a36Sopenharmony_ci return -EFAULT; 96862306a36Sopenharmony_ci return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ciSYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, 97262306a36Sopenharmony_ci int, msgflg) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci return ksys_msgsnd(msqid, msgp, msgsz, msgflg); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistruct compat_msgbuf { 98062306a36Sopenharmony_ci compat_long_t mtype; 98162306a36Sopenharmony_ci char mtext[1]; 98262306a36Sopenharmony_ci}; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cilong compat_ksys_msgsnd(int msqid, compat_uptr_t msgp, 98562306a36Sopenharmony_ci compat_ssize_t msgsz, int msgflg) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct compat_msgbuf __user *up = compat_ptr(msgp); 98862306a36Sopenharmony_ci compat_long_t mtype; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (get_user(mtype, &up->mtype)) 99162306a36Sopenharmony_ci return -EFAULT; 99262306a36Sopenharmony_ci return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg); 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp, 99662306a36Sopenharmony_ci compat_ssize_t, msgsz, int, msgflg) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci return compat_ksys_msgsnd(msqid, msgp, msgsz, msgflg); 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci#endif 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic inline int convert_mode(long *msgtyp, int msgflg) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci if (msgflg & MSG_COPY) 100562306a36Sopenharmony_ci return SEARCH_NUMBER; 100662306a36Sopenharmony_ci /* 100762306a36Sopenharmony_ci * find message of correct type. 100862306a36Sopenharmony_ci * msgtyp = 0 => get first. 100962306a36Sopenharmony_ci * msgtyp > 0 => get first message of matching type. 101062306a36Sopenharmony_ci * msgtyp < 0 => get message with least type must be < abs(msgtype). 101162306a36Sopenharmony_ci */ 101262306a36Sopenharmony_ci if (*msgtyp == 0) 101362306a36Sopenharmony_ci return SEARCH_ANY; 101462306a36Sopenharmony_ci if (*msgtyp < 0) { 101562306a36Sopenharmony_ci if (*msgtyp == LONG_MIN) /* -LONG_MIN is undefined */ 101662306a36Sopenharmony_ci *msgtyp = LONG_MAX; 101762306a36Sopenharmony_ci else 101862306a36Sopenharmony_ci *msgtyp = -*msgtyp; 101962306a36Sopenharmony_ci return SEARCH_LESSEQUAL; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci if (msgflg & MSG_EXCEPT) 102262306a36Sopenharmony_ci return SEARCH_NOTEQUAL; 102362306a36Sopenharmony_ci return SEARCH_EQUAL; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic long do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci struct msgbuf __user *msgp = dest; 102962306a36Sopenharmony_ci size_t msgsz; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (put_user(msg->m_type, &msgp->mtype)) 103262306a36Sopenharmony_ci return -EFAULT; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; 103562306a36Sopenharmony_ci if (store_msg(msgp->mtext, msg, msgsz)) 103662306a36Sopenharmony_ci return -EFAULT; 103762306a36Sopenharmony_ci return msgsz; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci#ifdef CONFIG_CHECKPOINT_RESTORE 104162306a36Sopenharmony_ci/* 104262306a36Sopenharmony_ci * This function creates new kernel message structure, large enough to store 104362306a36Sopenharmony_ci * bufsz message bytes. 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_cistatic inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct msg_msg *copy; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* 105062306a36Sopenharmony_ci * Create dummy message to copy real message to. 105162306a36Sopenharmony_ci */ 105262306a36Sopenharmony_ci copy = load_msg(buf, bufsz); 105362306a36Sopenharmony_ci if (!IS_ERR(copy)) 105462306a36Sopenharmony_ci copy->m_ts = bufsz; 105562306a36Sopenharmony_ci return copy; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic inline void free_copy(struct msg_msg *copy) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci if (copy) 106162306a36Sopenharmony_ci free_msg(copy); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci#else 106462306a36Sopenharmony_cistatic inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci return ERR_PTR(-ENOSYS); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic inline void free_copy(struct msg_msg *copy) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci#endif 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct msg_msg *msg, *found = NULL; 107762306a36Sopenharmony_ci long count = 0; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci list_for_each_entry(msg, &msq->q_messages, m_list) { 108062306a36Sopenharmony_ci if (testmsg(msg, *msgtyp, mode) && 108162306a36Sopenharmony_ci !security_msg_queue_msgrcv(&msq->q_perm, msg, current, 108262306a36Sopenharmony_ci *msgtyp, mode)) { 108362306a36Sopenharmony_ci if (mode == SEARCH_LESSEQUAL && msg->m_type != 1) { 108462306a36Sopenharmony_ci *msgtyp = msg->m_type - 1; 108562306a36Sopenharmony_ci found = msg; 108662306a36Sopenharmony_ci } else if (mode == SEARCH_NUMBER) { 108762306a36Sopenharmony_ci if (*msgtyp == count) 108862306a36Sopenharmony_ci return msg; 108962306a36Sopenharmony_ci } else 109062306a36Sopenharmony_ci return msg; 109162306a36Sopenharmony_ci count++; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return found ?: ERR_PTR(-EAGAIN); 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_cistatic long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, 109962306a36Sopenharmony_ci long (*msg_handler)(void __user *, struct msg_msg *, size_t)) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci int mode; 110262306a36Sopenharmony_ci struct msg_queue *msq; 110362306a36Sopenharmony_ci struct ipc_namespace *ns; 110462306a36Sopenharmony_ci struct msg_msg *msg, *copy = NULL; 110562306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (msqid < 0 || (long) bufsz < 0) 111062306a36Sopenharmony_ci return -EINVAL; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (msgflg & MSG_COPY) { 111362306a36Sopenharmony_ci if ((msgflg & MSG_EXCEPT) || !(msgflg & IPC_NOWAIT)) 111462306a36Sopenharmony_ci return -EINVAL; 111562306a36Sopenharmony_ci copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax)); 111662306a36Sopenharmony_ci if (IS_ERR(copy)) 111762306a36Sopenharmony_ci return PTR_ERR(copy); 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci mode = convert_mode(&msgtyp, msgflg); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci rcu_read_lock(); 112262306a36Sopenharmony_ci msq = msq_obtain_object_check(ns, msqid); 112362306a36Sopenharmony_ci if (IS_ERR(msq)) { 112462306a36Sopenharmony_ci rcu_read_unlock(); 112562306a36Sopenharmony_ci free_copy(copy); 112662306a36Sopenharmony_ci return PTR_ERR(msq); 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci for (;;) { 113062306a36Sopenharmony_ci struct msg_receiver msr_d; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci msg = ERR_PTR(-EACCES); 113362306a36Sopenharmony_ci if (ipcperms(ns, &msq->q_perm, S_IRUGO)) 113462306a36Sopenharmony_ci goto out_unlock1; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci ipc_lock_object(&msq->q_perm); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* raced with RMID? */ 113962306a36Sopenharmony_ci if (!ipc_valid_object(&msq->q_perm)) { 114062306a36Sopenharmony_ci msg = ERR_PTR(-EIDRM); 114162306a36Sopenharmony_ci goto out_unlock0; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci msg = find_msg(msq, &msgtyp, mode); 114562306a36Sopenharmony_ci if (!IS_ERR(msg)) { 114662306a36Sopenharmony_ci /* 114762306a36Sopenharmony_ci * Found a suitable message. 114862306a36Sopenharmony_ci * Unlink it from the queue. 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { 115162306a36Sopenharmony_ci msg = ERR_PTR(-E2BIG); 115262306a36Sopenharmony_ci goto out_unlock0; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci /* 115562306a36Sopenharmony_ci * If we are copying, then do not unlink message and do 115662306a36Sopenharmony_ci * not update queue parameters. 115762306a36Sopenharmony_ci */ 115862306a36Sopenharmony_ci if (msgflg & MSG_COPY) { 115962306a36Sopenharmony_ci msg = copy_msg(msg, copy); 116062306a36Sopenharmony_ci goto out_unlock0; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci list_del(&msg->m_list); 116462306a36Sopenharmony_ci msq->q_qnum--; 116562306a36Sopenharmony_ci msq->q_rtime = ktime_get_real_seconds(); 116662306a36Sopenharmony_ci ipc_update_pid(&msq->q_lrpid, task_tgid(current)); 116762306a36Sopenharmony_ci msq->q_cbytes -= msg->m_ts; 116862306a36Sopenharmony_ci percpu_counter_sub_local(&ns->percpu_msg_bytes, msg->m_ts); 116962306a36Sopenharmony_ci percpu_counter_sub_local(&ns->percpu_msg_hdrs, 1); 117062306a36Sopenharmony_ci ss_wakeup(msq, &wake_q, false); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci goto out_unlock0; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci /* No message waiting. Wait for a message */ 117662306a36Sopenharmony_ci if (msgflg & IPC_NOWAIT) { 117762306a36Sopenharmony_ci msg = ERR_PTR(-ENOMSG); 117862306a36Sopenharmony_ci goto out_unlock0; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci list_add_tail(&msr_d.r_list, &msq->q_receivers); 118262306a36Sopenharmony_ci msr_d.r_tsk = current; 118362306a36Sopenharmony_ci msr_d.r_msgtype = msgtyp; 118462306a36Sopenharmony_ci msr_d.r_mode = mode; 118562306a36Sopenharmony_ci if (msgflg & MSG_NOERROR) 118662306a36Sopenharmony_ci msr_d.r_maxsize = INT_MAX; 118762306a36Sopenharmony_ci else 118862306a36Sopenharmony_ci msr_d.r_maxsize = bufsz; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* memory barrier not require due to ipc_lock_object() */ 119162306a36Sopenharmony_ci WRITE_ONCE(msr_d.r_msg, ERR_PTR(-EAGAIN)); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* memory barrier not required, we own ipc_lock_object() */ 119462306a36Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 119762306a36Sopenharmony_ci rcu_read_unlock(); 119862306a36Sopenharmony_ci schedule(); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci /* 120162306a36Sopenharmony_ci * Lockless receive, part 1: 120262306a36Sopenharmony_ci * We don't hold a reference to the queue and getting a 120362306a36Sopenharmony_ci * reference would defeat the idea of a lockless operation, 120462306a36Sopenharmony_ci * thus the code relies on rcu to guarantee the existence of 120562306a36Sopenharmony_ci * msq: 120662306a36Sopenharmony_ci * Prior to destruction, expunge_all(-EIRDM) changes r_msg. 120762306a36Sopenharmony_ci * Thus if r_msg is -EAGAIN, then the queue not yet destroyed. 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_ci rcu_read_lock(); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * Lockless receive, part 2: 121362306a36Sopenharmony_ci * The work in pipelined_send() and expunge_all(): 121462306a36Sopenharmony_ci * - Set pointer to message 121562306a36Sopenharmony_ci * - Queue the receiver task for later wakeup 121662306a36Sopenharmony_ci * - Wake up the process after the lock is dropped. 121762306a36Sopenharmony_ci * 121862306a36Sopenharmony_ci * Should the process wake up before this wakeup (due to a 121962306a36Sopenharmony_ci * signal) it will either see the message and continue ... 122062306a36Sopenharmony_ci */ 122162306a36Sopenharmony_ci msg = READ_ONCE(msr_d.r_msg); 122262306a36Sopenharmony_ci if (msg != ERR_PTR(-EAGAIN)) { 122362306a36Sopenharmony_ci /* see MSG_BARRIER for purpose/pairing */ 122462306a36Sopenharmony_ci smp_acquire__after_ctrl_dep(); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci goto out_unlock1; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* 123062306a36Sopenharmony_ci * ... or see -EAGAIN, acquire the lock to check the message 123162306a36Sopenharmony_ci * again. 123262306a36Sopenharmony_ci */ 123362306a36Sopenharmony_ci ipc_lock_object(&msq->q_perm); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci msg = READ_ONCE(msr_d.r_msg); 123662306a36Sopenharmony_ci if (msg != ERR_PTR(-EAGAIN)) 123762306a36Sopenharmony_ci goto out_unlock0; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci list_del(&msr_d.r_list); 124062306a36Sopenharmony_ci if (signal_pending(current)) { 124162306a36Sopenharmony_ci msg = ERR_PTR(-ERESTARTNOHAND); 124262306a36Sopenharmony_ci goto out_unlock0; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ciout_unlock0: 124962306a36Sopenharmony_ci ipc_unlock_object(&msq->q_perm); 125062306a36Sopenharmony_ci wake_up_q(&wake_q); 125162306a36Sopenharmony_ciout_unlock1: 125262306a36Sopenharmony_ci rcu_read_unlock(); 125362306a36Sopenharmony_ci if (IS_ERR(msg)) { 125462306a36Sopenharmony_ci free_copy(copy); 125562306a36Sopenharmony_ci return PTR_ERR(msg); 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci bufsz = msg_handler(buf, msg, bufsz); 125962306a36Sopenharmony_ci free_msg(msg); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci return bufsz; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cilong ksys_msgrcv(int msqid, struct msgbuf __user *msgp, size_t msgsz, 126562306a36Sopenharmony_ci long msgtyp, int msgflg) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill); 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ciSYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, 127162306a36Sopenharmony_ci long, msgtyp, int, msgflg) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci return ksys_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg); 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 127762306a36Sopenharmony_cistatic long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci struct compat_msgbuf __user *msgp = dest; 128062306a36Sopenharmony_ci size_t msgsz; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (put_user(msg->m_type, &msgp->mtype)) 128362306a36Sopenharmony_ci return -EFAULT; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; 128662306a36Sopenharmony_ci if (store_msg(msgp->mtext, msg, msgsz)) 128762306a36Sopenharmony_ci return -EFAULT; 128862306a36Sopenharmony_ci return msgsz; 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_cilong compat_ksys_msgrcv(int msqid, compat_uptr_t msgp, compat_ssize_t msgsz, 129262306a36Sopenharmony_ci compat_long_t msgtyp, int msgflg) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp, 129562306a36Sopenharmony_ci msgflg, compat_do_msg_fill); 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp, 129962306a36Sopenharmony_ci compat_ssize_t, msgsz, compat_long_t, msgtyp, 130062306a36Sopenharmony_ci int, msgflg) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci return compat_ksys_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg); 130362306a36Sopenharmony_ci} 130462306a36Sopenharmony_ci#endif 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ciint msg_init_ns(struct ipc_namespace *ns) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci int ret; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci ns->msg_ctlmax = MSGMAX; 131162306a36Sopenharmony_ci ns->msg_ctlmnb = MSGMNB; 131262306a36Sopenharmony_ci ns->msg_ctlmni = MSGMNI; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci ret = percpu_counter_init(&ns->percpu_msg_bytes, 0, GFP_KERNEL); 131562306a36Sopenharmony_ci if (ret) 131662306a36Sopenharmony_ci goto fail_msg_bytes; 131762306a36Sopenharmony_ci ret = percpu_counter_init(&ns->percpu_msg_hdrs, 0, GFP_KERNEL); 131862306a36Sopenharmony_ci if (ret) 131962306a36Sopenharmony_ci goto fail_msg_hdrs; 132062306a36Sopenharmony_ci ipc_init_ids(&ns->ids[IPC_MSG_IDS]); 132162306a36Sopenharmony_ci return 0; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cifail_msg_hdrs: 132462306a36Sopenharmony_ci percpu_counter_destroy(&ns->percpu_msg_bytes); 132562306a36Sopenharmony_cifail_msg_bytes: 132662306a36Sopenharmony_ci return ret; 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci#ifdef CONFIG_IPC_NS 133062306a36Sopenharmony_civoid msg_exit_ns(struct ipc_namespace *ns) 133162306a36Sopenharmony_ci{ 133262306a36Sopenharmony_ci free_ipcs(ns, &msg_ids(ns), freeque); 133362306a36Sopenharmony_ci idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr); 133462306a36Sopenharmony_ci rhashtable_destroy(&ns->ids[IPC_MSG_IDS].key_ht); 133562306a36Sopenharmony_ci percpu_counter_destroy(&ns->percpu_msg_bytes); 133662306a36Sopenharmony_ci percpu_counter_destroy(&ns->percpu_msg_hdrs); 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci#endif 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 134162306a36Sopenharmony_cistatic int sysvipc_msg_proc_show(struct seq_file *s, void *it) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci struct pid_namespace *pid_ns = ipc_seq_pid_ns(s); 134462306a36Sopenharmony_ci struct user_namespace *user_ns = seq_user_ns(s); 134562306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = it; 134662306a36Sopenharmony_ci struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci seq_printf(s, 134962306a36Sopenharmony_ci "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10llu %10llu %10llu\n", 135062306a36Sopenharmony_ci msq->q_perm.key, 135162306a36Sopenharmony_ci msq->q_perm.id, 135262306a36Sopenharmony_ci msq->q_perm.mode, 135362306a36Sopenharmony_ci msq->q_cbytes, 135462306a36Sopenharmony_ci msq->q_qnum, 135562306a36Sopenharmony_ci pid_nr_ns(msq->q_lspid, pid_ns), 135662306a36Sopenharmony_ci pid_nr_ns(msq->q_lrpid, pid_ns), 135762306a36Sopenharmony_ci from_kuid_munged(user_ns, msq->q_perm.uid), 135862306a36Sopenharmony_ci from_kgid_munged(user_ns, msq->q_perm.gid), 135962306a36Sopenharmony_ci from_kuid_munged(user_ns, msq->q_perm.cuid), 136062306a36Sopenharmony_ci from_kgid_munged(user_ns, msq->q_perm.cgid), 136162306a36Sopenharmony_ci msq->q_stime, 136262306a36Sopenharmony_ci msq->q_rtime, 136362306a36Sopenharmony_ci msq->q_ctime); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci return 0; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci#endif 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_civoid __init msg_init(void) 137062306a36Sopenharmony_ci{ 137162306a36Sopenharmony_ci msg_init_ns(&init_ipc_ns); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci ipc_init_proc_interface("sysvipc/msg", 137462306a36Sopenharmony_ci " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n", 137562306a36Sopenharmony_ci IPC_MSG_IDS, sysvipc_msg_proc_show); 137662306a36Sopenharmony_ci} 1377