msg.c 20.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2
/*
 * linux/ipc/msg.c
3
 * Copyright (C) 1992 Krishna Balasubramanian
Linus Torvalds's avatar
Linus Torvalds committed
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>
Steve Grubb's avatar
Steve Grubb committed
16 17 18
 *
 * support for audit of ipc object properties and permission changes
 * Dustin Kirkland <dustin.kirkland@us.ibm.com>
Kirill Korotaev's avatar
Kirill Korotaev committed
19 20 21 22
 *
 * namespaces support
 * OpenVZ, SWsoft Inc.
 * Pavel Emelianov <xemul@openvz.org>
Linus Torvalds's avatar
Linus Torvalds committed
23 24
 */

25
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
26 27 28 29 30 31 32 33 34 35
#include <linux/slab.h>
#include <linux/msg.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#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>
Ingo Molnar's avatar
Ingo Molnar committed
37
#include <linux/mutex.h>
Kirill Korotaev's avatar
Kirill Korotaev committed
38
#include <linux/nsproxy.h>
Ingo Molnar's avatar
Ingo Molnar committed
39

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

44 45 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		*volatile 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;
Linus Torvalds's avatar
Linus Torvalds committed
62 63 64 65 66 67 68
};

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

69 70
static atomic_t msg_bytes =	ATOMIC_INIT(0);
static atomic_t msg_hdrs =	ATOMIC_INIT(0);
Linus Torvalds's avatar
Linus Torvalds committed
71

Kirill Korotaev's avatar
Kirill Korotaev committed
72
static struct ipc_ids init_msg_ids;
Linus Torvalds's avatar
Linus Torvalds committed
73

Kirill Korotaev's avatar
Kirill Korotaev committed
74
#define msg_ids(ns)	(*((ns)->ids[IPC_MSG_IDS]))
Linus Torvalds's avatar
Linus Torvalds committed
75

Kirill Korotaev's avatar
Kirill Korotaev committed
76 77 78 79 80 81 82
#define msg_lock(ns, id)	((struct msg_queue*)ipc_lock(&msg_ids(ns), id))
#define msg_unlock(msq)		ipc_unlock(&(msq)->q_perm)
#define msg_checkid(ns, msq, msgid)	\
	ipc_checkid(&msg_ids(ns), &msq->q_perm, msgid)
#define msg_buildid(ns, id, seq) \
	ipc_buildid(&msg_ids(ns), id, seq)

Nadia Derbey's avatar
Nadia Derbey committed
83
static void freeque(struct ipc_namespace *, struct msg_queue *);
Kirill Korotaev's avatar
Kirill Korotaev committed
84
static int newque (struct ipc_namespace *ns, key_t key, int msgflg);
Linus Torvalds's avatar
Linus Torvalds committed
85
#ifdef CONFIG_PROC_FS
86
static int sysvipc_msg_proc_show(struct seq_file *s, void *it);
Linus Torvalds's avatar
Linus Torvalds committed
87 88
#endif

89
static void __msg_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids)
Kirill Korotaev's avatar
Kirill Korotaev committed
90 91 92 93 94
{
	ns->ids[IPC_MSG_IDS] = ids;
	ns->msg_ctlmax = MSGMAX;
	ns->msg_ctlmnb = MSGMNB;
	ns->msg_ctlmni = MSGMNI;
Nadia Derbey's avatar
Nadia Derbey committed
95
	ipc_init_ids(ids);
Kirill Korotaev's avatar
Kirill Korotaev committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
}

int msg_init_ns(struct ipc_namespace *ns)
{
	struct ipc_ids *ids;

	ids = kmalloc(sizeof(struct ipc_ids), GFP_KERNEL);
	if (ids == NULL)
		return -ENOMEM;

	__msg_init_ns(ns, ids);
	return 0;
}

void msg_exit_ns(struct ipc_namespace *ns)
{
	struct msg_queue *msq;
Nadia Derbey's avatar
Nadia Derbey committed
113 114
	int next_id;
	int total, in_use;
Kirill Korotaev's avatar
Kirill Korotaev committed
115 116

	mutex_lock(&msg_ids(ns).mutex);
Nadia Derbey's avatar
Nadia Derbey committed
117 118 119 120 121

	in_use = msg_ids(ns).in_use;

	for (total = 0, next_id = 0; total < in_use; next_id++) {
		msq = idr_find(&msg_ids(ns).ipcs_idr, next_id);
Kirill Korotaev's avatar
Kirill Korotaev committed
122 123
		if (msq == NULL)
			continue;
Nadia Derbey's avatar
Nadia Derbey committed
124 125 126
		ipc_lock_by_ptr(&msq->q_perm);
		freeque(ns, msq);
		total++;
Kirill Korotaev's avatar
Kirill Korotaev committed
127 128 129 130 131 132 133
	}
	mutex_unlock(&msg_ids(ns).mutex);

	kfree(ns->ids[IPC_MSG_IDS]);
	ns->ids[IPC_MSG_IDS] = NULL;
}

