msg.c 22.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 * linux/ipc/msg.c
3
 * Copyright (C) 1992 Krishna Balasubramanian
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14
 *
 * 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
15
 * (c) 1999 Manfred Spraul <manfred@colorfullife.com>
S
Steve Grubb 已提交
16 17 18
 *
 * support for audit of ipc object properties and permission changes
 * Dustin Kirkland <dustin.kirkland@us.ibm.com>
K
Kirill Korotaev 已提交
19 20 21 22
 *
 * namespaces support
 * OpenVZ, SWsoft Inc.
 * Pavel Emelianov <xemul@openvz.org>
L
Linus Torvalds 已提交
23 24
 */

25
#include <linux/capability.h>
L
Linus Torvalds 已提交
26 27 28
#include <linux/msg.h>
#include <linux/spinlock.h>
#include <linux/init.h>
29
#include <linux/mm.h>
L
Linus Torvalds 已提交
30 31 32 33 34 35
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/security.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/audit.h>
36
#include <linux/seq_file.h>
N
Nadia Derbey 已提交
37
#include <linux/rwsem.h>
K
Kirill Korotaev 已提交
38
#include <linux/nsproxy.h>
39
#include <linux/ipc_namespace.h>
I
Ingo Molnar 已提交
40

L
Linus Torvalds 已提交
41 42 43 44
#include <asm/current.h>
#include <asm/uaccess.h>
#include "util.h"

45 46 47
/*
 * one msg_receiver structure for each sleeping receiver:
 */
L
Linus Torvalds 已提交
48
struct msg_receiver {
49 50
	struct list_head	r_list;
	struct task_struct	*r_tsk;
L
Linus Torvalds 已提交
51

52 53 54
	int			r_mode;
	long			r_msgtype;
	long			r_maxsize;
L
Linus Torvalds 已提交
55

56
	struct msg_msg		*volatile r_msg;
L
Linus Torvalds 已提交
57 58 59 60
};

/* one msg_sender for each sleeping sender */
struct msg_sender {
61 62
	struct list_head	list;
	struct task_struct	*tsk;
L
Linus Torvalds 已提交
63 64 65 66 67 68 69
};

#define SEARCH_ANY		1
#define SEARCH_EQUAL		2
#define SEARCH_NOTEQUAL		3
#define SEARCH_LESSEQUAL	4

70
#define msg_ids(ns)	((ns)->ids[IPC_MSG_IDS])
L
Linus Torvalds 已提交
71

K
Kirill Korotaev 已提交
72 73
#define msg_unlock(msq)		ipc_unlock(&(msq)->q_perm)

74
static void freeque(struct ipc_namespace *, struct kern_ipc_perm *);
N
Nadia Derbey 已提交
75
static int newque(struct ipc_namespace *, struct ipc_params *);
L
Linus Torvalds 已提交
76
#ifdef CONFIG_PROC_FS
77
static int sysvipc_msg_proc_show(struct seq_file *s, void *it);
L
Linus Torvalds 已提交
78 79
#endif

80 81 82
/*
 * Scale msgmni with the available lowmem size: the memory dedicated to msg
 * queues should occupy at most 1/MSG_MEM_SCALE of lowmem.
83 84
 * Also take into account the number of nsproxies created so far.
 * This should be done staying within the (MSGMNI , IPCMNI/nr_ipc_ns) range.
85
 */
86
void recompute_msgmni(struct ipc_namespace *ns)
87 88 89
{
	struct sysinfo i;
	unsigned long allowed;
90
	int nb_ns;
91 92 93 94

	si_meminfo(&i);
	allowed = (((i.totalram - i.totalhigh) / MSG_MEM_SCALE) * i.mem_unit)
		/ MSGMNB;
95 96
	nb_ns = atomic_read(&nr_ipc_ns);
	allowed /= nb_ns;
97 98 99

	if (allowed < MSGMNI) {
		ns->msg_ctlmni = MSGMNI;
100
		return;
101 102
	}

103 104
	if (allowed > IPCMNI / nb_ns) {
		ns->msg_ctlmni = IPCMNI / nb_ns;
105
		return;
106 107 108 109 110
	}

	ns->msg_ctlmni = allowed;
}

111
void msg_init_ns(struct ipc_namespace *ns)
K
Kirill Korotaev 已提交
112 113 114
{
	ns->msg_ctlmax = MSGMAX;
	ns->msg_ctlmnb = MSGMNB;
115 116 117

	recompute_msgmni(ns);

118 119
	atomic_set(&ns->msg_bytes, 0);
	atomic_set(&ns->msg_hdrs, 0);
120
	ipc_init_ids(&ns->ids[IPC_MSG_IDS]);
K
Kirill Korotaev 已提交
121 122
}

123
#ifdef CONFIG_IPC_NS
K
Kirill Korotaev 已提交
124 125
void msg_exit_ns(struct ipc_namespace *ns)
{
126
	free_ipcs(ns, &msg_ids(ns), freeque);
S
Serge E. Hallyn 已提交
127
	idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr);
