msg.c 27.4 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
Linus Torvalds's avatar
Linus Torvalds committed
2 3
/*
 * linux/ipc/msg.c
4
 * Copyright (C) 1992 Krishna Balasubramanian
Linus Torvalds's avatar
Linus Torvalds committed
5 6 7 8 9 10 11 12 13 14 15
 *
 * Removed all the remaining kerneld mess
 * Catch the -EFAULT stuff properly
 * Use GFP_KERNEL for messages as in 1.2
 * Fixed up the unchecked user space derefs
 * Copyright (C) 1998 Alan Cox & Andi Kleen
 *
 * /proc/sysvipc/msg support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
 *
 * mostly rewritten, threaded and wake-one semantics added
 * MSGMAX limit removed, sysctl's added
16
 * (c) 1999 Manfred Spraul <manfred@colorfullife.com>
17 18 19
 *
 * support for audit of ipc object properties and permission changes
 * Dustin Kirkland <dustin.kirkland@us.ibm.com>
20 21 22 23
 *
 * namespaces support
 * OpenVZ, SWsoft Inc.
 * Pavel Emelianov <xemul@openvz.org>
Linus Torvalds's avatar
Linus Torvalds committed
24 25
 */

26
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
27 28 29
#include <linux/msg.h>
#include <linux/spinlock.h>
#include <linux/init.h>
30
#include <linux/mm.h>
Linus Torvalds's avatar
Linus Torvalds committed
31 32 33
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/security.h>
34
#include <linux/sched/wake_q.h>
Linus Torvalds's avatar
Linus Torvalds committed
35 36
#include <linux/syscalls.h>
#include <linux/audit.h>
37
#include <linux/seq_file.h>
Nadia Derbey's avatar
Nadia Derbey committed
38
#include <linux/rwsem.h>
39
#include <linux/nsproxy.h>
40
#include <linux/ipc_namespace.h>
41

Linus Torvalds's avatar
Linus Torvalds committed
42
#include <asm/current.h>
43
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
44 45
#include "util.h"

46
/* one msg_receiver structure for each sleeping receiver */
Linus Torvalds's avatar
Linus Torvalds committed
47
struct msg_receiver {
48 49
	struct list_head	r_list;
	struct task_struct	*r_tsk;
Linus Torvalds's avatar
Linus Torvalds committed
50

51 52 53
	int			r_mode;
	long			r_msgtype;
	long			r_maxsize;
Linus Torvalds's avatar
Linus Torvalds committed
54

55
	struct msg_msg		*r_msg;
Linus Torvalds's avatar
Linus Torvalds committed
56 57 58 59
};

/* one msg_sender for each sleeping sender */
struct msg_sender {
60 61
	struct list_head	list;
	struct task_struct	*tsk;
62
	size_t                  msgsz;
Linus Torvalds's avatar
Linus Torvalds committed
63 64 65 66 67 68
};

#define SEARCH_ANY		1
#define SEARCH_EQUAL		2
#define SEARCH_NOTEQUAL		3
#define SEARCH_LESSEQUAL	4
69
#define SEARCH_NUMBER		5
Linus Torvalds's avatar
Linus Torvalds committed
70

71
#define msg_ids(ns)	((ns)->ids[IPC_MSG_IDS])
Linus Torvalds's avatar
Linus Torvalds committed
72

73 74
static inline struct msg_queue *msq_obtain_object(struct ipc_namespace *ns, int id)
{
75
	struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&msg_ids(ns), id);
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

	if (IS_ERR(ipcp))
		return ERR_CAST(ipcp);

	return container_of(ipcp, struct msg_queue, q_perm);
}

static inline struct msg_queue *msq_obtain_object_check(struct ipc_namespace *ns,
							int id)
{
	struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&msg_ids(ns), id);

	if (IS_ERR(ipcp))
		return ERR_CAST(ipcp);

	return container_of(ipcp, struct msg_queue, q_perm);
}

Nadia Derbey's avatar
Nadia Derbey committed
94 95 96 97 98
static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
{
	ipc_rmid(&msg_ids(ns), &s->q_perm);
}

99 100
static void msg_rcu_free(struct rcu_head *head)
{
101 102
	struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu);
	struct msg_queue *msq = container_of(p, struct msg_queue, q_perm);
103 104

	security_msg_queue_free(msq);
105
	kvfree(msq);
106 107
}

Nadia Derbey's avatar
Nadia Derbey committed
108 109 110 111 112
/**
 * newque - Create a new msg queue
 * @ns: namespace
 * @params: ptr to the structure that contains the key and msgflg
 *
113
 * Called with msg_ids.rwsem held (writer)
Nadia Derbey's avatar
Nadia Derbey committed
114
 */
115
static int newque(struct ipc_namespace *ns, struct ipc_params *params)
Linus Torvalds's avatar
Linus Torvalds committed
116 117
{
	struct msg_queue *msq;
118
	int retval;
119 120
	key_t key = params->key;
	int msgflg = params->flg;
Linus Torvalds's avatar
Linus Torvalds committed
121

122 123
	msq = kvmalloc(sizeof(*msq), GFP_KERNEL);
	if (unlikely(!msq))
Linus Torvalds's avatar
Linus Torvalds committed
124 125
		return -ENOMEM;

126
	msq->q_perm.mode = msgflg & S_IRWXUGO;
Linus Torvalds's avatar
Linus Torvalds committed
127 128 129 130 131
	msq->q_perm.key = key;

	msq->q_perm.security = NULL;
	retval = security_msg_queue_alloc(msq);
	if (retval) {
132
		kvfree(msq);
Linus Torvalds's avatar
Linus Torvalds committed
133 134 135 136
		return retval;
	}

	msq->q_stime = msq->q_rtime = 0;
137
	msq->q_ctime = ktime_get_real_seconds();
Linus Torvalds's avatar
Linus Torvalds committed
138
	msq->q_cbytes = msq->q_qnum = 0;
139
	msq->q_qbytes = ns->msg_ctlmnb;
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142 143
	msq->q_lspid = msq->q_lrpid = 0;
	INIT_LIST_HEAD(&msq->q_messages);
	INIT_LIST_HEAD(&msq->q_receivers);
	INIT_LIST_HEAD(&msq->q_senders);
Nadia Derbey's avatar
Nadia Derbey committed
144

145
	/* ipc_addid() locks msq upon success. */
146 147 148 149
	retval = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
	if (retval < 0) {
		call_rcu(&msq->q_perm.rcu, msg_rcu_free);
		return retval;
150 151
	}

152
	ipc_unlock_object(&msq->q_perm);
153
	rcu_read_unlock();
Linus Torvalds's avatar
Linus Torvalds committed
154

Nadia Derbey's avatar
Nadia Derbey committed
155
	return msq->q_perm.id;
Linus Torvalds's avatar
Linus Torvalds committed
156 157
}