134
void __init msg_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
135
{
Kirill Korotaev's avatar
Kirill Korotaev committed
136
	__msg_init_ns(&init_ipc_ns, &init_msg_ids);
137 138
	ipc_init_proc_interface("sysvipc/msg",
				"       key      msqid perms      cbytes       qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n",
Kirill Korotaev's avatar
Kirill Korotaev committed
139
				IPC_MSG_IDS, sysvipc_msg_proc_show);
Linus Torvalds's avatar
Linus Torvalds committed
140 141
}

Nadia Derbey's avatar
Nadia Derbey committed
142 143 144 145 146
static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
{
	ipc_rmid(&msg_ids(ns), &s->q_perm);
}

Kirill Korotaev's avatar
Kirill Korotaev committed
147
static int newque (struct ipc_namespace *ns, key_t key, int msgflg)
Linus Torvalds's avatar
Linus Torvalds committed
148 149
{
	struct msg_queue *msq;
150
	int id, retval;
Linus Torvalds's avatar
Linus Torvalds committed
151

152 153
	msq = ipc_rcu_alloc(sizeof(*msq));
	if (!msq)
Linus Torvalds's avatar
Linus Torvalds committed
154 155
		return -ENOMEM;

156
	msq->q_perm.mode = msgflg & S_IRWXUGO;
Linus Torvalds's avatar
Linus Torvalds committed
157 158 159 160 161 162 163 164 165
	msq->q_perm.key = key;

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

Nadia Derbey's avatar
Nadia Derbey committed
166 167 168
	/*
	 * ipc_addid() locks msq
	 */
Kirill Korotaev's avatar
Kirill Korotaev committed
169
	id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
170
	if (id == -1) {
Linus Torvalds's avatar
Linus Torvalds committed
171 172 173 174 175
		security_msg_queue_free(msq);
		ipc_rcu_putref(msq);
		return -ENOSPC;
	}

Nadia Derbey's avatar
Nadia Derbey committed
176
	msq->q_perm.id = msg_buildid(ns, id, msq->q_perm.seq);
Linus Torvalds's avatar
Linus Torvalds committed
177 178 179
	msq->q_stime = msq->q_rtime = 0;
	msq->q_ctime = get_seconds();
	msq->q_cbytes = msq->q_qnum = 0;
Kirill Korotaev's avatar
Kirill Korotaev committed
180
	msq->q_qbytes = ns->msg_ctlmnb;
Linus Torvalds's avatar
Linus Torvalds committed
181 182 183 184
	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
185

Linus Torvalds's avatar
Linus Torvalds committed
186 187
	msg_unlock(msq);

Nadia Derbey's avatar
Nadia Derbey committed
188
	return msq->q_perm.id;
Linus Torvalds's avatar
Linus Torvalds committed
189 190
}

191
static inline void ss_add(struct msg_queue *msq, struct msg_sender *mss)
Linus Torvalds's avatar
Linus Torvalds committed
192
{
193 194 195
	mss->tsk = current;
	current->state = TASK_INTERRUPTIBLE;
	list_add_tail(&mss->list, &msq->q_senders);
Linus Torvalds's avatar
Linus Torvalds committed
196 197
}

198
static inline void ss_del(struct msg_sender *mss)
Linus Torvalds's avatar
Linus Torvalds committed
199
{
200
	if (mss->list.next != NULL)
Linus Torvalds's avatar
Linus Torvalds committed
201 202 203
		list_del(&mss->list);
}

204
static void ss_wakeup(struct list_head *h, int kill)
Linus Torvalds's avatar
Linus Torvalds committed
205 206 207 208 209
{
	struct list_head *tmp;

	tmp = h->next;
	while (tmp != h) {
210 211 212
		struct msg_sender *mss;

		mss = list_entry(tmp, struct msg_sender, list);
Linus Torvalds's avatar
Linus Torvalds committed
213
		tmp = tmp->next;
214 215
		if (kill)
			mss->list.next = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
216 217 218 219
		wake_up_process(mss->tsk);
	}
}

220
static void expunge_all(struct msg_queue *msq, int res)
Linus Torvalds's avatar
Linus Torvalds committed
221 222 223 224 225
{
	struct list_head *tmp;

	tmp = msq->q_receivers.next;
	while (tmp != &msq->q_receivers) {
226 227 228
		struct msg_receiver *msr;

		msr = list_entry(tmp, struct msg_receiver, r_list);
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231 232 233 234 235
		tmp = tmp->next;
		msr->r_msg = NULL;
		wake_up_process(msr->r_tsk);
		smp_mb();
		msr->r_msg = ERR_PTR(res);
	}
}
236 237 238 239