K
Kirill Korotaev 已提交
128
}
129
#endif
K
Kirill Korotaev 已提交
130

131
void __init msg_init(void)
L
Linus Torvalds 已提交
132
{
133
	msg_init_ns(&init_ipc_ns);
134 135 136 137

	printk(KERN_INFO "msgmni has been set to %d\n",
		init_ipc_ns.msg_ctlmni);

138 139
	ipc_init_proc_interface("sysvipc/msg",
				"       key      msqid perms      cbytes       qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n",
K
Kirill Korotaev 已提交
140
				IPC_MSG_IDS, sysvipc_msg_proc_show);
L
Linus Torvalds 已提交
141 142
}

N
Nadia Derbey 已提交
143 144 145 146
/*
 * msg_lock_(check_) routines are called in the paths where the rw_mutex
 * is not held.
 */
147 148
static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id)
{
N
Nadia Derbey 已提交
149 150
	struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id);

151 152 153
	if (IS_ERR(ipcp))
		return (struct msg_queue *)ipcp;

N
Nadia Derbey 已提交
154
	return container_of(ipcp, struct msg_queue, q_perm);
155 156 157 158 159
}

static inline struct msg_queue *msg_lock_check(struct ipc_namespace *ns,
						int id)
{
N
Nadia Derbey 已提交
160 161
	struct kern_ipc_perm *ipcp = ipc_lock_check(&msg_ids(ns), id);

162 163 164
	if (IS_ERR(ipcp))
		return (struct msg_queue *)ipcp;

N
Nadia Derbey 已提交
165
	return container_of(ipcp, struct msg_queue, q_perm);
166 167
}

N
Nadia Derbey 已提交
168 169 170 171 172
static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
{
	ipc_rmid(&msg_ids(ns), &s->q_perm);
}

N
Nadia Derbey 已提交
173 174 175 176 177
/**
 * newque - Create a new msg queue
 * @ns: namespace
 * @params: ptr to the structure that contains the key and msgflg
 *
N
Nadia Derbey 已提交
178
 * Called with msg_ids.rw_mutex held (writer)
N
Nadia Derbey 已提交
179
 */
N
Nadia Derbey 已提交
180
static int newque(struct ipc_namespace *ns, struct ipc_params *params)
L
Linus Torvalds 已提交
181 182
{
	struct msg_queue *msq;
183
	int id, retval;
N
Nadia Derbey 已提交
184 185
	key_t key = params->key;
	int msgflg = params->flg;
L
Linus Torvalds 已提交
186

187 188
	msq = ipc_rcu_alloc(sizeof(*msq));
	if (!msq)
L
Linus Torvalds 已提交
189 190
		return -ENOMEM;

191
	msq->q_perm.mode = msgflg & S_IRWXUGO;
L
Linus Torvalds 已提交
192 193 194 195 196 197 198 199 200
	msq->q_perm.key = key;

	msq->q_perm.security = NULL;
	retval = security_msg_queue_alloc(msq);
	if (retval) {
		ipc_rcu_putref(msq);
		return retval;
	}

N
Nadia Derbey 已提交
201 202 203
	/*
	 * ipc_addid() locks msq
	 */
K
Kirill Korotaev 已提交
204
	id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
205
	if (id < 0) {
L
Linus Torvalds 已提交
206 207
		security_msg_queue_free(msq);
		ipc_rcu_putref(msq);
208
		return id;
L
Linus Torvalds 已提交
209 210 211 212 213
	}

	msq->q_stime = msq->q_rtime = 0;
	msq->q_ctime = get_seconds();
	msq->q_cbytes = msq->q_qnum = 0;
K
Kirill Korotaev 已提交
214
	msq->q_qbytes = ns->msg_ctlmnb;
L
Linus Torvalds 已提交
215 216 217 218
	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);
N
Nadia Derbey 已提交
219

L
Linus Torvalds 已提交
220 221
	msg_unlock(msq);

N
Nadia Derbey 已提交
222
	return msq->q_perm.id;
L
Linus Torvalds 已提交
223 224
}

225
static inline void ss_add(struct msg_queue *msq, struct msg_sender *mss)
L
Linus Torvalds 已提交
226
{
227 228 229
	mss->tsk = current;
	current->state = TASK_INTERRUPTIBLE;
	list_add_tail(&mss->list, &msq->q_senders);
L
Linus Torvalds 已提交
230 231
}

232
static inline void ss_del(struct msg_sender *mss)
L
Linus Torvalds 已提交
233
{
234
	if (mss->list.next != NULL)
L
Linus Torvalds 已提交
235 236 237
		list_del(&mss->list);
}