158 159 160 161 162 163 164 165
static inline bool msg_fits_inqueue(struct msg_queue *msq, size_t msgsz)
{
	return msgsz + msq->q_cbytes <= msq->q_qbytes &&
		1 + msq->q_qnum <= msq->q_qbytes;
}

static inline void ss_add(struct msg_queue *msq,
			  struct msg_sender *mss, size_t msgsz)
Linus Torvalds's avatar
Linus Torvalds committed
166
{
167
	mss->tsk = current;
168
	mss->msgsz = msgsz;
169
	__set_current_state(TASK_INTERRUPTIBLE);
170
	list_add_tail(&mss->list, &msq->q_senders);
Linus Torvalds's avatar
Linus Torvalds committed
171 172
}

173
static inline void ss_del(struct msg_sender *mss)
Linus Torvalds's avatar
Linus Torvalds committed
174
{
175
	if (mss->list.next)
Linus Torvalds's avatar
Linus Torvalds committed
176 177 178
		list_del(&mss->list);
}

179
static void ss_wakeup(struct msg_queue *msq,
180
		      struct wake_q_head *wake_q, bool kill)
Linus Torvalds's avatar
Linus Torvalds committed
181
{
182
	struct msg_sender *mss, *t;
183 184
	struct task_struct *stop_tsk = NULL;
	struct list_head *h = &msq->q_senders;
Linus Torvalds's avatar
Linus Torvalds committed
185

186
	list_for_each_entry_safe(mss, t, h, list) {
187 188
		if (kill)
			mss->list.next = NULL;
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

		/*
		 * Stop at the first task we don't wakeup,
		 * we've already iterated the original
		 * sender queue.
		 */
		else if (stop_tsk == mss->tsk)
			break;
		/*
		 * We are not in an EIDRM scenario here, therefore
		 * verify that we really need to wakeup the task.
		 * To maintain current semantics and wakeup order,
		 * move the sender to the tail on behalf of the
		 * blocked task.
		 */
		else if (!msg_fits_inqueue(msq, mss->msgsz)) {
			if (!stop_tsk)
				stop_tsk = mss->tsk;

			list_move_tail(&mss->list, &msq->q_senders);
			continue;
		}

212
		wake_q_add(wake_q, mss->tsk);
Linus Torvalds's avatar
Linus Torvalds committed
213 214 215
	}
}

216 217
static void expunge_all(struct msg_queue *msq, int res,
			struct wake_q_head *wake_q)
Linus Torvalds's avatar
Linus Torvalds committed
218
{
219
	struct msg_receiver *msr, *t;
220

221
	list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
222 223
		wake_q_add(wake_q, msr->r_tsk);
		WRITE_ONCE(msr->r_msg, ERR_PTR(res));
Linus Torvalds's avatar
Linus Torvalds committed
224 225
	}
}
226 227 228

/*
 * freeque() wakes up waiters on the sender and receiver waiting queue,
Nadia Derbey's avatar
Nadia Derbey committed
229 230
 * removes the message queue from message queue ID IDR, and cleans up all the
 * messages associated with this queue.
Linus Torvalds's avatar
Linus Torvalds committed
231
 *
232 233
 * msg_ids.rwsem (writer) and the spinlock for this message queue are held
 * before freeque() is called. msg_ids.rwsem remains locked on exit.
Linus Torvalds's avatar
Linus Torvalds committed
234
 */
235
static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
Linus Torvalds's avatar
Linus Torvalds committed
236
{
237
	struct msg_msg *msg, *t;
238
	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
239
	DEFINE_WAKE_Q(wake_q);
Linus Torvalds's avatar
Linus Torvalds committed
240

241
	expunge_all(msq, -EIDRM, &wake_q);
242
	ss_wakeup(msq, &wake_q, true);
Nadia Derbey's avatar
Nadia Derbey committed
243
	msg_rmid(ns, msq);
244
	ipc_unlock_object(&msq->q_perm);
245
	wake_up_q(&wake_q);
246
	rcu_read_unlock();
247

248
	list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) {
249
		atomic_dec(&ns->msg_hdrs);
Linus Torvalds's avatar
Linus Torvalds committed
250 251
		free_msg(msg);
	}
252
	atomic_sub(msq->q_cbytes, &ns->msg_bytes);
253
	ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
Linus Torvalds's avatar
Linus Torvalds committed
254 255
}

Nadia Derbey's avatar
Nadia Derbey committed
256
/*
257
 * Called with msg_ids.rwsem and ipcp locked.
Nadia Derbey's avatar
Nadia Derbey committed
258
 */
Nadia Derbey's avatar
Nadia Derbey committed
259
static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg)
260
{
Nadia Derbey's avatar
Nadia Derbey committed
261 262 263
	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);

	return security_msg_queue_associate(msq, msgflg);
264 265
}

266
SYSCALL_DEFINE2(msgget, key_t, key, int, msgflg)
Linus Torvalds's avatar
Linus Torvalds committed
267
{
268
	struct ipc_namespace *ns;
Mathias Krause's avatar
Mathias Krause committed
269 270 271 272
	static const struct ipc_ops msg_ops = {
		.getnew = newque,
		.associate = msg_security,
	};
273
	struct ipc_params msg_params;
274 275

	ns = current->nsproxy->ipc_ns;
Nadia Derbey's avatar
Nadia Derbey committed
276

277 278
	msg_params.key = key;
	msg_params.flg = msgflg;
279

280
	return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);
