xref: /openbsd-src/sys/kern/sysv_msg.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /*	$OpenBSD: sysv_msg.c,v 1.22 2009/08/09 10:40:17 blambert Exp $	*/
2 /*	$NetBSD: sysv_msg.c,v 1.19 1996/02/09 19:00:18 christos Exp $	*/
3 /*
4  * Copyright (c) 2009 Bret S. Lambert <blambert@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*
19  * Implementation of SVID messages
20  *
21  * Author:  Daniel Boulet
22  *
23  * Copyright 1993 Daniel Boulet and RTMX Inc.
24  *
25  * This system call was implemented by Daniel Boulet under contract from RTMX.
26  *
27  * Redistribution and use in source forms, with and without modification,
28  * are permitted provided that this entire comment appears intact.
29  *
30  * Redistribution in binary form may occur without any restrictions.
31  * Obviously, it would be nice if you gave credit where credit is due
32  * but requiring it would be too onerous.
33  *
34  * This software is provided ``AS IS'' without any warranties of any kind.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/mount.h>
42 #include <sys/msg.h>
43 #include <sys/pool.h>
44 #include <sys/proc.h>
45 #include <sys/queue.h>
46 #include <sys/syscallargs.h>
47 #include <sys/sysctl.h>
48 #include <sys/systm.h>
49 #include <sys/uio.h>
50 
51 struct que *que_create(key_t, struct ucred *, int);
52 struct que *que_lookup(int);
53 struct que *que_key_lookup(key_t);
54 void que_wakewriters(void);
55 void que_free(struct que *);
56 struct msg *msg_create(struct que *);
57 void msg_free(struct msg *);
58 void msg_enqueue(struct que *, struct msg *, struct proc *);
59 void msg_dequeue(struct que *, struct msg *, struct proc *);
60 struct msg *msg_lookup(struct que *, int);
61 int msg_copyin(struct msg *, const char *, size_t, struct proc *);
62 int msg_copyout(struct msg *, char *, size_t *, struct proc *);
63 
64 struct	pool sysvmsgpl;
65 struct	msginfo msginfo;
66 
67 TAILQ_HEAD(, que) msg_queues;
68 
69 int num_ques;
70 int num_msgs;
71 int sequence;
72 int maxmsgs;
73 
74 void
75 msginit(void)
76 {
77 	msginfo.msgmax = MSGMAX;
78 	msginfo.msgmni = MSGMNI;
79 	msginfo.msgmnb = MSGMNB;
80 	msginfo.msgtql = MSGTQL;
81 	msginfo.msgssz = MSGSSZ;
82 	msginfo.msgseg = MSGSEG;
83 
84 	pool_init(&sysvmsgpl, sizeof(struct msg), 0, 0, 0, "sysvmsgpl",
85 	    &pool_allocator_nointr);
86 
87 	TAILQ_INIT(&msg_queues);
88 
89 	num_ques = 0;
90 	num_msgs = 0;
91 	sequence = 1;
92 	maxmsgs = 0;
93 }
94 
95 int
96 sys_msgctl(struct proc *p, void *v, register_t *retval)
97 {
98 	struct sys_msgctl_args /* {
99 		syscallarg(int) msqid;
100 		syscallarg(int) cmd;
101 		syscallarg(struct msqid_ds *) buf;
102 	} */ *uap = v;
103 
104 	return (msgctl1(p, SCARG(uap, msqid), SCARG(uap, cmd),
105 	    (caddr_t)SCARG(uap, buf), copyin, copyout));
106 }
107 
108 int
109 msgctl1(struct proc *p, int msqid, int cmd, caddr_t buf,
110     int (*ds_copyin)(const void *, void *, size_t),
111     int (*ds_copyout)(const void *, void *, size_t))
112 {
113 	struct msqid_ds tmp;
114 	struct ucred *cred = p->p_ucred;
115 	struct que *que;
116 	int error = 0;
117 
118 	if ((que = que_lookup(msqid)) == NULL)
119 		return (EINVAL);
120 
121 	QREF(que);
122 
123 	switch (cmd) {
124 
125 	case IPC_RMID:
126 		if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M)))
127 			goto out;
128 
129 		TAILQ_REMOVE(&msg_queues, que, que_next);
130 		que->que_flags |= MSGQ_DYING;
131 
132 		/* lose interest in the queue and wait for others to too */
133 		if (--que->que_references > 0) {
134 			wakeup(que);
135 			tsleep(&que->que_references, PZERO, "msgqrm", 0);
136 		}
137 
138 		que_free(que);
139 
140 		return (0);
141 
142 	case IPC_SET:
143 		if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M)))
144 			goto out;
145 		if ((error = ds_copyin(buf, &tmp, sizeof(struct msqid_ds))))
146 			goto out;
147 
148 		/* only superuser can bump max bytes in queue */
149 		if (tmp.msg_qbytes > que->msqid_ds.msg_qbytes &&
150 		    cred->cr_uid != 0) {
151 			error = EPERM;
152 			goto out;
153 		}
154 
155 		/* restrict max bytes in queue to system limit */
156 		if (tmp.msg_qbytes > msginfo.msgmnb)
157 			tmp.msg_qbytes = msginfo.msgmnb;
158 
159 		/* can't reduce msg_bytes to 0 */
160 		if (tmp.msg_qbytes == 0) {
161 			error = EINVAL;		/* non-standard errno! */
162 			goto out;
163 		}
164 
165 		que->msqid_ds.msg_perm.uid = tmp.msg_perm.uid;
166 		que->msqid_ds.msg_perm.gid = tmp.msg_perm.gid;
167 		que->msqid_ds.msg_perm.mode =
168 		    (que->msqid_ds.msg_perm.mode & ~0777) |
169 		    (tmp.msg_perm.mode & 0777);
170 		que->msqid_ds.msg_qbytes = tmp.msg_qbytes;
171 		que->msqid_ds.msg_ctime = time_second;
172 		break;
173 
174 	case IPC_STAT:
175 		if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R)))
176 			goto out;
177 		error = ds_copyout(&que->msqid_ds, buf,
178 		    sizeof(struct msqid_ds));
179 		break;
180 
181 	default:
182 		error = EINVAL;
183 		break;
184 	}
185 out:
186 	QRELE(que);
187 
188 	return (error);
189 }
190 
191 int
192 sys_msgget(struct proc *p, void *v, register_t *retval)
193 {
194 	struct sys_msgget_args /* {
195 		syscallarg(key_t) key;
196 		syscallarg(int) msgflg;
197 	} */ *uap = v;
198 	struct ucred *cred = p->p_ucred;
199 	struct que *que;
200 	key_t key = SCARG(uap, key);
201 	int msgflg = SCARG(uap, msgflg);
202 	int error = 0;
203 
204 again:
205 	if (key != IPC_PRIVATE) {
206 		que = que_key_lookup(key);
207 		if (que) {
208 			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL))
209 				return (EEXIST);
210 			if ((error = ipcperm(cred, &que->msqid_ds.msg_perm,
211 			    msgflg & 0700)))
212 				return (error);
213 			goto found;
214 		}
215 	}
216 
217 	/* don't create a new message queue if the caller doesn't want to */
218 	if (key != IPC_PRIVATE && !(msgflg & IPC_CREAT))
219 		return (ENOENT);
220 
221 	/* enforce limits on the maximum number of message queues */
222 	if (num_ques >= msginfo.msgmni)
223 		return (ENOSPC);
224 
225 	/*
226 	 * if que_create returns NULL, it means that a que with an identical
227 	 * key was created while this process was sleeping, so start over
228 	 */
229 	if ((que = que_create(key, cred, msgflg & 0777)) == NULL)
230 		goto again;
231 
232 found:
233 	*retval = que->que_id;
234 	return (error);
235 }
236 
237 #define	MSGQ_SPACE(q)	((q)->msqid_ds.msg_qbytes - (q)->msqid_ds.msg_cbytes)
238 
239 int
240 sys_msgsnd(struct proc *p, void *v, register_t *retval)
241 {
242 	struct sys_msgsnd_args /* {
243 		syscallarg(int) msqid;
244 		syscallarg(const void *) msgp;
245 		syscallarg(size_t) msgsz;
246 		syscallarg(int) msgflg;
247 	} */ *uap = v;
248 	struct ucred *cred = p->p_ucred;
249 	struct que *que;
250 	struct msg *msg;
251 	size_t msgsz = SCARG(uap, msgsz);
252 	int error;
253 
254 	if (SCARG(uap, msgp) == NULL)
255 		panic("NULL userbuffer");
256 
257 	if ((que = que_lookup(SCARG(uap, msqid))) == NULL)
258 		return (EINVAL);
259 
260 	if (msgsz > que->msqid_ds.msg_qbytes || msgsz > msginfo.msgmax)
261 		return (EINVAL);
262 
263 	if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_W)))
264 		return (error);
265 
266 	QREF(que);
267 
268 	while (MSGQ_SPACE(que) < msgsz || num_msgs >= msginfo.msgtql) {
269 
270 		if (SCARG(uap, msgflg) & IPC_NOWAIT) {
271 			error = EAGAIN;
272 			goto out;
273 		}
274 
275 		/* notify world that process may wedge here */
276 		if (num_msgs >= msginfo.msgtql)
277 			maxmsgs = 1;
278 
279 		que->que_flags |= MSGQ_WRITERS;
280 		if ((error = tsleep(que, PZERO|PCATCH, "msgwait", 0)))
281 			goto out;
282 
283 		if (que->que_flags & MSGQ_DYING) {
284 			error = EIDRM;
285 			goto out;
286 		}
287 	}
288 
289 	/* if msg_create returns NULL, the queue is being removed */
290 	if ((msg = msg_create(que)) == NULL) {
291 		error = EIDRM;
292 		goto out;
293 	}
294 
295 	/* msg_copyin frees msg on error */
296 	if ((error = msg_copyin(msg, (const char *)SCARG(uap, msgp), msgsz, p)))
297 		goto out;
298 
299 	msg_enqueue(que, msg, p);
300 
301 	if (que->que_flags & MSGQ_READERS) {
302 		que->que_flags &= ~MSGQ_READERS;
303 		wakeup(que);
304 	}
305 
306 	if (que->que_flags & MSGQ_DYING) {
307 		error = EIDRM;
308 		wakeup(que);
309 	}
310 out:
311 	QRELE(que);
312 
313 	return (error);
314 }
315 
316 int
317 sys_msgrcv(struct proc *p, void *v, register_t *retval)
318 {
319 	struct sys_msgrcv_args /* {
320 		syscallarg(int) msqid;
321 		syscallarg(void *) msgp;
322 		syscallarg(size_t) msgsz;
323 		syscallarg(long) msgtyp;
324 		syscallarg(int) msgflg;
325 	} */ *uap = v;
326 	struct ucred *cred = p->p_ucred;
327 	char *msgp = SCARG(uap, msgp);
328 	struct que *que;
329 	struct msg *msg;
330 	size_t msgsz = SCARG(uap, msgsz);
331 	long msgtyp = SCARG(uap, msgtyp);
332 	int error;
333 
334 	if ((que = que_lookup(SCARG(uap, msqid))) == NULL)
335 		return (EINVAL);
336 
337 	if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R)))
338 		return (error);
339 
340 	QREF(que);
341 
342 	/* msg_lookup handles matching; sleeping gets handled here */
343 	while ((msg = msg_lookup(que, msgtyp)) == NULL) {
344 
345 		if (SCARG(uap, msgflg) & IPC_NOWAIT) {
346 			error = ENOMSG;
347 			goto out;
348 		}
349 
350 		que->que_flags |= MSGQ_READERS;
351 		if ((error = tsleep(que, PZERO|PCATCH, "msgwait", 0)))
352 			goto out;
353 
354 		/* make sure the queue still alive */
355 		if (que->que_flags & MSGQ_DYING) {
356 			error = EIDRM;
357 			goto out;
358 		}
359 	}
360 
361 	/* if msg_copyout fails, keep the message around so it isn't lost */
362 	if ((error = msg_copyout(msg, msgp, &msgsz, p)))
363 		goto out;
364 
365 	msg_dequeue(que, msg, p);
366 	msg_free(msg);
367 
368 	if (que->que_flags & MSGQ_WRITERS) {
369 		que->que_flags &= ~MSGQ_WRITERS;
370 		wakeup(que);
371 	}
372 
373 	/* ensure processes waiting on the global limit don't wedge */
374 	if (maxmsgs) {
375 		maxmsgs = 0;
376 		que_wakewriters();
377 	}
378 
379 	*retval = msgsz;
380 out:
381 	QRELE(que);
382 
383 	return (error);
384 }
385 
386 /*
387  * que management functions
388  */
389 
390 struct que *
391 que_create(key_t key, struct ucred *cred, int mode)
392 {
393 	struct que *que;
394 
395 	que = malloc(sizeof(*que), M_TEMP, M_WAIT|M_ZERO);
396 
397 	/* if malloc slept, a queue with the same key may have been created */
398 	if (que_key_lookup(key)) {
399 		free(que, M_TEMP);
400 		return (NULL);
401 	}
402 
403 	que->msqid_ds.msg_perm.key = key;
404 	que->msqid_ds.msg_perm.cuid = cred->cr_uid;
405 	que->msqid_ds.msg_perm.uid = cred->cr_uid;
406 	que->msqid_ds.msg_perm.cgid = cred->cr_gid;
407 	que->msqid_ds.msg_perm.gid = cred->cr_gid;
408 	que->msqid_ds.msg_perm.mode = mode & 0777;
409 	que->msqid_ds.msg_perm.seq = ++sequence & 0x7fff;
410 	que->msqid_ds.msg_qbytes = msginfo.msgmnb;
411 	que->msqid_ds.msg_ctime = time_second;
412 
413 	TAILQ_INIT(&que->que_msgs);
414 
415 	TAILQ_INSERT_TAIL(&msg_queues, que, que_next);
416 	num_ques++;
417 
418 	return (que);
419 }
420 
421 struct que *
422 que_lookup(int id)
423 {
424 	struct que *que;
425 
426 	TAILQ_FOREACH(que, &msg_queues, que_next)
427 		if (que->que_id == id)
428 			break;
429 
430 	/* don't return queues marked for removal */
431 	if (que && que->que_flags & MSGQ_DYING)
432 		return (NULL);
433 
434 	return (que);
435 }
436 
437 struct que *
438 que_key_lookup(key_t key)
439 {
440 	struct que *que;
441 
442 	if (key == IPC_PRIVATE)
443 		return (NULL);
444 
445 	TAILQ_FOREACH(que, &msg_queues, que_next)
446 		if (que->msqid_ds.msg_perm.key == key)
447 			break;
448 
449 	/* don't return queues marked for removal */
450 	if (que && que->que_flags & MSGQ_DYING)
451 		return (NULL);
452 
453 	return (que);
454 }
455 
456 void
457 que_wakewriters(void)
458 {
459 	struct que *que;
460 
461 	TAILQ_FOREACH(que, &msg_queues, que_next) {
462 		if (que->que_flags & MSGQ_WRITERS) {
463 			que->que_flags &= ~MSGQ_WRITERS;
464 			wakeup(que);
465 		}
466 	}
467 }
468 
469 void
470 que_free(struct que *que)
471 {
472 	struct msg *msg;
473 #ifdef DIAGNOSTIC
474 	if (que->que_references > 0)
475 		panic("freeing message queue with active references");
476 #endif
477 
478 	while ((msg = TAILQ_FIRST(&que->que_msgs))) {
479 		TAILQ_REMOVE(&que->que_msgs, msg, msg_next);
480 		msg_free(msg);
481 	}
482 	free(que, M_TEMP);
483 	num_ques--;
484 }
485 
486 /*
487  * msg management functions
488  */
489 
490 struct msg *
491 msg_create(struct que *que)
492 {
493 	struct msg *msg;
494 
495 	msg = pool_get(&sysvmsgpl, PR_WAITOK|PR_ZERO);
496 
497 	/* if the queue has died during allocation, return NULL */
498 	if (que->que_flags & MSGQ_DYING) {
499 		pool_put(&sysvmsgpl, msg);
500 		wakeup(que);
501 		return(NULL);
502 	}
503 
504 	num_msgs++;
505 
506 	return (msg);
507 }
508 
509 struct msg *
510 msg_lookup(struct que *que, int msgtyp)
511 {
512 	struct msg *msg;
513 
514 	/*
515 	 * Three different matches are performed based on the value of msgtyp:
516 	 * 1) msgtyp > 0 => match exactly
517 	 * 2> msgtyp = 0 => match any
518 	 * 3) msgtyp < 0 => match any up to absolute value of msgtyp
519 	 */
520 	TAILQ_FOREACH(msg, &que->que_msgs, msg_next)
521 		if (msgtyp == 0 || msgtyp == msg->msg_type ||
522 		    (msgtyp < 0 && -msgtyp <= msg->msg_type))
523 			break;
524 
525 	return (msg);
526 }
527 
528 void
529 msg_free(struct msg *msg)
530 {
531 	m_freem(msg->msg_data);
532 	pool_put(&sysvmsgpl, msg);
533 	num_msgs--;
534 }
535 
536 void
537 msg_enqueue(struct que *que, struct msg *msg, struct proc *p)
538 {
539 	que->msqid_ds.msg_cbytes += msg->msg_len;
540 	que->msqid_ds.msg_qnum++;
541 	que->msqid_ds.msg_lspid = p->p_p->ps_mainproc->p_pid;
542 	que->msqid_ds.msg_stime = time_second;
543 
544 	TAILQ_INSERT_TAIL(&que->que_msgs, msg, msg_next);
545 }
546 
547 void
548 msg_dequeue(struct que *que, struct msg *msg, struct proc *p)
549 {
550 	que->msqid_ds.msg_cbytes -= msg->msg_len;
551 	que->msqid_ds.msg_qnum--;
552 	que->msqid_ds.msg_lrpid = p->p_p->ps_mainproc->p_pid;
553 	que->msqid_ds.msg_rtime = time_second;
554 
555 	TAILQ_REMOVE(&que->que_msgs, msg, msg_next);
556 }
557 
558 /*
559  * The actual I/O routines. A note concerning the layout of SysV msg buffers:
560  *
561  * The data to be copied is laid out as a single userspace buffer, with a
562  * long preceding an opaque buffer of len bytes. The long value ends
563  * up being the message type, which needs to be copied seperately from
564  * the buffer data, which is stored in in mbufs.
565  */
566 
567 int
568 msg_copyin(struct msg *msg, const char *ubuf, size_t len, struct proc *p)
569 {
570 	struct mbuf **mm, *m;
571 	size_t xfer;
572 	int error;
573 
574 	if (msg == NULL)
575 		panic ("msg NULL");
576 
577 	if ((error = copyin(ubuf, &msg->msg_type, sizeof(long)))) {
578 		msg_free(msg);
579 		return (error);
580 	}
581 
582 	if (msg->msg_type < 0) {
583 		msg_free(msg);
584 		return (EINVAL);
585 	}
586 
587 	ubuf += sizeof(long);
588 
589 	msg->msg_len = 0;
590 	mm = &msg->msg_data;
591 
592 	while (msg->msg_len < len) {
593 		m = m_get(M_WAIT, MT_DATA);
594 		if (len >= MINCLSIZE) {
595 			MCLGET(m, M_WAIT);
596 			xfer = min(len, MCLBYTES);
597 		} else {
598 			xfer = min(len, MLEN);
599 		}
600 		m->m_len = xfer;
601 		msg->msg_len += xfer;
602 		*mm = m;
603 		mm = &m->m_next;
604 	}
605 
606 	for (m = msg->msg_data; m; m = m->m_next) {
607 		if ((error = copyin(ubuf, mtod(m, void *), m->m_len))) {
608 			msg_free(msg);
609 			return (error);
610 		}
611 		ubuf += m->m_len;
612 	}
613 
614 	return (0);
615 }
616 
617 int
618 msg_copyout(struct msg *msg, char *ubuf, size_t *len, struct proc *p)
619 {
620 	struct mbuf *m;
621 	size_t xfer;
622 	int error;
623 
624 #ifdef DIAGNOSTIC
625 	if (msg->msg_len > MSGMAX)
626 		panic("SysV message longer than MSGMAX");
627 #endif
628 
629 	/* silently truncate messages too large for user buffer */
630 	xfer = min(*len, msg->msg_len);
631 
632 	if ((error = copyout(&msg->msg_type, ubuf, sizeof(long))))
633 		return (error);
634 
635 	ubuf += sizeof(long);
636 	*len = xfer;
637 
638 	for (m = msg->msg_data; m; m = m->m_next) {
639 		if ((error = copyout(mtod(m, void *), ubuf, m->m_len)))
640 			return (error);
641 		ubuf += m->m_len;
642 	}
643 
644 	return (0);
645 }
646 
647 int
648 sysctl_sysvmsg(int *name, u_int namelen, void *where, size_t *sizep)
649 {
650 	struct msg_sysctl_info *info;
651 	struct que *que;
652 	size_t infolen;
653 	int error, i = 0;
654 
655 	switch (*name) {
656 	case KERN_SYSVIPC_MSG_INFO:
657 
658 		if (namelen != 1)
659 			return (ENOTDIR);
660 
661 		/*
662 		 * The userland ipcs(1) utility expects to be able
663 		 * to iterate over at least msginfo.msgmni queues,
664 		 * even if those queues don't exist. This is an
665 		 * artifact of the previous implementation of
666 		 * message queues; for now, emulate this behavior
667 		 * until a more thorough fix can be made.
668 		 */
669 		infolen = sizeof(msginfo) +
670 		    msginfo.msgmni * sizeof(struct msqid_ds);
671 		if (where == NULL) {
672 			*sizep = infolen;
673 			return (0);
674 		}
675 
676 		/*
677 		 * More special-casing due to previous implementation:
678 		 * if the caller just wants the msginfo struct, then
679 		 * sizep will point to the value sizeof(struct msginfo).
680 		 * In that case, only copy out the msginfo struct to
681 		 * the caller.
682 		 */
683 		if (*sizep == sizeof(struct msginfo))
684 			return (copyout(&msginfo, where, sizeof(msginfo)));
685 
686 		info = malloc(infolen, M_TEMP, M_WAIT|M_ZERO);
687 
688 		/* if the malloc slept, this may have changed */
689 		infolen = sizeof(msginfo) +
690 		    msginfo.msgmni * sizeof(struct msqid_ds);
691 
692 		if (*sizep < infolen) {
693 			free(info, M_TEMP);
694 			return (ENOMEM);
695 		}
696 
697 		bcopy(&msginfo, &info->msginfo, sizeof(struct msginfo));
698 
699 		TAILQ_FOREACH(que, &msg_queues, que_next)
700 			bcopy(&que->msqid_ds, &info->msgids[i++],
701 			    sizeof(struct msqid_ds));
702 
703 		error = copyout(info, where, infolen);
704 
705 		free(info, M_TEMP);
706 
707 		return (error);
708 
709 	default:
710 		return (EINVAL);
711 	}
712 }
713