238
static void ss_wakeup(struct list_head *h, int kill)
L
Linus Torvalds 已提交
239 240 241 242 243
{
	struct list_head *tmp;

	tmp = h->next;
	while (tmp != h) {
244 245 246
		struct msg_sender *mss;

		mss = list_entry(tmp, struct msg_sender, list);
L
Linus Torvalds 已提交
247
		tmp = tmp->next;
248 249
		if (kill)
			mss->list.next = NULL;
L
Linus Torvalds 已提交
250 251 252 253
		wake_up_process(mss->tsk);
	}
}

254
static void expunge_all(struct msg_queue *msq, int res)
L
Linus Torvalds 已提交
255 256 257 258 259
{
	struct list_head *tmp;

	tmp = msq->q_receivers.next;
	while (tmp != &msq->q_receivers) {
260 261 262
		struct msg_receiver *msr;

		msr = list_entry(tmp, struct msg_receiver, r_list);
L
Linus Torvalds 已提交
263 264 265 266 267 268 269
		tmp = tmp->next;
		msr->r_msg = NULL;
		wake_up_process(msr->r_tsk);
		smp_mb();
		msr->r_msg = ERR_PTR(res);
	}
}
270 271 272

/*
 * freeque() wakes up waiters on the sender and receiver waiting queue,
N
Nadia Derbey 已提交
273 274
 * removes the message queue from message queue ID IDR, and cleans up all the
 * messages associated with this queue.
L
Linus Torvalds 已提交
275
 *
N
Nadia Derbey 已提交
276 277
 * msg_ids.rw_mutex (writer) and the spinlock for this message queue are held
 * before freeque() is called. msg_ids.rw_mutex remains locked on exit.
L
Linus Torvalds 已提交
278
 */
279
static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
L
Linus Torvalds 已提交
280 281
{
	struct list_head *tmp;
282
	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
L
Linus Torvalds 已提交
283

284 285
	expunge_all(msq, -EIDRM);
	ss_wakeup(&msq->q_senders, 1);
N
Nadia Derbey 已提交
286
	msg_rmid(ns, msq);
L
Linus Torvalds 已提交
287
	msg_unlock(msq);
288

L
Linus Torvalds 已提交
289
	tmp = msq->q_messages.next;
290 291 292
	while (tmp != &msq->q_messages) {
		struct msg_msg *msg = list_entry(tmp, struct msg_msg, m_list);

L
Linus Torvalds 已提交
293
		tmp = tmp->next;
294
		atomic_dec(&ns->msg_hdrs);
L
Linus Torvalds 已提交
295 296
		free_msg(msg);
	}
297
	atomic_sub(msq->q_cbytes, &ns->msg_bytes);
L
Linus Torvalds 已提交
298 299 300 301
	security_msg_queue_free(msq);
	ipc_rcu_putref(msq);
}

N
Nadia Derbey 已提交
302
/*
N
Nadia Derbey 已提交
303
 * Called with msg_ids.rw_mutex and ipcp locked.
N
Nadia Derbey 已提交
304
 */
N
Nadia Derbey 已提交
305
static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg)
N
Nadia Derbey 已提交
306
{
N
Nadia Derbey 已提交
307 308 309
	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);

	return security_msg_queue_associate(msq, msgflg);
N
Nadia Derbey 已提交
310 311
}

312
SYSCALL_DEFINE2(msgget, key_t, key, int, msgflg)
L
Linus Torvalds 已提交
313
{
K
Kirill Korotaev 已提交
314
	struct ipc_namespace *ns;
N
Nadia Derbey 已提交
315 316
	struct ipc_ops msg_ops;
	struct ipc_params msg_params;
K
Kirill Korotaev 已提交
317 318

	ns = current->nsproxy->ipc_ns;
N
Nadia Derbey 已提交
319

N
Nadia Derbey 已提交
320 321 322 323 324 325
	msg_ops.getnew = newque;
	msg_ops.associate = msg_security;
	msg_ops.more_checks = NULL;

	msg_params.key = key;
	msg_params.flg = msgflg;
326

N
Nadia Derbey 已提交
327
	return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);
L
Linus Torvalds 已提交
328 329
}

330 331
static inline unsigned long
copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
L
Linus Torvalds 已提交
332 333 334
{
	switch(version) {
	case IPC_64:
335
		return copy_to_user(buf, in, sizeof(*in));
L
Linus Torvalds 已提交
336
	case IPC_OLD:
337
	{
L
Linus Torvalds 已提交
338 339
		struct msqid_ds out;

340
		memset(&out, 0, sizeof(out));
L
Linus Torvalds 已提交
341 342 343 344 345 346 347

		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;

348 349
		if (in->msg_cbytes > USHRT_MAX)
			out.msg_cbytes	= USHRT_MAX;
L
Linus Torvalds 已提交
350 351 352 353
		else
			out.msg_cbytes	= in->msg_cbytes;
		out.msg_lcbytes		= in->msg_cbytes;

354 355
		if (in->msg_qnum > USHRT_MAX)
			out.msg_qnum	= USHRT_MAX;
L
Linus Torvalds 已提交
356 357 358
		else
			out.msg_qnum	= in->msg_qnum;

359 360
		if (in->msg_qbytes > USHRT_MAX)
			out.msg_qbytes	= USHRT_MAX;
L
Linus Torvalds 已提交
361 362 363 364 365 366 367
		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;

368 369
		return copy_to_user(buf, &out, sizeof(out));
	}
L
Linus Torvalds 已提交
370 371 372 373 374
	default:
		return -EINVAL;
	}
}