Linus Torvalds's avatar
Linus Torvalds committed
281 282
}

283 284
static inline unsigned long
copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
Linus Torvalds's avatar
Linus Torvalds committed
285
{
Manfred Spraul's avatar
Manfred Spraul committed
286
	switch (version) {
Linus Torvalds's avatar
Linus Torvalds committed
287
	case IPC_64:
288
		return copy_to_user(buf, in, sizeof(*in));
Linus Torvalds's avatar
Linus Torvalds committed
289
	case IPC_OLD:
290
	{
Linus Torvalds's avatar
Linus Torvalds committed
291 292
		struct msqid_ds out;

293
		memset(&out, 0, sizeof(out));
Linus Torvalds's avatar
Linus Torvalds committed
294 295 296 297 298 299 300

		ipc64_perm_to_ipc_perm(&in->msg_perm, &out.msg_perm);

		out.msg_stime		= in->msg_stime;
		out.msg_rtime		= in->msg_rtime;
		out.msg_ctime		= in->msg_ctime;

301 302
		if (in->msg_cbytes > USHRT_MAX)
			out.msg_cbytes	= USHRT_MAX;
Linus Torvalds's avatar
Linus Torvalds committed
303 304 305 306
		else
			out.msg_cbytes	= in->msg_cbytes;
		out.msg_lcbytes		= in->msg_cbytes;

307 308
		if (in->msg_qnum > USHRT_MAX)
			out.msg_qnum	= USHRT_MAX;
Linus Torvalds's avatar
Linus Torvalds committed
309 310 311
		else
			out.msg_qnum	= in->msg_qnum;

312 313
		if (in->msg_qbytes > USHRT_MAX)
			out.msg_qbytes	= USHRT_MAX;
Linus Torvalds's avatar
Linus Torvalds committed
314 315 316 317 318 319 320
		else
			out.msg_qbytes	= in->msg_qbytes;
		out.msg_lqbytes		= in->msg_qbytes;

		out.msg_lspid		= in->msg_lspid;
		out.msg_lrpid		= in->msg_lrpid;

321 322
		return copy_to_user(buf, &out, sizeof(out));
	}
Linus Torvalds's avatar
Linus Torvalds committed
323 324 325 326 327
	default:
		return -EINVAL;
	}
}

328
static inline unsigned long
329
copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version)
Linus Torvalds's avatar
Linus Torvalds committed
330
{
Manfred Spraul's avatar
Manfred Spraul committed
331
	switch (version) {
Linus Torvalds's avatar
Linus Torvalds committed
332
	case IPC_64:
333
		if (copy_from_user(out, buf, sizeof(*out)))
Linus Torvalds's avatar
Linus Torvalds committed
334 335 336
			return -EFAULT;
		return 0;
	case IPC_OLD:
337
	{
Linus Torvalds's avatar
Linus Torvalds committed
338 339
		struct msqid_ds tbuf_old;

340
		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
Linus Torvalds's avatar
Linus Torvalds committed
341 342
			return -EFAULT;

Manfred Spraul's avatar
Manfred Spraul committed
343 344 345
		out->msg_perm.uid	= tbuf_old.msg_perm.uid;
		out->msg_perm.gid	= tbuf_old.msg_perm.gid;
		out->msg_perm.mode	= tbuf_old.msg_perm.mode;
Linus Torvalds's avatar
Linus Torvalds committed
346

347
		if (tbuf_old.msg_qbytes == 0)
348
			out->msg_qbytes	= tbuf_old.msg_lqbytes;
Linus Torvalds's avatar
Linus Torvalds committed
349
		else
350
			out->msg_qbytes	= tbuf_old.msg_qbytes;
Linus Torvalds's avatar
Linus Torvalds committed
351 352

		return 0;
353
	}
Linus Torvalds's avatar
Linus Torvalds committed
354 355 356 357 358
	default:
		return -EINVAL;
	}
}

359
/*
360
 * This function handles some msgctl commands which require the rwsem
361
 * to be held in write mode.
362
 * NOTE: no locks must be held, the rwsem is taken inside this function.
363 364
 */
static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
365
			struct msqid64_ds *msqid64)
Linus Torvalds's avatar
Linus Torvalds committed
366 367
{
	struct kern_ipc_perm *ipcp;
368 369 370
	struct msg_queue *msq;
	int err;

371
	down_write(&msg_ids(ns).rwsem);
372 373
	rcu_read_lock();

374
	ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd,
375
				      &msqid64->msg_perm, msqid64->msg_qbytes);
376 377 378 379
	if (IS_ERR(ipcp)) {
		err = PTR_ERR(ipcp);
		goto out_unlock1;
	}
380

381
	msq = container_of(ipcp, struct msg_queue, q_perm);
382 383 384

	err = security_msg_queue_msgctl(msq, cmd);
	if (err)
385
		goto out_unlock1;
386 387 388

	switch (cmd) {
	case IPC_RMID:
389
		ipc_lock_object(&msq->q_perm);
390
		/* freeque unlocks the ipc object and rcu */
391 392 393
		freeque(ns, ipcp);
		goto out_up;
	case IPC_SET:
394
	{
395
		DEFINE_WAKE_Q(wake_q);
396

397
		if (msqid64->msg_qbytes > ns->msg_ctlmnb &&
398 399
		    !capable(CAP_SYS_RESOURCE)) {
			err = -EPERM;
400
			goto out_unlock1;
401 402
		}

403
		ipc_lock_object(&msq->q_perm);
404
		err = ipc_update_perm(&msqid64->msg_perm, ipcp);
405
		if (err)
406
			goto out_unlock0;
407

408
		msq->q_qbytes = msqid64->msg_qbytes;
409

410
		msq->q_ctime = ktime_get_real_seconds();
411 412
		/*
		 * Sleeping receivers might be excluded by
413 414
		 * stricter permissions.
		 */
415
		expunge_all(msq, -EAGAIN, &wake_q);
416 417
		/*
		 * Sleeping senders might be able to send
418 419
		 * due to a larger queue size.
		 */
420
		ss_wakeup(msq, &wake_q, false);
421 422 423 424 425
		ipc_unlock_object(&msq->q_perm);
		wake_up_q(&wake_q);

		goto out_unlock1;
	}
426 427
	default:
		err = -EINVAL;
428
		goto out_unlock1;
429
	}