/*
 * freeque() wakes up waiters on the sender and receiver waiting queue,
 * removes the message queue from message queue ID
Nadia Derbey's avatar
Nadia Derbey committed
240
 * IDR, and cleans up all the messages associated with this queue.
Linus Torvalds's avatar
Linus Torvalds committed
241
 *
Nadia Derbey's avatar
Nadia Derbey committed
242
 * msg_ids.mutex and the spinlock for this message queue are held
Ingo Molnar's avatar
Ingo Molnar committed
243
 * before freeque() is called. msg_ids.mutex remains locked on exit.
Linus Torvalds's avatar
Linus Torvalds committed
244
 */
Nadia Derbey's avatar
Nadia Derbey committed
245
static void freeque(struct ipc_namespace *ns, struct msg_queue *msq)
Linus Torvalds's avatar
Linus Torvalds committed
246 247 248
{
	struct list_head *tmp;

249 250
	expunge_all(msq, -EIDRM);
	ss_wakeup(&msq->q_senders, 1);
Nadia Derbey's avatar
Nadia Derbey committed
251
	msg_rmid(ns, msq);
Linus Torvalds's avatar
Linus Torvalds committed
252
	msg_unlock(msq);
253

Linus Torvalds's avatar
Linus Torvalds committed
254
	tmp = msq->q_messages.next;
255 256 257
	while (tmp != &msq->q_messages) {
		struct msg_msg *msg = list_entry(tmp, struct msg_msg, m_list);

Linus Torvalds's avatar
Linus Torvalds committed
258 259 260 261 262 263 264 265 266
		tmp = tmp->next;
		atomic_dec(&msg_hdrs);
		free_msg(msg);
	}
	atomic_sub(msq->q_cbytes, &msg_bytes);
	security_msg_queue_free(msq);
	ipc_rcu_putref(msq);
}

267
asmlinkage long sys_msgget(key_t key, int msgflg)
Linus Torvalds's avatar
Linus Torvalds committed
268 269
{
	struct msg_queue *msq;
Nadia Derbey's avatar
Nadia Derbey committed
270
	int ret;
Kirill Korotaev's avatar
Kirill Korotaev committed
271 272 273
	struct ipc_namespace *ns;

	ns = current->nsproxy->ipc_ns;
Nadia Derbey's avatar
Nadia Derbey committed
274 275 276 277 278 279 280 281

	ret = idr_pre_get(&msg_ids(ns).ipcs_idr, GFP_KERNEL);

	if (key == IPC_PRIVATE)  {
		if (!ret)
			ret = -ENOMEM;
		else {
			mutex_lock(&msg_ids(ns).mutex);
Kirill Korotaev's avatar
Kirill Korotaev committed
282
			ret = newque(ns, key, msgflg);
Nadia Derbey's avatar
Nadia Derbey committed
283 284
			mutex_unlock(&msg_ids(ns).mutex);
		}
Linus Torvalds's avatar
Linus Torvalds committed
285
	} else {
Nadia Derbey's avatar
Nadia Derbey committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
		mutex_lock(&msg_ids(ns).mutex);
		msq = (struct msg_queue *) ipc_findkey(&msg_ids(ns), key);
		if (msq == NULL) {
			/* key not used */
			if (!(msgflg & IPC_CREAT))
				ret = -ENOENT;
			else if (!ret)
				ret = -ENOMEM;
			else
				ret = newque(ns, key, msgflg);
		} else {
			/* msq has been locked by ipc_findkey() */

			if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
				ret = -EEXIST;
			else {
				if (ipcperms(&msq->q_perm, msgflg))
					ret = -EACCES;
				else {
					ret = security_msg_queue_associate(
								msq, msgflg);
					if (!ret)
						ret = msq->q_perm.id;
				}
			}
			msg_unlock(msq);
Linus Torvalds's avatar
Linus Torvalds committed
312
		}
Nadia Derbey's avatar
Nadia Derbey committed
313
		mutex_unlock(&msg_ids(ns).mutex);
Linus Torvalds's avatar
Linus Torvalds committed
314
	}
315

Linus Torvalds's avatar
Linus Torvalds committed
316 317 318
	return ret;
}

319 320
static inline unsigned long
copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
Linus Torvalds's avatar
Linus Torvalds committed
321 322 323
{
	switch(version) {
	case IPC_64:
324
		return copy_to_user(buf, in, sizeof(*in));
Linus Torvalds's avatar
Linus Torvalds committed
325
	case IPC_OLD:
326
	{
Linus Torvalds's avatar
Linus Torvalds committed
327 328
		struct msqid_ds out;

329
		memset(&out, 0, sizeof(out));
Linus Torvalds's avatar
Linus Torvalds committed
330 331 332 333 334 335 336

		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;

337
		if (in->msg_cbytes > USHRT_MAX)
Linus Torvalds's avatar
Linus Torvalds committed
338 339 340 341 342
			out.msg_cbytes	= USHRT_MAX;
		else
			out.msg_cbytes	= in->msg_cbytes;
		out.msg_lcbytes		= in->msg_cbytes;

343
		if (in->msg_qnum > USHRT_MAX)
Linus Torvalds's avatar
Linus Torvalds committed
344 345 346 347
			out.msg_qnum	= USHRT_MAX;
		else
			out.msg_qnum	= in->msg_qnum;

348
		if (in->msg_qbytes > USHRT_MAX)
Linus Torvalds's avatar
Linus Torvalds committed
349 350 351 352 353 354 355 356
			out.msg_qbytes	= USHRT_MAX;
		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;

357 358
		return copy_to_user(buf, &out, sizeof(out));
	}
Linus Torvalds's avatar
Linus Torvalds committed
359 360 361 362 363 364 365 366 367 368 369 370
	default:
		return -EINVAL;
	}
}