375
static inline unsigned long
376
copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version)
L
Linus Torvalds 已提交
377 378 379
{
	switch(version) {
	case IPC_64:
380
		if (copy_from_user(out, buf, sizeof(*out)))
L
Linus Torvalds 已提交
381 382 383
			return -EFAULT;
		return 0;
	case IPC_OLD:
384
	{
L
Linus Torvalds 已提交
385 386
		struct msqid_ds tbuf_old;

387
		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
L
Linus Torvalds 已提交
388 389
			return -EFAULT;

390 391 392
		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;
L
Linus Torvalds 已提交
393

394
		if (tbuf_old.msg_qbytes == 0)
395
			out->msg_qbytes	= tbuf_old.msg_lqbytes;
L
Linus Torvalds 已提交
396
		else
397
			out->msg_qbytes	= tbuf_old.msg_qbytes;
L
Linus Torvalds 已提交
398 399

		return 0;
400
	}
L
Linus Torvalds 已提交
401 402 403 404 405
	default:
		return -EINVAL;
	}
}

406 407 408 409 410 411 412
/*
 * This function handles some msgctl commands which require the rw_mutex
 * to be held in write mode.
 * NOTE: no locks must be held, the rw_mutex is taken inside this function.
 */
static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
		       struct msqid_ds __user *buf, int version)
L
Linus Torvalds 已提交
413 414
{
	struct kern_ipc_perm *ipcp;
415
	struct msqid64_ds uninitialized_var(msqid64);
416 417 418 419
	struct msg_queue *msq;
	int err;

	if (cmd == IPC_SET) {
420
		if (copy_msqid_from_user(&msqid64, buf, version))
421 422 423
			return -EFAULT;
	}

424
	ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd,
425 426 427
			       &msqid64.msg_perm, msqid64.msg_qbytes);
	if (IS_ERR(ipcp))
		return PTR_ERR(ipcp);
428

429
	msq = container_of(ipcp, struct msg_queue, q_perm);
430 431 432 433 434 435 436 437 438 439

	err = security_msg_queue_msgctl(msq, cmd);
	if (err)
		goto out_unlock;

	switch (cmd) {
	case IPC_RMID:
		freeque(ns, ipcp);
		goto out_up;
	case IPC_SET:
440
		if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
441 442 443 444 445
		    !capable(CAP_SYS_RESOURCE)) {
			err = -EPERM;
			goto out_unlock;
		}

446 447 448 449
		err = ipc_update_perm(&msqid64.msg_perm, ipcp);
		if (err)
			goto out_unlock;

450
		msq->q_qbytes = msqid64.msg_qbytes;
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471

		msq->q_ctime = get_seconds();
		/* sleeping receivers might be excluded by
		 * stricter permissions.
		 */
		expunge_all(msq, -EAGAIN);
		/* sleeping senders might be able to send
		 * due to a larger queue size.
		 */
		ss_wakeup(&msq->q_senders, 0);
		break;
	default:
		err = -EINVAL;
	}
out_unlock:
	msg_unlock(msq);
out_up:
	up_write(&msg_ids(ns).rw_mutex);
	return err;
}