430 431 432 433 434

out_unlock0:
	ipc_unlock_object(&msq->q_perm);
out_unlock1:
	rcu_read_unlock();
435
out_up:
436
	up_write(&msg_ids(ns).rwsem);
437 438 439
	return err;
}

440 441
static int msgctl_info(struct ipc_namespace *ns, int msqid,
			 int cmd, struct msginfo *msginfo)
442
{
443
	int err;
444
	int max_id;
Linus Torvalds's avatar
Linus Torvalds committed
445

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
	/*
	 * We must not return kernel stack data.
	 * due to padding, it's not enough
	 * to set all member fields.
	 */
	err = security_msg_queue_msgctl(NULL, cmd);
	if (err)
		return err;

	memset(msginfo, 0, sizeof(*msginfo));
	msginfo->msgmni = ns->msg_ctlmni;
	msginfo->msgmax = ns->msg_ctlmax;
	msginfo->msgmnb = ns->msg_ctlmnb;
	msginfo->msgssz = MSGSSZ;
	msginfo->msgseg = MSGSEG;
	down_read(&msg_ids(ns).rwsem);
	if (cmd == MSG_INFO) {
		msginfo->msgpool = msg_ids(ns).in_use;
		msginfo->msgmap = atomic_read(&ns->msg_hdrs);
		msginfo->msgtql = atomic_read(&ns->msg_bytes);
	} else {
		msginfo->msgmap = MSGMAP;
		msginfo->msgpool = MSGPOOL;
		msginfo->msgtql = MSGTQL;
Linus Torvalds's avatar
Linus Torvalds committed
470
	}
471 472 473 474
	max_id = ipc_get_maxid(&msg_ids(ns));
	up_read(&msg_ids(ns).rwsem);
	return (max_id < 0) ? 0 : max_id;
}
475

476 477 478 479 480 481
static int msgctl_stat(struct ipc_namespace *ns, int msqid,
			 int cmd, struct msqid64_ds *p)
{
	int err;
	struct msg_queue *msq;
	int success_return;
482

483
	memset(p, 0, sizeof(*p));
484

485 486 487 488 489
	rcu_read_lock();
	if (cmd == MSG_STAT) {
		msq = msq_obtain_object(ns, msqid);
		if (IS_ERR(msq)) {
			err = PTR_ERR(msq);
Linus Torvalds's avatar
Linus Torvalds committed
490 491
			goto out_unlock;
		}
492 493 494 495 496
		success_return = msq->q_perm.id;
	} else {
		msq = msq_obtain_object_check(ns, msqid);
		if (IS_ERR(msq)) {
			err = PTR_ERR(msq);
Linus Torvalds's avatar
Linus Torvalds committed
497
			goto out_unlock;
498 499 500
		}
		success_return = 0;
	}
Linus Torvalds's avatar
Linus Torvalds committed
501

502 503 504
	err = -EACCES;
	if (ipcperms(ns, &msq->q_perm, S_IRUGO))
		goto out_unlock;
505

506 507 508 509 510 511 512 513 514 515 516 517 518 519
	err = security_msg_queue_msgctl(msq, cmd);
	if (err)
		goto out_unlock;

	kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm);
	p->msg_stime  = msq->q_stime;
	p->msg_rtime  = msq->q_rtime;
	p->msg_ctime  = msq->q_ctime;
	p->msg_cbytes = msq->q_cbytes;
	p->msg_qnum   = msq->q_qnum;
	p->msg_qbytes = msq->q_qbytes;
	p->msg_lspid  = msq->q_lspid;
	p->msg_lrpid  = msq->q_lrpid;
	rcu_read_unlock();
520

521
	return success_return;
Linus Torvalds's avatar
Linus Torvalds committed
522 523

out_unlock:
524
	rcu_read_unlock();
Linus Torvalds's avatar
Linus Torvalds committed
525 526 527
	return err;
}

528 529 530 531
SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
{
	int version;
	struct ipc_namespace *ns;
532 533
	struct msqid64_ds msqid64;
	int err;
534 535 536 537 538 539 540 541 542

	if (msqid < 0 || cmd < 0)
		return -EINVAL;

	version = ipc_parse_version(&cmd);
	ns = current->nsproxy->ipc_ns;

	switch (cmd) {
	case IPC_INFO:
543 544 545 546 547 548 549 550 551
	case MSG_INFO: {
		struct msginfo msginfo;
		err = msgctl_info(ns, msqid, cmd, &msginfo);
		if (err < 0)
			return err;
		if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
			err = -EFAULT;
		return err;
	}
552 553
	case MSG_STAT:	/* msqid is an index rather than a msg queue id */
	case IPC_STAT:
554 555 556 557 558 559
		err = msgctl_stat(ns, msqid, cmd, &msqid64);
		if (err < 0)
			return err;
		if (copy_msqid_to_user(buf, &msqid64, version))
			err = -EFAULT;
		return err;
560
	case IPC_SET:
561 562 563
		if (copy_msqid_from_user(&msqid64, buf, version))
			return -EFAULT;
		/* fallthru */
564
	case IPC_RMID:
565
		return msgctl_down(ns, msqid, cmd, &msqid64);
566 567 568 569 570
	default:
		return  -EINVAL;
	}
}

571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
#ifdef CONFIG_COMPAT

struct compat_msqid_ds {
	struct compat_ipc_perm msg_perm;
	compat_uptr_t msg_first;
	compat_uptr_t msg_last;
	compat_time_t msg_stime;
	compat_time_t msg_rtime;
	compat_time_t msg_ctime;
	compat_ulong_t msg_lcbytes;
	compat_ulong_t msg_lqbytes;
	unsigned short msg_cbytes;
	unsigned short msg_qnum;
	unsigned short msg_qbytes;
	compat_ipc_pid_t msg_lspid;
	compat_ipc_pid_t msg_lrpid;
};