struct msq_setbuf {
	unsigned long	qbytes;
	uid_t		uid;
	gid_t		gid;
	mode_t		mode;
};

371 372
static inline unsigned long
copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version)
Linus Torvalds's avatar
Linus Torvalds committed
373 374 375
{
	switch(version) {
	case IPC_64:
376
	{
Linus Torvalds's avatar
Linus Torvalds committed
377 378
		struct msqid64_ds tbuf;

379
		if (copy_from_user(&tbuf, buf, sizeof(tbuf)))
Linus Torvalds's avatar
Linus Torvalds committed
380 381 382 383 384 385 386 387
			return -EFAULT;

		out->qbytes		= tbuf.msg_qbytes;
		out->uid		= tbuf.msg_perm.uid;
		out->gid		= tbuf.msg_perm.gid;
		out->mode		= tbuf.msg_perm.mode;

		return 0;
388
	}
Linus Torvalds's avatar
Linus Torvalds committed
389
	case IPC_OLD:
390
	{
Linus Torvalds's avatar
Linus Torvalds committed
391 392
		struct msqid_ds tbuf_old;

393
		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
Linus Torvalds's avatar
Linus Torvalds committed
394 395 396 397 398 399
			return -EFAULT;

		out->uid		= tbuf_old.msg_perm.uid;
		out->gid		= tbuf_old.msg_perm.gid;
		out->mode		= tbuf_old.msg_perm.mode;

400
		if (tbuf_old.msg_qbytes == 0)
Linus Torvalds's avatar
Linus Torvalds committed
401 402 403 404 405
			out->qbytes	= tbuf_old.msg_lqbytes;
		else
			out->qbytes	= tbuf_old.msg_qbytes;

		return 0;
406
	}
Linus Torvalds's avatar
Linus Torvalds committed
407 408 409 410 411
	default:
		return -EINVAL;
	}
}