472
SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
473
{
474 475
	struct msg_queue *msq;
	int err, version;
K
Kirill Korotaev 已提交
476
	struct ipc_namespace *ns;
477

L
Linus Torvalds 已提交
478 479 480 481
	if (msqid < 0 || cmd < 0)
		return -EINVAL;

	version = ipc_parse_version(&cmd);
K
Kirill Korotaev 已提交
482
	ns = current->nsproxy->ipc_ns;
L
Linus Torvalds 已提交
483 484

	switch (cmd) {
485 486 487
	case IPC_INFO:
	case MSG_INFO:
	{
L
Linus Torvalds 已提交
488 489
		struct msginfo msginfo;
		int max_id;
490

L
Linus Torvalds 已提交
491 492
		if (!buf)
			return -EFAULT;
493 494
		/*
		 * We must not return kernel stack data.
L
Linus Torvalds 已提交
495 496 497 498 499 500 501
		 * due to padding, it's not enough
		 * to set all member fields.
		 */
		err = security_msg_queue_msgctl(NULL, cmd);
		if (err)
			return err;

502
		memset(&msginfo, 0, sizeof(msginfo));
K
Kirill Korotaev 已提交
503 504 505
		msginfo.msgmni = ns->msg_ctlmni;
		msginfo.msgmax = ns->msg_ctlmax;
		msginfo.msgmnb = ns->msg_ctlmnb;
L
Linus Torvalds 已提交
506 507
		msginfo.msgssz = MSGSSZ;
		msginfo.msgseg = MSGSEG;
N
Nadia Derbey 已提交
508
		down_read(&msg_ids(ns).rw_mutex);
L
Linus Torvalds 已提交
509
		if (cmd == MSG_INFO) {
K
Kirill Korotaev 已提交
510
			msginfo.msgpool = msg_ids(ns).in_use;
511 512
			msginfo.msgmap = atomic_read(&ns->msg_hdrs);
			msginfo.msgtql = atomic_read(&ns->msg_bytes);
L
Linus Torvalds 已提交
513 514 515 516 517
		} else {
			msginfo.msgmap = MSGMAP;
			msginfo.msgpool = MSGPOOL;
			msginfo.msgtql = MSGTQL;
		}
N
Nadia Derbey 已提交
518
		max_id = ipc_get_maxid(&msg_ids(ns));
N
Nadia Derbey 已提交
519
		up_read(&msg_ids(ns).rw_mutex);
520
		if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
L
Linus Torvalds 已提交
521
			return -EFAULT;
522
		return (max_id < 0) ? 0 : max_id;
L
Linus Torvalds 已提交
523
	}
N
Nadia Derbey 已提交
524
	case MSG_STAT:	/* msqid is an index rather than a msg queue id */
L
Linus Torvalds 已提交
525 526 527 528
	case IPC_STAT:
	{
		struct msqid64_ds tbuf;
		int success_return;
529

L
Linus Torvalds 已提交
530 531 532
		if (!buf)
			return -EFAULT;

533
		if (cmd == MSG_STAT) {
534 535 536
			msq = msg_lock(ns, msqid);
			if (IS_ERR(msq))
				return PTR_ERR(msq);
N
Nadia Derbey 已提交
537
			success_return = msq->q_perm.id;
L
Linus Torvalds 已提交
538
		} else {
539 540 541
			msq = msg_lock_check(ns, msqid);
			if (IS_ERR(msq))
				return PTR_ERR(msq);
L
Linus Torvalds 已提交
542 543 544
			success_return = 0;
		}
		err = -EACCES;
545
		if (ipcperms(ns, &msq->q_perm, S_IRUGO))
L
Linus Torvalds 已提交
546 547 548 549 550 551
			goto out_unlock;

		err = security_msg_queue_msgctl(msq, cmd);
		if (err)
			goto out_unlock;

552 553
		memset(&tbuf, 0, sizeof(tbuf));

L
Linus Torvalds 已提交
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
		kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);
		tbuf.msg_stime  = msq->q_stime;
		tbuf.msg_rtime  = msq->q_rtime;
		tbuf.msg_ctime  = msq->q_ctime;
		tbuf.msg_cbytes = msq->q_cbytes;
		tbuf.msg_qnum   = msq->q_qnum;
		tbuf.msg_qbytes = msq->q_qbytes;
		tbuf.msg_lspid  = msq->q_lspid;
		tbuf.msg_lrpid  = msq->q_lrpid;
		msg_unlock(msq);
		if (copy_msqid_to_user(buf, &tbuf, version))
			return -EFAULT;
		return success_return;
	}
	case IPC_SET:
	case IPC_RMID:
570 571
		err = msgctl_down(ns, msqid, cmd, buf, version);
		return err;
L
Linus Torvalds 已提交
572 573 574 575 576 577 578 579 580
	default:
		return  -EINVAL;
	}

out_unlock:
	msg_unlock(msq);
	return err;
}

581
static int testmsg(struct msg_msg *msg, long type, int mode)
L
Linus Torvalds 已提交
582 583 584 585 586 587
{
	switch(mode)
	{
		case SEARCH_ANY:
			return 1;
		case SEARCH_LESSEQUAL:
588
			if (msg->m_type <=type)
L
Linus Torvalds 已提交
589 590 591
				return 1;
			break;
		case SEARCH_EQUAL:
592
			if (msg->m_type == type)
L
Linus Torvalds 已提交
593 594 595
				return 1;
			break;
		case SEARCH_NOTEQUAL:
596
			if (msg->m_type != type)
L
Linus Torvalds 已提交
597 598 599 600 601 602
				return 1;
			break;
	}
	return 0;
}