static int copy_compat_msqid_from_user(struct msqid64_ds *out, void __user *buf,
					int version)
{
	memset(out, 0, sizeof(*out));
	if (version == IPC_64) {
		struct compat_msqid64_ds *p = buf;
595
		if (get_compat_ipc64_perm(&out->msg_perm, &p->msg_perm))
596 597 598 599 600
			return -EFAULT;
		if (get_user(out->msg_qbytes, &p->msg_qbytes))
			return -EFAULT;
	} else {
		struct compat_msqid_ds *p = buf;
601
		if (get_compat_ipc_perm(&out->msg_perm, &p->msg_perm))
602 603 604 605 606 607 608 609 610 611 612 613 614
			return -EFAULT;
		if (get_user(out->msg_qbytes, &p->msg_qbytes))
			return -EFAULT;
	}
	return 0;
}

static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in,
					int version)
{
	if (version == IPC_64) {
		struct compat_msqid64_ds v;
		memset(&v, 0, sizeof(v));
615
		to_compat_ipc64_perm(&v.msg_perm, &in->msg_perm);
616 617 618 619 620 621 622 623 624 625 626 627
		v.msg_stime = in->msg_stime;
		v.msg_rtime = in->msg_rtime;
		v.msg_ctime = in->msg_ctime;
		v.msg_cbytes = in->msg_cbytes;
		v.msg_qnum = in->msg_qnum;
		v.msg_qbytes = in->msg_qbytes;
		v.msg_lspid = in->msg_lspid;
		v.msg_lrpid = in->msg_lrpid;
		return copy_to_user(buf, &v, sizeof(v));
	} else {
		struct compat_msqid_ds v;
		memset(&v, 0, sizeof(v));
628
		to_compat_ipc_perm(&v.msg_perm, &in->msg_perm);
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
		v.msg_stime = in->msg_stime;
		v.msg_rtime = in->msg_rtime;
		v.msg_ctime = in->msg_ctime;
		v.msg_cbytes = in->msg_cbytes;
		v.msg_qnum = in->msg_qnum;
		v.msg_qbytes = in->msg_qbytes;
		v.msg_lspid = in->msg_lspid;
		v.msg_lrpid = in->msg_lrpid;
		return copy_to_user(buf, &v, sizeof(v));
	}
}

COMPAT_SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, void __user *, uptr)
{
	struct ipc_namespace *ns;
	int err;
	struct msqid64_ds msqid64;
	int version = compat_ipc_parse_version(&cmd);

	ns = current->nsproxy->ipc_ns;

	if (msqid < 0 || cmd < 0)
		return -EINVAL;

	switch (cmd & (~IPC_64)) {
	case IPC_INFO:
	case MSG_INFO: {
		struct msginfo msginfo;
		err = msgctl_info(ns, msqid, cmd, &msginfo);
		if (err < 0)
			return err;
		if (copy_to_user(uptr, &msginfo, sizeof(struct msginfo)))
			err = -EFAULT;
		return err;
	}
	case IPC_STAT:
	case MSG_STAT:
		err = msgctl_stat(ns, msqid, cmd, &msqid64);
		if (err < 0)
			return err;
		if (copy_compat_msqid_to_user(uptr, &msqid64, version))
			err = -EFAULT;
		return err;
	case IPC_SET:
		if (copy_compat_msqid_from_user(&msqid64, uptr, version))
			return -EFAULT;
		/* fallthru */
	case IPC_RMID:
		return msgctl_down(ns, msqid, cmd, &msqid64);
	default:
		return -EINVAL;
	}
}
#endif

684
static int testmsg(struct msg_msg *msg, long type, int mode)
Linus Torvalds's avatar
Linus Torvalds committed
685
{
686 687 688 689 690 691 692 693 694 695
	switch (mode) {
	case SEARCH_ANY:
	case SEARCH_NUMBER:
		return 1;
	case SEARCH_LESSEQUAL:
		if (msg->m_type <= type)
			return 1;
		break;
	case SEARCH_EQUAL:
		if (msg->m_type == type)
Linus Torvalds's avatar
Linus Torvalds committed
696
			return 1;
697 698 699 700 701
		break;
	case SEARCH_NOTEQUAL:
		if (msg->m_type != type)
			return 1;
		break;
Linus Torvalds's avatar
Linus Torvalds committed
702 703 704 705
	}
	return 0;
}

706 707
static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg,
				 struct wake_q_head *wake_q)
Linus Torvalds's avatar
Linus Torvalds committed
708
{
709
	struct msg_receiver *msr, *t;
710

711
	list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
712 713 714 715
		if (testmsg(msg, msr->r_msgtype, msr->r_mode) &&
		    !security_msg_queue_msgrcv(msq, msg, msr->r_tsk,
					       msr->r_msgtype, msr->r_mode)) {

Linus Torvalds's avatar
Linus Torvalds committed
716
			list_del(&msr->r_list);
717
			if (msr->r_maxsize < msg->m_ts) {
718 719
				wake_q_add(wake_q, msr->r_tsk);
				WRITE_ONCE(msr->r_msg, ERR_PTR(-E2BIG));
Linus Torvalds's avatar
Linus Torvalds committed
720
			} else {
721
				msq->q_lrpid = task_pid_vnr(msr->r_tsk);
Linus Torvalds's avatar
Linus Torvalds committed
722
				msq->q_rtime = get_seconds();
723

724 725
				wake_q_add(wake_q, msr->r_tsk);
				WRITE_ONCE(msr->r_msg, msg);
Linus Torvalds's avatar
Linus Torvalds committed
726 727 728 729
				return 1;
			}
		}
	}
730

Linus Torvalds's avatar
Linus Torvalds committed
731 732 733
	return 0;
}

734
static long do_msgsnd(int msqid, long mtype, void __user *mtext,
735
		size_t msgsz, int msgflg)