412
asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
Linus Torvalds's avatar
Linus Torvalds committed
413 414
{
	struct kern_ipc_perm *ipcp;
415
	struct msq_setbuf uninitialized_var(setbuf);
416 417
	struct msg_queue *msq;
	int err, version;
Kirill Korotaev's avatar
Kirill Korotaev committed
418
	struct ipc_namespace *ns;
419

Linus Torvalds's avatar
Linus Torvalds committed
420 421 422 423
	if (msqid < 0 || cmd < 0)
		return -EINVAL;

	version = ipc_parse_version(&cmd);
Kirill Korotaev's avatar
Kirill Korotaev committed
424
	ns = current->nsproxy->ipc_ns;
Linus Torvalds's avatar
Linus Torvalds committed
425 426

	switch (cmd) {
427 428 429
	case IPC_INFO:
	case MSG_INFO:
	{
Linus Torvalds's avatar
Linus Torvalds committed
430 431
		struct msginfo msginfo;
		int max_id;
432

Linus Torvalds's avatar
Linus Torvalds committed
433 434
		if (!buf)
			return -EFAULT;
435 436
		/*
		 * We must not return kernel stack data.
Linus Torvalds's avatar
Linus Torvalds committed
437 438 439 440 441 442 443
		 * due to padding, it's not enough
		 * to set all member fields.
		 */
		err = security_msg_queue_msgctl(NULL, cmd);
		if (err)
			return err;

444
		memset(&msginfo, 0, sizeof(msginfo));
Kirill Korotaev's avatar
Kirill Korotaev committed
445 446 447
		msginfo.msgmni = ns->msg_ctlmni;
		msginfo.msgmax = ns->msg_ctlmax;
		msginfo.msgmnb = ns->msg_ctlmnb;
Linus Torvalds's avatar
Linus Torvalds committed
448 449
		msginfo.msgssz = MSGSSZ;
		msginfo.msgseg = MSGSEG;
Kirill Korotaev's avatar
Kirill Korotaev committed
450
		mutex_lock(&msg_ids(ns).mutex);
Linus Torvalds's avatar
Linus Torvalds committed
451
		if (cmd == MSG_INFO) {
Kirill Korotaev's avatar
Kirill Korotaev committed
452
			msginfo.msgpool = msg_ids(ns).in_use;
Linus Torvalds's avatar
Linus Torvalds committed
453 454 455 456 457 458 459
			msginfo.msgmap = atomic_read(&msg_hdrs);
			msginfo.msgtql = atomic_read(&msg_bytes);
		} else {
			msginfo.msgmap = MSGMAP;
			msginfo.msgpool = MSGPOOL;
			msginfo.msgtql = MSGTQL;
		}
Nadia Derbey's avatar
Nadia Derbey committed
460
		max_id = ipc_get_maxid(&msg_ids(ns));
Kirill Korotaev's avatar
Kirill Korotaev committed
461
		mutex_unlock(&msg_ids(ns).mutex);
462
		if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
Linus Torvalds's avatar
Linus Torvalds committed
463
			return -EFAULT;
464
		return (max_id < 0) ? 0 : max_id;
Linus Torvalds's avatar
Linus Torvalds committed
465
	}
Nadia Derbey's avatar
Nadia Derbey committed
466
	case MSG_STAT:	/* msqid is an index rather than a msg queue id */
Linus Torvalds's avatar
Linus Torvalds committed
467 468 469 470
	case IPC_STAT:
	{
		struct msqid64_ds tbuf;
		int success_return;
471

Linus Torvalds's avatar
Linus Torvalds committed
472 473 474
		if (!buf)
			return -EFAULT;

475
		memset(&tbuf, 0, sizeof(tbuf));
Linus Torvalds's avatar
Linus Torvalds committed
476

Kirill Korotaev's avatar
Kirill Korotaev committed
477
		msq = msg_lock(ns, msqid);
Linus Torvalds's avatar
Linus Torvalds committed
478 479 480
		if (msq == NULL)
			return -EINVAL;

481
		if (cmd == MSG_STAT) {
Nadia Derbey's avatar
Nadia Derbey committed
482
			success_return = msq->q_perm.id;
Linus Torvalds's avatar
Linus Torvalds committed
483 484
		} else {
			err = -EIDRM;
Kirill Korotaev's avatar
Kirill Korotaev committed
485
			if (msg_checkid(ns, msq, msqid))
Linus Torvalds's avatar
Linus Torvalds committed
486 487 488 489
				goto out_unlock;
			success_return = 0;
		}
		err = -EACCES;
490
		if (ipcperms(&msq->q_perm, S_IRUGO))
Linus Torvalds's avatar
Linus Torvalds committed
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
			goto out_unlock;

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

		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:
		if (!buf)
			return -EFAULT;
514
		if (copy_msqid_from_user(&setbuf, buf, version))
Linus Torvalds's avatar
Linus Torvalds committed
515 516 517 518 519 520 521 522
			return -EFAULT;
		break;
	case IPC_RMID:
		break;
	default:
		return  -EINVAL;
	}

Kirill Korotaev's avatar
Kirill Korotaev committed
523 524
	mutex_lock(&msg_ids(ns).mutex);
	msq = msg_lock(ns, msqid);
525
	err = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
526 527 528 529
	if (msq == NULL)
		goto out_up;

	err = -EIDRM;
Kirill Korotaev's avatar
Kirill Korotaev committed
530
	if (msg_checkid(ns, msq, msqid))
Linus Torvalds's avatar
Linus Torvalds committed
531 532
		goto out_unlock_up;
	ipcp = &msq->q_perm;
Steve Grubb's avatar
Steve Grubb committed
533 534 535 536

	err = audit_ipc_obj(ipcp);
	if (err)
		goto out_unlock_up;
537
	if (cmd == IPC_SET) {
538 539
		err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid,
					 setbuf.mode);
540 541 542
		if (err)
			goto out_unlock_up;
	}
Steve Grubb's avatar
Steve Grubb committed
543

Linus Torvalds's avatar
Linus Torvalds committed
544
	err = -EPERM;
545
	if (current->euid != ipcp->cuid &&
Linus Torvalds's avatar
Linus Torvalds committed
546
	    current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
547
		/* We _could_ check for CAP_CHOWN above, but we don't */
Linus Torvalds's avatar
Linus Torvalds committed
548 549 550 551 552 553 554 555 556 557
		goto out_unlock_up;

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

	switch (cmd) {
	case IPC_SET:
	{
		err = -EPERM;
Kirill Korotaev's avatar
Kirill Korotaev committed
558
		if (setbuf.qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
Linus Torvalds's avatar
Linus Torvalds committed
559 560 561 562 563 564
			goto out_unlock_up;

		msq->q_qbytes = setbuf.qbytes;

		ipcp->uid = setbuf.uid;
		ipcp->gid = setbuf.gid;
565 566
		ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
			     (S_IRWXUGO & setbuf.mode);
Linus Torvalds's avatar
Linus Torvalds committed
567 568 569 570
		msq->q_ctime = get_seconds();
		/* sleeping receivers might be excluded by
		 * stricter permissions.
		 */
571
		expunge_all(msq, -EAGAIN);
Linus Torvalds's avatar
Linus Torvalds committed
572 573 574
		/* sleeping senders might be able to send
		 * due to a larger queue size.
		 */
575
		ss_wakeup(&msq->q_senders, 0);
Linus Torvalds's avatar
Linus Torvalds committed
576 577 578 579
		msg_unlock(msq);
		break;
	}
	case IPC_RMID:
Nadia Derbey's avatar
Nadia Derbey committed
580
		freeque(ns, msq);
Linus Torvalds's avatar
Linus Torvalds committed
581 582 583 584
		break;
	}
	err = 0;
out_up:
Kirill Korotaev's avatar
Kirill Korotaev committed
585
	mutex_unlock(&msg_ids(ns).mutex);
Linus Torvalds's avatar
Linus Torvalds committed
586 587 588 589 590 591 592 593 594
	return err;
out_unlock_up:
	msg_unlock(msq);
	goto out_up;
out_unlock:
	msg_unlock(msq);
	return err;
}

595
static int testmsg(struct msg_msg *msg, long type, int mode)
Linus Torvalds's avatar
Linus Torvalds committed
596 597 598 599 600 601
{
	switch(mode)
	{
		case SEARCH_ANY:
			return 1;
		case SEARCH_LESSEQUAL:
602
			if (msg->m_type <=type)
Linus Torvalds's avatar
Linus Torvalds committed
603 604 605
				return 1;
			break;
		case SEARCH_EQUAL:
606
			if (msg->m_type == type)
Linus Torvalds's avatar
Linus Torvalds committed
607 608 609
				return 1;
			break;
		case SEARCH_NOTEQUAL:
610
			if (msg->m_type != type)
Linus Torvalds's avatar
Linus Torvalds committed
611 612 613 614 615 616
				return 1;
			break;
	}
	return 0;
}

617
static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg)
Linus Torvalds's avatar
Linus Torvalds committed
618
{
619
	struct list_head *tmp;
Linus Torvalds's avatar
Linus Torvalds committed
620 621 622

	tmp = msq->q_receivers.next;
	while (tmp != &msq->q_receivers) {
623 624 625
		struct msg_receiver *msr;

		msr = list_entry(tmp, struct msg_receiver, r_list);
Linus Torvalds's avatar
Linus Torvalds committed
626
		tmp = tmp->next;
627 628 629 630
		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
631
			list_del(&msr->r_list);
632
			if (msr->r_maxsize < msg->m_ts) {
Linus Torvalds's avatar
Linus Torvalds committed
633 634 635 636 637 638
				msr->r_msg = NULL;
				wake_up_process(msr->r_tsk);
				smp_mb();
				msr->r_msg = ERR_PTR(-E2BIG);
			} else {
				msr->r_msg = NULL;
639
				msq->q_lrpid = task_pid_vnr(msr->r_tsk);
Linus Torvalds's avatar
Linus Torvalds committed
640 641 642 643
				msq->q_rtime = get_seconds();
				wake_up_process(msr->r_tsk);
				smp_mb();
				msr->r_msg = msg;
644

Linus Torvalds's avatar
Linus Torvalds committed
645 646 647 648 649 650 651
				return 1;
			}
		}
	}
	return 0;
}