603
static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg)
L
Linus Torvalds 已提交
604
{
605
	struct list_head *tmp;
L
Linus Torvalds 已提交
606 607 608

	tmp = msq->q_receivers.next;
	while (tmp != &msq->q_receivers) {
609 610 611
		struct msg_receiver *msr;

		msr = list_entry(tmp, struct msg_receiver, r_list);
L
Linus Torvalds 已提交
612
		tmp = tmp->next;
613 614 615 616
		if (testmsg(msg, msr->r_msgtype, msr->r_mode) &&
		    !security_msg_queue_msgrcv(msq, msg, msr->r_tsk,
					       msr->r_msgtype, msr->r_mode)) {

L
Linus Torvalds 已提交
617
			list_del(&msr->r_list);
618
			if (msr->r_maxsize < msg->m_ts) {
L
Linus Torvalds 已提交
619 620 621 622 623 624
				msr->r_msg = NULL;
				wake_up_process(msr->r_tsk);
				smp_mb();
				msr->r_msg = ERR_PTR(-E2BIG);
			} else {
				msr->r_msg = NULL;
625
				msq->q_lrpid = task_pid_vnr(msr->r_tsk);
L
Linus Torvalds 已提交
626 627 628 629
				msq->q_rtime = get_seconds();
				wake_up_process(msr->r_tsk);
				smp_mb();
				msr->r_msg = msg;
630

L
Linus Torvalds 已提交
631 632 633 634 635 636 637
				return 1;
			}
		}
	}
	return 0;
}

638 639
long do_msgsnd(int msqid, long mtype, void __user *mtext,
		size_t msgsz, int msgflg)
L
Linus Torvalds 已提交
640 641 642 643
{
	struct msg_queue *msq;
	struct msg_msg *msg;
	int err;
K
Kirill Korotaev 已提交
644 645 646
	struct ipc_namespace *ns;

	ns = current->nsproxy->ipc_ns;
647

K
Kirill Korotaev 已提交
648
	if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)
L
Linus Torvalds 已提交
649 650 651 652
		return -EINVAL;
	if (mtype < 1)
		return -EINVAL;

653
	msg = load_msg(mtext, msgsz);
654
	if (IS_ERR(msg))
L
Linus Torvalds 已提交
655 656 657 658 659
		return PTR_ERR(msg);

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

660 661 662
	msq = msg_lock_check(ns, msqid);
	if (IS_ERR(msq)) {
		err = PTR_ERR(msq);
L
Linus Torvalds 已提交
663
		goto out_free;
664
	}
L
Linus Torvalds 已提交
665 666 667 668

	for (;;) {
		struct msg_sender s;

669
		err = -EACCES;
670
		if (ipcperms(ns, &msq->q_perm, S_IWUGO))
L
Linus Torvalds 已提交
671 672 673 674 675 676
			goto out_unlock_free;

		err = security_msg_queue_msgsnd(msq, msg, msgflg);
		if (err)
			goto out_unlock_free;

677
		if (msgsz + msq->q_cbytes <= msq->q_qbytes &&
L
Linus Torvalds 已提交
678 679 680 681 682
				1 + msq->q_qnum <= msq->q_qbytes) {
			break;
		}

		/* queue full, wait: */
683 684
		if (msgflg & IPC_NOWAIT) {
			err = -EAGAIN;
L
Linus Torvalds 已提交
685 686 687 688 689 690 691 692 693 694 695 696 697 698
			goto out_unlock_free;
		}
		ss_add(msq, &s);
		ipc_rcu_getref(msq);
		msg_unlock(msq);
		schedule();

		ipc_lock_by_ptr(&msq->q_perm);
		ipc_rcu_putref(msq);
		if (msq->q_perm.deleted) {
			err = -EIDRM;
			goto out_unlock_free;
		}
		ss_del(&s);
699

L
Linus Torvalds 已提交
700
		if (signal_pending(current)) {
701
			err = -ERESTARTNOHAND;
L
Linus Torvalds 已提交
702 703 704 705
			goto out_unlock_free;
		}
	}

706
	msq->q_lspid = task_tgid_vnr(current);
L
Linus Torvalds 已提交
707 708
	msq->q_stime = get_seconds();

709
	if (!pipelined_send(msq, msg)) {
L
Lucas De Marchi 已提交
710
		/* no one is waiting for this message, enqueue it */
711
		list_add_tail(&msg->m_list, &msq->q_messages);
L
Linus Torvalds 已提交
712 713
		msq->q_cbytes += msgsz;
		msq->q_qnum++;
714 715
		atomic_add(msgsz, &ns->msg_bytes);
		atomic_inc(&ns->msg_hdrs);
L
Linus Torvalds 已提交
716
	}
717

L
Linus Torvalds 已提交
718 719 720 721 722 723
	err = 0;
	msg = NULL;

out_unlock_free:
	msg_unlock(msq);
out_free:
724
	if (msg != NULL)
L
Linus Torvalds 已提交
725 726 727 728
		free_msg(msg);
	return err;
}

729 730
SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
		int, msgflg)