Linus Torvalds's avatar
Linus Torvalds committed
736 737 738 739
{
	struct msg_queue *msq;
	struct msg_msg *msg;
	int err;
740
	struct ipc_namespace *ns;
741
	DEFINE_WAKE_Q(wake_q);
742 743

	ns = current->nsproxy->ipc_ns;
744

745
	if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)
Linus Torvalds's avatar
Linus Torvalds committed
746 747 748 749
		return -EINVAL;
	if (mtype < 1)
		return -EINVAL;

750
	msg = load_msg(mtext, msgsz);
751
	if (IS_ERR(msg))
Linus Torvalds's avatar
Linus Torvalds committed
752 753 754 755 756
		return PTR_ERR(msg);

	msg->m_type = mtype;
	msg->m_ts = msgsz;

757 758
	rcu_read_lock();
	msq = msq_obtain_object_check(ns, msqid);
759 760
	if (IS_ERR(msq)) {
		err = PTR_ERR(msq);
761
		goto out_unlock1;
762
	}
Linus Torvalds's avatar
Linus Torvalds committed
763

764 765
	ipc_lock_object(&msq->q_perm);

Linus Torvalds's avatar
Linus Torvalds committed
766 767 768
	for (;;) {
		struct msg_sender s;

769
		err = -EACCES;
770
		if (ipcperms(ns, &msq->q_perm, S_IWUGO))
771
			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
772

773
		/* raced with RMID? */
774
		if (!ipc_valid_object(&msq->q_perm)) {
775 776 777 778
			err = -EIDRM;
			goto out_unlock0;
		}

Linus Torvalds's avatar
Linus Torvalds committed
779 780
		err = security_msg_queue_msgsnd(msq, msg, msgflg);
		if (err)
781
			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
782

783
		if (msg_fits_inqueue(msq, msgsz))
Linus Torvalds's avatar
Linus Torvalds committed
784 785 786
			break;

		/* queue full, wait: */
787 788
		if (msgflg & IPC_NOWAIT) {
			err = -EAGAIN;
789
			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
790
		}
791

792
		/* enqueue the sender and prepare to block */
793
		ss_add(msq, &s, msgsz);
794

795
		if (!ipc_rcu_getref(&msq->q_perm)) {
796
			err = -EIDRM;
797
			goto out_unlock0;
798 799
		}

800 801
		ipc_unlock_object(&msq->q_perm);
		rcu_read_unlock();
Linus Torvalds's avatar
Linus Torvalds committed
802 803
		schedule();

804 805 806
		rcu_read_lock();
		ipc_lock_object(&msq->q_perm);

807
		ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
808 809
		/* raced with RMID? */
		if (!ipc_valid_object(&msq->q_perm)) {
Linus Torvalds's avatar
Linus Torvalds committed
810
			err = -EIDRM;
811
			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
812 813
		}
		ss_del(&s);
814

Linus Torvalds's avatar
Linus Torvalds committed
815
		if (signal_pending(current)) {
816
			err = -ERESTARTNOHAND;
817
			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
818
		}
819

Linus Torvalds's avatar
Linus Torvalds committed
820
	}
821

822
	msq->q_lspid = task_tgid_vnr(current);
Linus Torvalds's avatar
Linus Torvalds committed
823 824
	msq->q_stime = get_seconds();

825
	if (!pipelined_send(msq, msg, &wake_q)) {
826
		/* no one is waiting for this message, enqueue it */
827
		list_add_tail(&msg->m_list, &msq->q_messages);
Linus Torvalds's avatar
Linus Torvalds committed
828 829
		msq->q_cbytes += msgsz;
		msq->q_qnum++;
830 831
		atomic_add(msgsz, &ns->msg_bytes);
		atomic_inc(&ns->msg_hdrs);
Linus Torvalds's avatar
Linus Torvalds committed
832
	}
833

Linus Torvalds's avatar
Linus Torvalds committed
834 835 836
	err = 0;
	msg = NULL;

837 838
out_unlock0:
	ipc_unlock_object(&msq->q_perm);
839
	wake_up_q(&wake_q);
840 841
out_unlock1:
	rcu_read_unlock();
842
	if (msg != NULL)
Linus Torvalds's avatar
Linus Torvalds committed
843 844 845 846
		free_msg(msg);
	return err;
}

847 848
SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
		int, msgflg)
849 850 851 852 853 854 855 856
{
	long mtype;

	if (get_user(mtype, &msgp->mtype))
		return -EFAULT;
	return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
}

857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
#ifdef CONFIG_COMPAT

struct compat_msgbuf {
	compat_long_t mtype;
	char mtext[1];
};

COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp,
		       compat_ssize_t, msgsz, int, msgflg)
{
	struct compat_msgbuf __user *up = compat_ptr(msgp);
	compat_long_t mtype;

	if (get_user(mtype, &up->mtype))
		return -EFAULT;
	return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg);
}
#endif

876
static inline int convert_mode(long *msgtyp, int msgflg)
Linus Torvalds's avatar
Linus Torvalds committed
877
{
878 879
	if (msgflg & MSG_COPY)
		return SEARCH_NUMBER;
880
	/*
Linus Torvalds's avatar
Linus Torvalds committed
881 882 883
	 *  find message of correct type.
	 *  msgtyp = 0 => get first.
	 *  msgtyp > 0 => get first message of matching type.
884
	 *  msgtyp < 0 => get message with least type must be < abs(msgtype).
Linus Torvalds's avatar
Linus Torvalds committed
885
	 */
886
	if (*msgtyp == 0)
Linus Torvalds's avatar
Linus Torvalds committed
887
		return SEARCH_ANY;
888
	if (*msgtyp < 0) {
889 890 891 892
		if (*msgtyp == LONG_MIN) /* -LONG_MIN is undefined */
			*msgtyp = LONG_MAX;
		else
			*msgtyp = -*msgtyp;
Linus Torvalds's avatar
Linus Torvalds committed
893 894
		return SEARCH_LESSEQUAL;
	}
895
	if (msgflg & MSG_EXCEPT)
Linus Torvalds's avatar
Linus Torvalds committed
896 897 898 899
		return SEARCH_NOTEQUAL;
	return SEARCH_EQUAL;
}