652 653
long do_msgsnd(int msqid, long mtype, void __user *mtext,
		size_t msgsz, int msgflg)
Linus Torvalds's avatar
Linus Torvalds committed
654 655 656 657
{
	struct msg_queue *msq;
	struct msg_msg *msg;
	int err;
Kirill Korotaev's avatar
Kirill Korotaev committed
658 659 660
	struct ipc_namespace *ns;

	ns = current->nsproxy->ipc_ns;
661

Kirill Korotaev's avatar
Kirill Korotaev committed
662
	if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)
Linus Torvalds's avatar
Linus Torvalds committed
663 664 665 666
		return -EINVAL;
	if (mtype < 1)
		return -EINVAL;

667
	msg = load_msg(mtext, msgsz);
668
	if (IS_ERR(msg))
Linus Torvalds's avatar
Linus Torvalds committed
669 670 671 672 673
		return PTR_ERR(msg);

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

Kirill Korotaev's avatar
Kirill Korotaev committed
674
	msq = msg_lock(ns, msqid);
675 676
	err = -EINVAL;
	if (msq == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
677 678 679
		goto out_free;

	err= -EIDRM;
Kirill Korotaev's avatar
Kirill Korotaev committed
680
	if (msg_checkid(ns, msq, msqid))
Linus Torvalds's avatar
Linus Torvalds committed
681 682 683 684 685
		goto out_unlock_free;

	for (;;) {
		struct msg_sender s;

686
		err = -EACCES;
Linus Torvalds's avatar
Linus Torvalds committed
687 688 689 690 691 692 693
		if (ipcperms(&msq->q_perm, S_IWUGO))
			goto out_unlock_free;

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

694
		if (msgsz + msq->q_cbytes <= msq->q_qbytes &&
Linus Torvalds's avatar
Linus Torvalds committed
695 696 697 698 699
				1 + msq->q_qnum <= msq->q_qbytes) {
			break;
		}

		/* queue full, wait: */
700 701
		if (msgflg & IPC_NOWAIT) {
			err = -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
702 703 704 705 706 707 708 709 710 711 712 713 714 715
			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);
716

Linus Torvalds's avatar
Linus Torvalds committed
717
		if (signal_pending(current)) {
718
			err = -ERESTARTNOHAND;
Linus Torvalds's avatar
Linus Torvalds committed
719 720 721 722
			goto out_unlock_free;
		}
	}

723
	msq->q_lspid = task_tgid_vnr(current);
Linus Torvalds's avatar
Linus Torvalds committed
724 725
	msq->q_stime = get_seconds();

726
	if (!pipelined_send(msq, msg)) {
Linus Torvalds's avatar
Linus Torvalds committed
727
		/* noone is waiting for this message, enqueue it */
728
		list_add_tail(&msg->m_list, &msq->q_messages);
Linus Torvalds's avatar
Linus Torvalds committed
729 730
		msq->q_cbytes += msgsz;
		msq->q_qnum++;
731
		atomic_add(msgsz, &msg_bytes);
Linus Torvalds's avatar
Linus Torvalds committed
732 733
		atomic_inc(&msg_hdrs);
	}
734

Linus Torvalds's avatar
Linus Torvalds committed
735 736 737 738 739 740
	err = 0;
	msg = NULL;

out_unlock_free:
	msg_unlock(msq);
out_free:
741
	if (msg != NULL)
Linus Torvalds's avatar
Linus Torvalds committed
742 743 744 745
		free_msg(msg);
	return err;
}