731 732 733 734 735 736 737 738
{
	long mtype;

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

739
static inline int convert_mode(long *msgtyp, int msgflg)
L
Linus Torvalds 已提交
740
{
741
	/*
L
Linus Torvalds 已提交
742 743 744
	 *  find message of correct type.
	 *  msgtyp = 0 => get first.
	 *  msgtyp > 0 => get first message of matching type.
745
	 *  msgtyp < 0 => get message with least type must be < abs(msgtype).
L
Linus Torvalds 已提交
746
	 */
747
	if (*msgtyp == 0)
L
Linus Torvalds 已提交
748
		return SEARCH_ANY;
749 750
	if (*msgtyp < 0) {
		*msgtyp = -*msgtyp;
L
Linus Torvalds 已提交
751 752
		return SEARCH_LESSEQUAL;
	}
753
	if (msgflg & MSG_EXCEPT)
L
Linus Torvalds 已提交
754 755 756 757
		return SEARCH_NOTEQUAL;
	return SEARCH_EQUAL;
}

758 759 760 761 762 763 764 765 766 767 768 769 770 771
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;
}

772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
#ifdef CONFIG_CHECKPOINT_RESTORE
static inline struct msg_msg *fill_copy(unsigned long copy_nr,
					unsigned long msg_nr,
					struct msg_msg *msg,
					struct msg_msg *copy)
{
	if (copy_nr == msg_nr)
		return copy_msg(msg, copy);
	return NULL;
}

static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz,
					   int msgflg, long *msgtyp,
					   unsigned long *copy_number)
{
	struct msg_msg *copy;

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

static inline void free_copy(int msgflg, struct msg_msg *copy)
{
	if (msgflg & MSG_COPY)
		free_msg(copy);
}
#else
#define free_copy(msgflg, copy)				do {} while (0)
#define prepare_copy(buf, sz, msgflg, msgtyp, copy_nr)	ERR_PTR(-ENOSYS)
#define fill_copy(copy_nr, msg_nr, msg, copy)		NULL
#endif

811 812 813
long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
	       int msgflg,
	       long (*msg_handler)(void __user *, struct msg_msg *, size_t))