900 901 902 903 904 905 906 907 908 909 910 911 912 913
static long do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
{
	struct msgbuf __user *msgp = dest;
	size_t msgsz;

	if (put_user(msg->m_type, &msgp->mtype))
		return -EFAULT;

	msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz;
	if (store_msg(msgp->mtext, msg, msgsz))
		return -EFAULT;
	return msgsz;
}

914
#ifdef CONFIG_CHECKPOINT_RESTORE
915 916 917 918
/*
 * This function creates new kernel message structure, large enough to store
 * bufsz message bytes.
 */
919
static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz)
920 921 922 923 924 925 926 927 928 929 930 931
{
	struct msg_msg *copy;

	/*
	 * Create dummy message to copy real message to.
	 */
	copy = load_msg(buf, bufsz);
	if (!IS_ERR(copy))
		copy->m_ts = bufsz;
	return copy;
}

932
static inline void free_copy(struct msg_msg *copy)
933
{
934
	if (copy)
935 936 937
		free_msg(copy);
}
#else
938
static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz)
939 940 941 942
{
	return ERR_PTR(-ENOSYS);
}

943 944 945
static inline void free_copy(struct msg_msg *copy)
{
}
946 947
#endif

948 949
static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
{
950
	struct msg_msg *msg, *found = NULL;
951 952 953 954 955 956 957 958
	long count = 0;

	list_for_each_entry(msg, &msq->q_messages, m_list) {
		if (testmsg(msg, *msgtyp, mode) &&
		    !security_msg_queue_msgrcv(msq, msg, current,
					       *msgtyp, mode)) {
			if (mode == SEARCH_LESSEQUAL && msg->m_type != 1) {
				*msgtyp = msg->m_type - 1;
959
				found = msg;
960 961 962 963 964 965 966 967 968
			} else if (mode == SEARCH_NUMBER) {
				if (*msgtyp == count)
					return msg;
			} else
				return msg;
			count++;
		}
	}

969
	return found ?: ERR_PTR(-EAGAIN);
970 971
}

972
static long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
973
	       long (*msg_handler)(void __user *, struct msg_msg *, size_t))
Linus Torvalds's avatar
Linus Torvalds committed
974 975
{
	int mode;
976
	struct msg_queue *msq;
977
	struct ipc_namespace *ns;
978
	struct msg_msg *msg, *copy = NULL;
979
	DEFINE_WAKE_Q(wake_q);
Linus Torvalds's avatar
Linus Torvalds committed
980

981 982
	ns = current->nsproxy->ipc_ns;

983
	if (msqid < 0 || (long) bufsz < 0)
Linus Torvalds's avatar
Linus Torvalds committed
984
		return -EINVAL;
985

986
	if (msgflg & MSG_COPY) {
987 988
		if ((msgflg & MSG_EXCEPT) || !(msgflg & IPC_NOWAIT))
			return -EINVAL;
989
		copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax));
990 991 992
		if (IS_ERR(copy))
			return PTR_ERR(copy);
	}
993
	mode = convert_mode(&msgtyp, msgflg);
Linus Torvalds's avatar
Linus Torvalds committed
994

995 996
	rcu_read_lock();
	msq = msq_obtain_object_check(ns, msqid);
997
	if (IS_ERR(msq)) {
998
		rcu_read_unlock();
999
		free_copy(copy);
1000
		return PTR_ERR(msq);
1001
	}
Linus Torvalds's avatar
Linus Torvalds committed
1002 1003 1004 1005 1006

	for (;;) {
		struct msg_receiver msr_d;

		msg = ERR_PTR(-EACCES);
1007
		if (ipcperms(ns, &msq->q_perm, S_IRUGO))
1008
			goto out_unlock1;
Linus Torvalds's avatar
Linus Torvalds committed
1009

1010
		ipc_lock_object(&msq->q_perm);
1011 1012

		/* raced with RMID? */
1013
		if (!ipc_valid_object(&msq->q_perm)) {
1014 1015 1016 1017
			msg = ERR_PTR(-EIDRM);
			goto out_unlock0;
		}

1018
		msg = find_msg(msq, &msgtyp, mode);
1019 1020 1021 1022 1023
		if (!IS_ERR(msg)) {
			/*
			 * Found a suitable message.
			 * Unlink it from the queue.
			 */
1024
			if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
Linus Torvalds's avatar
Linus Torvalds committed
1025
				msg = ERR_PTR(-E2BIG);
1026
				goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
1027
			}
1028 1029 1030 1031
			/*
			 * If we are copying, then do not unlink message and do
			 * not update queue parameters.
			 */
1032 1033
			if (msgflg & MSG_COPY) {
				msg = copy_msg(msg, copy);
1034
				goto out_unlock0;
1035
			}
1036

Linus Torvalds's avatar
Linus Torvalds committed
1037 1038 1039
			list_del(&msg->m_list);
			msq->q_qnum--;
			msq->q_rtime = get_seconds();
1040
			msq->q_lrpid = task_tgid_vnr(current);
Linus Torvalds's avatar
Linus Torvalds committed
1041
			msq->q_cbytes -= msg->m_ts;
1042 1043
			atomic_sub(msg->m_ts, &ns->msg_bytes);
			atomic_dec(&ns->msg_hdrs);
1044
			ss_wakeup(msq, &wake_q, false);
1045 1046

			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
1047
		}
1048

Linus Torvalds's avatar
Linus Torvalds committed
1049 1050 1051
		/* No message waiting. Wait for a message */
		if (msgflg & IPC_NOWAIT) {
			msg = ERR_PTR(-ENOMSG);
1052
			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
1053
		}
1054

1055
		list_add_tail(&msr_d.r_list, &msq->q_receivers);
Linus Torvalds's avatar
Linus Torvalds committed
1056 1057 1058
		msr_d.r_tsk = current;
		msr_d.r_msgtype = msgtyp;
		msr_d.r_mode = mode;
1059
		if (msgflg & MSG_NOERROR)
Linus Torvalds's avatar
Linus Torvalds committed
1060
			msr_d.r_maxsize = INT_MAX;
1061
		else
1062
			msr_d.r_maxsize = bufsz;