746 747 748 749 750 751 752 753 754 755
asmlinkage long
sys_msgsnd(int msqid, struct msgbuf __user *msgp, size_t msgsz, int msgflg)
{
	long mtype;

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

756
static inline int convert_mode(long *msgtyp, int msgflg)
Linus Torvalds's avatar
Linus Torvalds committed
757
{
758
	/*
Linus Torvalds's avatar
Linus Torvalds committed
759 760 761
	 *  find message of correct type.
	 *  msgtyp = 0 => get first.
	 *  msgtyp > 0 => get first message of matching type.
762
	 *  msgtyp < 0 => get message with least type must be < abs(msgtype).
Linus Torvalds's avatar
Linus Torvalds committed
763
	 */
764
	if (*msgtyp == 0)
Linus Torvalds's avatar
Linus Torvalds committed
765
		return SEARCH_ANY;
766 767
	if (*msgtyp < 0) {
		*msgtyp = -*msgtyp;
Linus Torvalds's avatar
Linus Torvalds committed
768 769
		return SEARCH_LESSEQUAL;
	}
770
	if (msgflg & MSG_EXCEPT)
Linus Torvalds's avatar
Linus Torvalds committed
771 772 773 774
		return SEARCH_NOTEQUAL;
	return SEARCH_EQUAL;
}

775 776
long do_msgrcv(int msqid, long *pmtype, void __user *mtext,
		size_t msgsz, long msgtyp, int msgflg)
Linus Torvalds's avatar
Linus Torvalds committed
777 778 779 780
{
	struct msg_queue *msq;
	struct msg_msg *msg;
	int mode;
Kirill Korotaev's avatar
Kirill Korotaev committed
781
	struct ipc_namespace *ns;
Linus Torvalds's avatar
Linus Torvalds committed
782 783 784

	if (msqid < 0 || (long) msgsz < 0)
		return -EINVAL;
785
	mode = convert_mode(&msgtyp, msgflg);
Kirill Korotaev's avatar
Kirill Korotaev committed
786
	ns = current->nsproxy->ipc_ns;
Linus Torvalds's avatar
Linus Torvalds committed
787

Kirill Korotaev's avatar
Kirill Korotaev committed
788
	msq = msg_lock(ns, msqid);
789
	if (msq == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
790 791 792
		return -EINVAL;

	msg = ERR_PTR(-EIDRM);
Kirill Korotaev's avatar
Kirill Korotaev committed
793
	if (msg_checkid(ns, msq, msqid))
Linus Torvalds's avatar
Linus Torvalds committed
794 795 796 797
		goto out_unlock;

	for (;;) {
		struct msg_receiver msr_d;
798
		struct list_head *tmp;
Linus Torvalds's avatar
Linus Torvalds committed
799 800

		msg = ERR_PTR(-EACCES);
801
		if (ipcperms(&msq->q_perm, S_IRUGO))
Linus Torvalds's avatar
Linus Torvalds committed
802 803 804 805 806 807
			goto out_unlock;

		msg = ERR_PTR(-EAGAIN);
		tmp = msq->q_messages.next;
		while (tmp != &msq->q_messages) {
			struct msg_msg *walk_msg;
808 809 810 811 812 813

			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)) {

Linus Torvalds's avatar
Linus Torvalds committed
814
				msg = walk_msg;
815 816 817 818
				if (mode == SEARCH_LESSEQUAL &&
						walk_msg->m_type != 1) {
					msg = walk_msg;
					msgtyp = walk_msg->m_type - 1;
Linus Torvalds's avatar
Linus Torvalds committed
819
				} else {
820
					msg = walk_msg;
Linus Torvalds's avatar
Linus Torvalds committed
821 822 823 824 825
					break;
				}
			}
			tmp = tmp->next;
		}
826 827 828 829 830
		if (!IS_ERR(msg)) {
			/*
			 * Found a suitable message.
			 * Unlink it from the queue.
			 */
Linus Torvalds's avatar
Linus Torvalds committed
831 832 833 834 835 836 837
			if ((msgsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
				msg = ERR_PTR(-E2BIG);
				goto out_unlock;
			}
			list_del(&msg->m_list);
			msq->q_qnum--;
			msq->q_rtime = get_seconds();
838
			msq->q_lrpid = task_tgid_vnr(current);
Linus Torvalds's avatar
Linus Torvalds committed
839
			msq->q_cbytes -= msg->m_ts;
840
			atomic_sub(msg->m_ts, &msg_bytes);
Linus Torvalds's avatar
Linus Torvalds committed
841
			atomic_dec(&msg_hdrs);
842
			ss_wakeup(&msq->q_senders, 0);
Linus Torvalds's avatar
Linus Torvalds committed
843 844 845 846 847 848 849 850
			msg_unlock(msq);
			break;
		}
		/* No message waiting. Wait for a message */
		if (msgflg & IPC_NOWAIT) {
			msg = ERR_PTR(-ENOMSG);
			goto out_unlock;
		}
851
		list_add_tail(&msr_d.r_list, &msq->q_receivers);
Linus Torvalds's avatar
Linus Torvalds committed
852 853 854
		msr_d.r_tsk = current;
		msr_d.r_msgtype = msgtyp;
		msr_d.r_mode = mode;
855
		if (msgflg & MSG_NOERROR)
Linus Torvalds's avatar
Linus Torvalds committed
856
			msr_d.r_maxsize = INT_MAX;
857
		else
Linus Torvalds's avatar
Linus Torvalds committed
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
			msr_d.r_maxsize = msgsz;
		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
		 * existance of msq:
		 * 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.
		 */
882
		msg = (struct msg_msg*)msr_d.r_msg;
Linus Torvalds's avatar
Linus Torvalds committed
883 884
		while (msg == NULL) {
			cpu_relax();
885
			msg = (struct msg_msg *)msr_d.r_msg;
Linus Torvalds's avatar
Linus Torvalds committed
886 887 888 889 890 891
		}

		/* Lockless receive, part 3:
		 * If there is a message or an error then accept it without
		 * locking.
		 */
892
		if (msg != ERR_PTR(-EAGAIN)) {
Linus Torvalds's avatar
Linus Torvalds committed
893 894 895 896 897 898 899 900 901 902 903 904 905 906
			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;
907
		if (msg != ERR_PTR(-EAGAIN))
Linus Torvalds's avatar
Linus Torvalds committed
908 909 910 911 912 913 914 915 916 917 918
			goto out_unlock;

		list_del(&msr_d.r_list);
		if (signal_pending(current)) {
			msg = ERR_PTR(-ERESTARTNOHAND);
out_unlock:
			msg_unlock(msq);
			break;
		}
	}
	if (IS_ERR(msg))
919
		return PTR_ERR(msg);
Linus Torvalds's avatar
Linus Torvalds committed
920 921

	msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz;
922 923
	*pmtype = msg->m_type;
	if (store_msg(mtext, msg, msgsz))
924
		msgsz = -EFAULT;
925

Linus Torvalds's avatar
Linus Torvalds committed
926
	free_msg(msg);
927

Linus Torvalds's avatar
Linus Torvalds committed
928 929 930
	return msgsz;
}

931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
asmlinkage long sys_msgrcv(int msqid, struct msgbuf __user *msgp, size_t msgsz,
			   long msgtyp, int msgflg)
{
	long err, mtype;

	err =  do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg);
	if (err < 0)
		goto out;

	if (put_user(mtype, &msgp->mtype))
		err = -EFAULT;
out:
	return err;
}

Linus Torvalds's avatar
Linus Torvalds committed
946
#ifdef CONFIG_PROC_FS
947
static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
Linus Torvalds's avatar
Linus Torvalds committed
948
{
949 950 951
	struct msg_queue *msq = it;

	return seq_printf(s,
952 953
			"%10d %10d  %4o  %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
			msq->q_perm.key,
Nadia Derbey's avatar
Nadia Derbey committed
954
			msq->q_perm.id,
955 956 957 958 959 960 961 962 963 964 965 966
			msq->q_perm.mode,
			msq->q_cbytes,
			msq->q_qnum,
			msq->q_lspid,
			msq->q_lrpid,
			msq->q_perm.uid,
			msq->q_perm.gid,
			msq->q_perm.cuid,
			msq->q_perm.cgid,
			msq->q_stime,
			msq->q_rtime,
			msq->q_ctime);
Linus Torvalds's avatar
Linus Torvalds committed
967 968
}
#endif