L
Linus Torvalds 已提交
814 815 816 817
{
	struct msg_queue *msq;
	struct msg_msg *msg;
	int mode;
K
Kirill Korotaev 已提交
818
	struct ipc_namespace *ns;
819 820
	struct msg_msg *copy;
	unsigned long __maybe_unused copy_number;
L
Linus Torvalds 已提交
821

822
	if (msqid < 0 || (long) bufsz < 0)
L
Linus Torvalds 已提交
823
		return -EINVAL;
824 825 826 827 828
	if (msgflg & MSG_COPY) {
		copy = prepare_copy(buf, bufsz, msgflg, &msgtyp, &copy_number);
		if (IS_ERR(copy))
			return PTR_ERR(copy);
	}
829
	mode = convert_mode(&msgtyp, msgflg);
K
Kirill Korotaev 已提交
830
	ns = current->nsproxy->ipc_ns;
L
Linus Torvalds 已提交
831

832
	msq = msg_lock_check(ns, msqid);
833 834
	if (IS_ERR(msq)) {
		free_copy(msgflg, copy);
835
		return PTR_ERR(msq);
836
	}
L
Linus Torvalds 已提交
837 838 839

	for (;;) {
		struct msg_receiver msr_d;
840
		struct list_head *tmp;
841
		long msg_counter = 0;
L
Linus Torvalds 已提交
842 843

		msg = ERR_PTR(-EACCES);
844
		if (ipcperms(ns, &msq->q_perm, S_IRUGO))
L
Linus Torvalds 已提交
845 846 847 848 849 850
			goto out_unlock;

		msg = ERR_PTR(-EAGAIN);
		tmp = msq->q_messages.next;
		while (tmp != &msq->q_messages) {
			struct msg_msg *walk_msg;
851 852 853 854 855 856

			walk_msg = list_entry(tmp, struct msg_msg, m_list);
			if (testmsg(walk_msg, msgtyp, mode) &&
			    !security_msg_queue_msgrcv(msq, walk_msg, current,
						       msgtyp, mode)) {

L
Linus Torvalds 已提交
857
				msg = walk_msg;
858 859 860
				if (mode == SEARCH_LESSEQUAL &&
						walk_msg->m_type != 1) {
					msgtyp = walk_msg->m_type - 1;
861 862 863 864 865 866
				} else if (msgflg & MSG_COPY) {
					msg = fill_copy(copy_number,
							msg_counter,
							walk_msg, copy);
					if (msg)
						break;
867
				} else
L
Linus Torvalds 已提交
868
					break;
869
				msg_counter++;
L
Linus Torvalds 已提交
870 871 872
			}
			tmp = tmp->next;
		}
873 874 875 876 877
		if (!IS_ERR(msg)) {
			/*
			 * Found a suitable message.
			 * Unlink it from the queue.
			 */
878
			if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
L
Linus Torvalds 已提交
879 880 881
				msg = ERR_PTR(-E2BIG);
				goto out_unlock;
			}
882 883
			if (msgflg & MSG_COPY)
				goto out_unlock;
L
Linus Torvalds 已提交
884 885 886
			list_del(&msg->m_list);
			msq->q_qnum--;
			msq->q_rtime = get_seconds();
887
			msq->q_lrpid = task_tgid_vnr(current);
L
Linus Torvalds 已提交
888
			msq->q_cbytes -= msg->m_ts;
889 890
			atomic_sub(msg->m_ts, &ns->msg_bytes);
			atomic_dec(&ns->msg_hdrs);
891
			ss_wakeup(&msq->q_senders, 0);
L
Linus Torvalds 已提交
892 893 894 895 896 897 898 899
			msg_unlock(msq);
			break;
		}
		/* No message waiting. Wait for a message */
		if (msgflg & IPC_NOWAIT) {
			msg = ERR_PTR(-ENOMSG);
			goto out_unlock;
		}
900
		list_add_tail(&msr_d.r_list, &msq->q_receivers);
L
Linus Torvalds 已提交
901 902 903
		msr_d.r_tsk = current;
		msr_d.r_msgtype = msgtyp;
		msr_d.r_mode = mode;
904
		if (msgflg & MSG_NOERROR)
L
Linus Torvalds 已提交
905
			msr_d.r_maxsize = INT_MAX;
906
		else
907
			msr_d.r_maxsize = bufsz;
L
Linus Torvalds 已提交
908 909 910 911 912 913 914 915 916 917
		msr_d.r_msg = ERR_PTR(-EAGAIN);
		current->state = TASK_INTERRUPTIBLE;
		msg_unlock(msq);

		schedule();

		/* Lockless receive, part 1:
		 * Disable preemption.  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
L
Lucas De Marchi 已提交
918
		 * existence of msq:
L
Linus Torvalds 已提交
919 920 921 922 923 924 925 926 927 928 929 930
		 * Prior to destruction, expunge_all(-EIRDM) changes r_msg.
		 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
		 * rcu_read_lock() prevents preemption between reading r_msg
		 * and the spin_lock() inside ipc_lock_by_ptr().
		 */
		rcu_read_lock();

		/* Lockless receive, part 2:
		 * Wait until pipelined_send or expunge_all are outside of
		 * wake_up_process(). There is a race with exit(), see
		 * ipc/mqueue.c for the details.
		 */
931
		msg = (struct msg_msg*)msr_d.r_msg;
L
Linus Torvalds 已提交
932 933
		while (msg == NULL) {
			cpu_relax();
934
			msg = (struct msg_msg *)msr_d.r_msg;
L
Linus Torvalds 已提交
935 936 937 938 939 940
		}

		/* Lockless receive, part 3:
		 * If there is a message or an error then accept it without
		 * locking.
		 */
941
		if (msg != ERR_PTR(-EAGAIN)) {
L
Linus Torvalds 已提交
942 943 944 945 946 947 948 949 950 951 952 953 954 955
			rcu_read_unlock();
			break;
		}

		/* Lockless receive, part 3:
		 * Acquire the queue spinlock.
		 */
		ipc_lock_by_ptr(&msq->q_perm);
		rcu_read_unlock();

		/* Lockless receive, part 4:
		 * Repeat test after acquiring the spinlock.
		 */
		msg = (struct msg_msg*)msr_d.r_msg;
956
		if (msg != ERR_PTR(-EAGAIN))
L
Linus Torvalds 已提交
957 958 959 960 961 962 963 964 965 966
			goto out_unlock;

		list_del(&msr_d.r_list);
		if (signal_pending(current)) {
			msg = ERR_PTR(-ERESTARTNOHAND);
out_unlock:
			msg_unlock(msq);
			break;
		}
	}
967 968
	if (IS_ERR(msg)) {
		free_copy(msgflg, copy);
969
		return PTR_ERR(msg);
970
	}
L
Linus Torvalds 已提交
971

972
	bufsz = msg_handler(buf, msg, bufsz);
L
Linus Torvalds 已提交
973
	free_msg(msg);
974

975
	return bufsz;
L
Linus Torvalds 已提交
976 977
}

978 979
SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
		long, msgtyp, int, msgflg)
980
{
981
	return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill);
982 983
}

L
Linus Torvalds 已提交
984
#ifdef CONFIG_PROC_FS
985
static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
L
Linus Torvalds 已提交
986
{
987
	struct user_namespace *user_ns = seq_user_ns(s);
988 989 990
	struct msg_queue *msq = it;

	return seq_printf(s,
991 992
			"%10d %10d  %4o  %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
			msq->q_perm.key,
N
Nadia Derbey 已提交
993
			msq->q_perm.id,
994 995 996 997 998
			msq->q_perm.mode,
			msq->q_cbytes,
			msq->q_qnum,
			msq->q_lspid,
			msq->q_lrpid,
999 1000 1001 1002
			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),
1003 1004 1005
			msq->q_stime,
			msq->q_rtime,
			msq->q_ctime);
L
Linus Torvalds 已提交
1006 1007
}
#endif