Linus Torvalds's avatar
Linus Torvalds committed
1063
		msr_d.r_msg = ERR_PTR(-EAGAIN);
1064
		__set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds's avatar
Linus Torvalds committed
1065

1066 1067
		ipc_unlock_object(&msq->q_perm);
		rcu_read_unlock();
Linus Torvalds's avatar
Linus Torvalds committed
1068 1069
		schedule();

1070 1071 1072 1073 1074 1075
		/*
		 * Lockless receive, part 1:
		 * We don't hold a reference to the queue and getting a
		 * reference would defeat the idea of a lockless operation,
		 * thus the code relies on rcu to guarantee the existence of
		 * msq:
Linus Torvalds's avatar
Linus Torvalds committed
1076 1077 1078 1079 1080
		 * Prior to destruction, expunge_all(-EIRDM) changes r_msg.
		 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
		 */
		rcu_read_lock();

1081 1082 1083 1084 1085 1086
		/*
		 * Lockless receive, part 2:
		 * The work in pipelined_send() and expunge_all():
		 * - Set pointer to message
		 * - Queue the receiver task for later wakeup
		 * - Wake up the process after the lock is dropped.
1087
		 *
1088 1089
		 * Should the process wake up before this wakeup (due to a
		 * signal) it will either see the message and continue ...
Linus Torvalds's avatar
Linus Torvalds committed
1090
		 */
1091
		msg = READ_ONCE(msr_d.r_msg);
1092 1093
		if (msg != ERR_PTR(-EAGAIN))
			goto out_unlock1;
Linus Torvalds's avatar
Linus Torvalds committed
1094

1095 1096 1097 1098
		 /*
		  * ... or see -EAGAIN, acquire the lock to check the message
		  * again.
		  */
1099
		ipc_lock_object(&msq->q_perm);
Linus Torvalds's avatar
Linus Torvalds committed
1100

1101
		msg = msr_d.r_msg;
1102
		if (msg != ERR_PTR(-EAGAIN))
1103
			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
1104 1105 1106 1107

		list_del(&msr_d.r_list);
		if (signal_pending(current)) {
			msg = ERR_PTR(-ERESTARTNOHAND);
1108
			goto out_unlock0;
Linus Torvalds's avatar
Linus Torvalds committed
1109
		}
1110 1111

		ipc_unlock_object(&msq->q_perm);
Linus Torvalds's avatar
Linus Torvalds committed
1112
	}
1113 1114 1115

out_unlock0:
	ipc_unlock_object(&msq->q_perm);
1116
	wake_up_q(&wake_q);
1117 1118
out_unlock1:
	rcu_read_unlock();
1119
	if (IS_ERR(msg)) {
1120
		free_copy(copy);
1121
		return PTR_ERR(msg);
1122
	}
Linus Torvalds's avatar
Linus Torvalds committed
1123

1124
	bufsz = msg_handler(buf, msg, bufsz);
Linus Torvalds's avatar
Linus Torvalds committed
1125
	free_msg(msg);
1126

1127
	return bufsz;
Linus Torvalds's avatar
Linus Torvalds committed
1128 1129
}

1130 1131
SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
		long, msgtyp, int, msgflg)
1132
{
1133
	return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill);
1134 1135
}

1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
#ifdef CONFIG_COMPAT
static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
{
	struct compat_msgbuf __user *msgp = dest;
	size_t msgsz;

	if (put_user(msg->m_type, &msgp->mtype))
		return -EFAULT;

	msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz;
	if (store_msg(msgp->mtext, msg, msgsz))
		return -EFAULT;
	return msgsz;
}

COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp,
		       compat_ssize_t, msgsz, compat_long_t, msgtyp, int, msgflg)
{
	return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp,
			 msgflg, compat_do_msg_fill);
}
#endif
1158

1159
int msg_init_ns(struct ipc_namespace *ns)
1160 1161 1162
{
	ns->msg_ctlmax = MSGMAX;
	ns->msg_ctlmnb = MSGMNB;
1163
	ns->msg_ctlmni = MSGMNI;
1164 1165 1166

	atomic_set(&ns->msg_bytes, 0);
	atomic_set(&ns->msg_hdrs, 0);
1167
	return ipc_init_ids(&ns->ids[IPC_MSG_IDS]);
1168 1169 1170 1171 1172 1173 1174
}

#ifdef CONFIG_IPC_NS
void msg_exit_ns(struct ipc_namespace *ns)
{
	free_ipcs(ns, &msg_ids(ns), freeque);
	idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr);
1175
	rhashtable_destroy(&ns->ids[IPC_MSG_IDS].key_ht);
1176 1177 1178
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
1179
#ifdef CONFIG_PROC_FS
1180
static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
Linus Torvalds's avatar
Linus Torvalds committed
1181
{
1182
	struct user_namespace *user_ns = seq_user_ns(s);
1183 1184
	struct kern_ipc_perm *ipcp = it;
	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
1185

1186
	seq_printf(s,
1187
		   "%10d %10d  %4o  %10lu %10lu %5u %5u %5u %5u %5u %5u %10llu %10llu %10llu\n",
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
		   msq->q_perm.key,
		   msq->q_perm.id,
		   msq->q_perm.mode,
		   msq->q_cbytes,
		   msq->q_qnum,
		   msq->q_lspid,
		   msq->q_lrpid,
		   from_kuid_munged(user_ns, msq->q_perm.uid),
		   from_kgid_munged(user_ns, msq->q_perm.gid),
		   from_kuid_munged(user_ns, msq->q_perm.cuid),
		   from_kgid_munged(user_ns, msq->q_perm.cgid),
		   msq->q_stime,
		   msq->q_rtime,
		   msq->q_ctime);

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1204 1205
}
#endif
1206

1207
int __init msg_init(void)
1208
{
1209
	const int err = msg_init_ns(&init_ipc_ns);
1210 1211 1212 1213

	ipc_init_proc_interface("sysvipc/msg",
				"       key      msqid perms      cbytes       qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n",
				IPC_MSG_IDS, sysvipc_msg_proc_show);
1214
	return err;
1215
}