xref: /freebsd/sys/kern/sysv_sem.c (revision 830940567b49bb0c08dfaed40418999e76616909)
1 /*-
2  * Implementation of SVID semaphores
3  *
4  * Author:  Daniel Boulet
5  *
6  * This software is provided ``AS IS'' without any warranties of any kind.
7  */
8 /*-
9  * Copyright (c) 2003-2005 McAfee, Inc.
10  * All rights reserved.
11  *
12  * This software was developed for the FreeBSD Project in part by McAfee
13  * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
14  * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
15  * program.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 #include "opt_compat.h"
43 #include "opt_sysvipc.h"
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/sysproto.h>
48 #include <sys/eventhandler.h>
49 #include <sys/kernel.h>
50 #include <sys/proc.h>
51 #include <sys/lock.h>
52 #include <sys/module.h>
53 #include <sys/mutex.h>
54 #include <sys/sem.h>
55 #include <sys/syscall.h>
56 #include <sys/syscallsubr.h>
57 #include <sys/sysent.h>
58 #include <sys/sysctl.h>
59 #include <sys/uio.h>
60 #include <sys/malloc.h>
61 #include <sys/jail.h>
62 
63 #include <security/mac/mac_framework.h>
64 
65 static MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores");
66 
67 #ifdef SEM_DEBUG
68 #define DPRINTF(a)	printf a
69 #else
70 #define DPRINTF(a)
71 #endif
72 
73 static void seminit(void);
74 static int sysvsem_modload(struct module *, int, void *);
75 static int semunload(void);
76 static void semexit_myhook(void *arg, struct proc *p);
77 static int sysctl_sema(SYSCTL_HANDLER_ARGS);
78 static int semvalid(int semid, struct semid_kernel *semakptr);
79 
80 #ifndef _SYS_SYSPROTO_H_
81 struct __semctl_args;
82 int __semctl(struct thread *td, struct __semctl_args *uap);
83 struct semget_args;
84 int semget(struct thread *td, struct semget_args *uap);
85 struct semop_args;
86 int semop(struct thread *td, struct semop_args *uap);
87 #endif
88 
89 static struct sem_undo *semu_alloc(struct thread *td);
90 static int semundo_adjust(struct thread *td, struct sem_undo **supptr,
91     int semid, int semseq, int semnum, int adjval);
92 static void semundo_clear(int semid, int semnum);
93 
94 static struct mtx	sem_mtx;	/* semaphore global lock */
95 static struct mtx sem_undo_mtx;
96 static int	semtot = 0;
97 static struct semid_kernel *sema;	/* semaphore id pool */
98 static struct mtx *sema_mtx;	/* semaphore id pool mutexes*/
99 static struct sem *sem;		/* semaphore pool */
100 LIST_HEAD(, sem_undo) semu_list;	/* list of active undo structures */
101 LIST_HEAD(, sem_undo) semu_free_list;	/* list of free undo structures */
102 static int	*semu;		/* undo structure pool */
103 static eventhandler_tag semexit_tag;
104 
105 #define SEMUNDO_MTX		sem_undo_mtx
106 #define SEMUNDO_LOCK()		mtx_lock(&SEMUNDO_MTX);
107 #define SEMUNDO_UNLOCK()	mtx_unlock(&SEMUNDO_MTX);
108 #define SEMUNDO_LOCKASSERT(how)	mtx_assert(&SEMUNDO_MTX, (how));
109 
110 struct sem {
111 	u_short	semval;		/* semaphore value */
112 	pid_t	sempid;		/* pid of last operation */
113 	u_short	semncnt;	/* # awaiting semval > cval */
114 	u_short	semzcnt;	/* # awaiting semval = 0 */
115 };
116 
117 /*
118  * Undo structure (one per process)
119  */
120 struct sem_undo {
121 	LIST_ENTRY(sem_undo) un_next;	/* ptr to next active undo structure */
122 	struct	proc *un_proc;		/* owner of this structure */
123 	short	un_cnt;			/* # of active entries */
124 	struct undo {
125 		short	un_adjval;	/* adjust on exit values */
126 		short	un_num;		/* semaphore # */
127 		int	un_id;		/* semid */
128 		unsigned short un_seq;
129 	} un_ent[1];			/* undo entries */
130 };
131 
132 /*
133  * Configuration parameters
134  */
135 #ifndef SEMMNI
136 #define SEMMNI	10		/* # of semaphore identifiers */
137 #endif
138 #ifndef SEMMNS
139 #define SEMMNS	60		/* # of semaphores in system */
140 #endif
141 #ifndef SEMUME
142 #define SEMUME	10		/* max # of undo entries per process */
143 #endif
144 #ifndef SEMMNU
145 #define SEMMNU	30		/* # of undo structures in system */
146 #endif
147 
148 /* shouldn't need tuning */
149 #ifndef SEMMAP
150 #define SEMMAP	30		/* # of entries in semaphore map */
151 #endif
152 #ifndef SEMMSL
153 #define SEMMSL	SEMMNS		/* max # of semaphores per id */
154 #endif
155 #ifndef SEMOPM
156 #define SEMOPM	100		/* max # of operations per semop call */
157 #endif
158 
159 #define SEMVMX	32767		/* semaphore maximum value */
160 #define SEMAEM	16384		/* adjust on exit max value */
161 
162 /*
163  * Due to the way semaphore memory is allocated, we have to ensure that
164  * SEMUSZ is properly aligned.
165  */
166 
167 #define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1))
168 
169 /* actual size of an undo structure */
170 #define SEMUSZ	SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME]))
171 
172 /*
173  * Macro to find a particular sem_undo vector
174  */
175 #define SEMU(ix) \
176 	((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz))
177 
178 /*
179  * semaphore info struct
180  */
181 struct seminfo seminfo = {
182                 SEMMAP,         /* # of entries in semaphore map */
183                 SEMMNI,         /* # of semaphore identifiers */
184                 SEMMNS,         /* # of semaphores in system */
185                 SEMMNU,         /* # of undo structures in system */
186                 SEMMSL,         /* max # of semaphores per id */
187                 SEMOPM,         /* max # of operations per semop call */
188                 SEMUME,         /* max # of undo entries per process */
189                 SEMUSZ,         /* size in bytes of undo structure */
190                 SEMVMX,         /* semaphore maximum value */
191                 SEMAEM          /* adjust on exit max value */
192 };
193 
194 SYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0,
195     "Number of entries in the semaphore map");
196 SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RDTUN, &seminfo.semmni, 0,
197     "Number of semaphore identifiers");
198 SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RDTUN, &seminfo.semmns, 0,
199     "Maximum number of semaphores in the system");
200 SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RDTUN, &seminfo.semmnu, 0,
201     "Maximum number of undo structures in the system");
202 SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0,
203     "Max semaphores per id");
204 SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RDTUN, &seminfo.semopm, 0,
205     "Max operations per semop call");
206 SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RDTUN, &seminfo.semume, 0,
207     "Max undo entries per process");
208 SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RDTUN, &seminfo.semusz, 0,
209     "Size in bytes of undo structure");
210 SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0,
211     "Semaphore maximum value");
212 SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0,
213     "Adjust on exit max value");
214 SYSCTL_PROC(_kern_ipc, OID_AUTO, sema, CTLFLAG_RD,
215     NULL, 0, sysctl_sema, "", "");
216 
217 static void
218 seminit(void)
219 {
220 	int i;
221 
222 	TUNABLE_INT_FETCH("kern.ipc.semmap", &seminfo.semmap);
223 	TUNABLE_INT_FETCH("kern.ipc.semmni", &seminfo.semmni);
224 	TUNABLE_INT_FETCH("kern.ipc.semmns", &seminfo.semmns);
225 	TUNABLE_INT_FETCH("kern.ipc.semmnu", &seminfo.semmnu);
226 	TUNABLE_INT_FETCH("kern.ipc.semmsl", &seminfo.semmsl);
227 	TUNABLE_INT_FETCH("kern.ipc.semopm", &seminfo.semopm);
228 	TUNABLE_INT_FETCH("kern.ipc.semume", &seminfo.semume);
229 	TUNABLE_INT_FETCH("kern.ipc.semusz", &seminfo.semusz);
230 	TUNABLE_INT_FETCH("kern.ipc.semvmx", &seminfo.semvmx);
231 	TUNABLE_INT_FETCH("kern.ipc.semaem", &seminfo.semaem);
232 
233 	sem = malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK);
234 	sema = malloc(sizeof(struct semid_kernel) * seminfo.semmni, M_SEM,
235 	    M_WAITOK);
236 	sema_mtx = malloc(sizeof(struct mtx) * seminfo.semmni, M_SEM,
237 	    M_WAITOK | M_ZERO);
238 	semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK);
239 
240 	for (i = 0; i < seminfo.semmni; i++) {
241 		sema[i].u.sem_base = 0;
242 		sema[i].u.sem_perm.mode = 0;
243 		sema[i].u.sem_perm.seq = 0;
244 #ifdef MAC
245 		mac_sysvsem_init(&sema[i]);
246 #endif
247 	}
248 	for (i = 0; i < seminfo.semmni; i++)
249 		mtx_init(&sema_mtx[i], "semid", NULL, MTX_DEF);
250 	LIST_INIT(&semu_free_list);
251 	for (i = 0; i < seminfo.semmnu; i++) {
252 		struct sem_undo *suptr = SEMU(i);
253 		suptr->un_proc = NULL;
254 		LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
255 	}
256 	LIST_INIT(&semu_list);
257 	mtx_init(&sem_mtx, "sem", NULL, MTX_DEF);
258 	mtx_init(&sem_undo_mtx, "semu", NULL, MTX_DEF);
259 	semexit_tag = EVENTHANDLER_REGISTER(process_exit, semexit_myhook, NULL,
260 	    EVENTHANDLER_PRI_ANY);
261 }
262 
263 static int
264 semunload(void)
265 {
266 	int i;
267 
268 	/* XXXKIB */
269 	if (semtot != 0)
270 		return (EBUSY);
271 
272 	EVENTHANDLER_DEREGISTER(process_exit, semexit_tag);
273 #ifdef MAC
274 	for (i = 0; i < seminfo.semmni; i++)
275 		mac_sysvsem_destroy(&sema[i]);
276 #endif
277 	free(sem, M_SEM);
278 	free(sema, M_SEM);
279 	free(semu, M_SEM);
280 	for (i = 0; i < seminfo.semmni; i++)
281 		mtx_destroy(&sema_mtx[i]);
282 	free(sema_mtx, M_SEM);
283 	mtx_destroy(&sem_mtx);
284 	mtx_destroy(&sem_undo_mtx);
285 	return (0);
286 }
287 
288 static int
289 sysvsem_modload(struct module *module, int cmd, void *arg)
290 {
291 	int error = 0;
292 
293 	switch (cmd) {
294 	case MOD_LOAD:
295 		seminit();
296 		break;
297 	case MOD_UNLOAD:
298 		error = semunload();
299 		break;
300 	case MOD_SHUTDOWN:
301 		break;
302 	default:
303 		error = EINVAL;
304 		break;
305 	}
306 	return (error);
307 }
308 
309 static moduledata_t sysvsem_mod = {
310 	"sysvsem",
311 	&sysvsem_modload,
312 	NULL
313 };
314 
315 SYSCALL_MODULE_HELPER(__semctl);
316 SYSCALL_MODULE_HELPER(semget);
317 SYSCALL_MODULE_HELPER(semop);
318 
319 DECLARE_MODULE(sysvsem, sysvsem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
320 MODULE_VERSION(sysvsem, 1);
321 
322 /*
323  * Allocate a new sem_undo structure for a process
324  * (returns ptr to structure or NULL if no more room)
325  */
326 
327 static struct sem_undo *
328 semu_alloc(struct thread *td)
329 {
330 	struct sem_undo *suptr;
331 
332 	SEMUNDO_LOCKASSERT(MA_OWNED);
333 	if ((suptr = LIST_FIRST(&semu_free_list)) == NULL)
334 		return (NULL);
335 	LIST_REMOVE(suptr, un_next);
336 	LIST_INSERT_HEAD(&semu_list, suptr, un_next);
337 	suptr->un_cnt = 0;
338 	suptr->un_proc = td->td_proc;
339 	return (suptr);
340 }
341 
342 static int
343 semu_try_free(struct sem_undo *suptr)
344 {
345 
346 	SEMUNDO_LOCKASSERT(MA_OWNED);
347 
348 	if (suptr->un_cnt != 0)
349 		return (0);
350 	LIST_REMOVE(suptr, un_next);
351 	LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
352 	return (1);
353 }
354 
355 /*
356  * Adjust a particular entry for a particular proc
357  */
358 
359 static int
360 semundo_adjust(struct thread *td, struct sem_undo **supptr, int semid,
361     int semseq, int semnum, int adjval)
362 {
363 	struct proc *p = td->td_proc;
364 	struct sem_undo *suptr;
365 	struct undo *sunptr;
366 	int i;
367 
368 	SEMUNDO_LOCKASSERT(MA_OWNED);
369 	/* Look for and remember the sem_undo if the caller doesn't provide
370 	   it */
371 
372 	suptr = *supptr;
373 	if (suptr == NULL) {
374 		LIST_FOREACH(suptr, &semu_list, un_next) {
375 			if (suptr->un_proc == p) {
376 				*supptr = suptr;
377 				break;
378 			}
379 		}
380 		if (suptr == NULL) {
381 			if (adjval == 0)
382 				return(0);
383 			suptr = semu_alloc(td);
384 			if (suptr == NULL)
385 				return (ENOSPC);
386 			*supptr = suptr;
387 		}
388 	}
389 
390 	/*
391 	 * Look for the requested entry and adjust it (delete if adjval becomes
392 	 * 0).
393 	 */
394 	sunptr = &suptr->un_ent[0];
395 	for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
396 		if (sunptr->un_id != semid || sunptr->un_num != semnum)
397 			continue;
398 		if (adjval != 0) {
399 			adjval += sunptr->un_adjval;
400 			if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
401 				return (ERANGE);
402 		}
403 		sunptr->un_adjval = adjval;
404 		if (sunptr->un_adjval == 0) {
405 			suptr->un_cnt--;
406 			if (i < suptr->un_cnt)
407 				suptr->un_ent[i] =
408 				    suptr->un_ent[suptr->un_cnt];
409 			if (suptr->un_cnt == 0)
410 				semu_try_free(suptr);
411 		}
412 		return (0);
413 	}
414 
415 	/* Didn't find the right entry - create it */
416 	if (adjval == 0)
417 		return (0);
418 	if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
419 		return (ERANGE);
420 	if (suptr->un_cnt != seminfo.semume) {
421 		sunptr = &suptr->un_ent[suptr->un_cnt];
422 		suptr->un_cnt++;
423 		sunptr->un_adjval = adjval;
424 		sunptr->un_id = semid;
425 		sunptr->un_num = semnum;
426 		sunptr->un_seq = semseq;
427 	} else
428 		return (EINVAL);
429 	return (0);
430 }
431 
432 static void
433 semundo_clear(int semid, int semnum)
434 {
435 	struct sem_undo *suptr, *suptr1;
436 	struct undo *sunptr;
437 	int i;
438 
439 	SEMUNDO_LOCKASSERT(MA_OWNED);
440 	LIST_FOREACH_SAFE(suptr, &semu_list, un_next, suptr1) {
441 		sunptr = &suptr->un_ent[0];
442 		for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
443 			if (sunptr->un_id != semid)
444 				continue;
445 			if (semnum == -1 || sunptr->un_num == semnum) {
446 				suptr->un_cnt--;
447 				if (i < suptr->un_cnt) {
448 					suptr->un_ent[i] =
449 					    suptr->un_ent[suptr->un_cnt];
450 					continue;
451 				}
452 				semu_try_free(suptr);
453 			}
454 			if (semnum != -1)
455 				break;
456 		}
457 	}
458 }
459 
460 static int
461 semvalid(int semid, struct semid_kernel *semakptr)
462 {
463 
464 	return ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
465 	    semakptr->u.sem_perm.seq != IPCID_TO_SEQ(semid) ? EINVAL : 0);
466 }
467 
468 /*
469  * Note that the user-mode half of this passes a union, not a pointer.
470  */
471 #ifndef _SYS_SYSPROTO_H_
472 struct __semctl_args {
473 	int	semid;
474 	int	semnum;
475 	int	cmd;
476 	union	semun *arg;
477 };
478 #endif
479 int
480 __semctl(struct thread *td, struct __semctl_args *uap)
481 {
482 	struct semid_ds dsbuf;
483 	union semun arg, semun;
484 	register_t rval;
485 	int error;
486 
487 	switch (uap->cmd) {
488 	case SEM_STAT:
489 	case IPC_SET:
490 	case IPC_STAT:
491 	case GETALL:
492 	case SETVAL:
493 	case SETALL:
494 		error = copyin(uap->arg, &arg, sizeof(arg));
495 		if (error)
496 			return (error);
497 		break;
498 	}
499 
500 	switch (uap->cmd) {
501 	case SEM_STAT:
502 	case IPC_STAT:
503 		semun.buf = &dsbuf;
504 		break;
505 	case IPC_SET:
506 		error = copyin(arg.buf, &dsbuf, sizeof(dsbuf));
507 		if (error)
508 			return (error);
509 		semun.buf = &dsbuf;
510 		break;
511 	case GETALL:
512 	case SETALL:
513 		semun.array = arg.array;
514 		break;
515 	case SETVAL:
516 		semun.val = arg.val;
517 		break;
518 	}
519 
520 	error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun,
521 	    &rval);
522 	if (error)
523 		return (error);
524 
525 	switch (uap->cmd) {
526 	case SEM_STAT:
527 	case IPC_STAT:
528 		error = copyout(&dsbuf, arg.buf, sizeof(dsbuf));
529 		break;
530 	}
531 
532 	if (error == 0)
533 		td->td_retval[0] = rval;
534 	return (error);
535 }
536 
537 int
538 kern_semctl(struct thread *td, int semid, int semnum, int cmd,
539     union semun *arg, register_t *rval)
540 {
541 	u_short *array;
542 	struct ucred *cred = td->td_ucred;
543 	int i, error;
544 	struct semid_ds *sbuf;
545 	struct semid_kernel *semakptr;
546 	struct mtx *sema_mtxp;
547 	u_short usval, count;
548 	int semidx;
549 
550 	DPRINTF(("call to semctl(%d, %d, %d, 0x%p)\n",
551 	    semid, semnum, cmd, arg));
552 	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
553 		return (ENOSYS);
554 
555 	array = NULL;
556 
557 	switch(cmd) {
558 	case SEM_STAT:
559 		/*
560 		 * For this command we assume semid is an array index
561 		 * rather than an IPC id.
562 		 */
563 		if (semid < 0 || semid >= seminfo.semmni)
564 			return (EINVAL);
565 		semakptr = &sema[semid];
566 		sema_mtxp = &sema_mtx[semid];
567 		mtx_lock(sema_mtxp);
568 		if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) {
569 			error = EINVAL;
570 			goto done2;
571 		}
572 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
573 			goto done2;
574 #ifdef MAC
575 		error = mac_sysvsem_check_semctl(cred, semakptr, cmd);
576 		if (error != 0)
577 			goto done2;
578 #endif
579 		bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds));
580 		*rval = IXSEQ_TO_IPCID(semid, semakptr->u.sem_perm);
581 		mtx_unlock(sema_mtxp);
582 		return (0);
583 	}
584 
585 	semidx = IPCID_TO_IX(semid);
586 	if (semidx < 0 || semidx >= seminfo.semmni)
587 		return (EINVAL);
588 
589 	semakptr = &sema[semidx];
590 	sema_mtxp = &sema_mtx[semidx];
591 	if (cmd == IPC_RMID)
592 		mtx_lock(&sem_mtx);
593 	mtx_lock(sema_mtxp);
594 #ifdef MAC
595 	error = mac_sysvsem_check_semctl(cred, semakptr, cmd);
596 	if (error != 0)
597 		goto done2;
598 #endif
599 
600 	error = 0;
601 	*rval = 0;
602 
603 	switch (cmd) {
604 	case IPC_RMID:
605 		if ((error = semvalid(semid, semakptr)) != 0)
606 			goto done2;
607 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M)))
608 			goto done2;
609 		semakptr->u.sem_perm.cuid = cred->cr_uid;
610 		semakptr->u.sem_perm.uid = cred->cr_uid;
611 		semakptr->u.sem_perm.mode = 0;
612 		SEMUNDO_LOCK();
613 		semundo_clear(semidx, -1);
614 		SEMUNDO_UNLOCK();
615 #ifdef MAC
616 		mac_sysvsem_cleanup(semakptr);
617 #endif
618 		wakeup(semakptr);
619 		for (i = 0; i < seminfo.semmni; i++) {
620 			if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
621 			    sema[i].u.sem_base > semakptr->u.sem_base)
622 				mtx_lock_flags(&sema_mtx[i], LOP_DUPOK);
623 		}
624 		for (i = semakptr->u.sem_base - sem; i < semtot; i++)
625 			sem[i] = sem[i + semakptr->u.sem_nsems];
626 		for (i = 0; i < seminfo.semmni; i++) {
627 			if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
628 			    sema[i].u.sem_base > semakptr->u.sem_base) {
629 				sema[i].u.sem_base -= semakptr->u.sem_nsems;
630 				mtx_unlock(&sema_mtx[i]);
631 			}
632 		}
633 		semtot -= semakptr->u.sem_nsems;
634 		break;
635 
636 	case IPC_SET:
637 		if ((error = semvalid(semid, semakptr)) != 0)
638 			goto done2;
639 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M)))
640 			goto done2;
641 		sbuf = arg->buf;
642 		semakptr->u.sem_perm.uid = sbuf->sem_perm.uid;
643 		semakptr->u.sem_perm.gid = sbuf->sem_perm.gid;
644 		semakptr->u.sem_perm.mode = (semakptr->u.sem_perm.mode &
645 		    ~0777) | (sbuf->sem_perm.mode & 0777);
646 		semakptr->u.sem_ctime = time_second;
647 		break;
648 
649 	case IPC_STAT:
650 		if ((error = semvalid(semid, semakptr)) != 0)
651 			goto done2;
652 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
653 			goto done2;
654 		bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds));
655 		break;
656 
657 	case GETNCNT:
658 		if ((error = semvalid(semid, semakptr)) != 0)
659 			goto done2;
660 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
661 			goto done2;
662 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
663 			error = EINVAL;
664 			goto done2;
665 		}
666 		*rval = semakptr->u.sem_base[semnum].semncnt;
667 		break;
668 
669 	case GETPID:
670 		if ((error = semvalid(semid, semakptr)) != 0)
671 			goto done2;
672 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
673 			goto done2;
674 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
675 			error = EINVAL;
676 			goto done2;
677 		}
678 		*rval = semakptr->u.sem_base[semnum].sempid;
679 		break;
680 
681 	case GETVAL:
682 		if ((error = semvalid(semid, semakptr)) != 0)
683 			goto done2;
684 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
685 			goto done2;
686 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
687 			error = EINVAL;
688 			goto done2;
689 		}
690 		*rval = semakptr->u.sem_base[semnum].semval;
691 		break;
692 
693 	case GETALL:
694 		/*
695 		 * Unfortunately, callers of this function don't know
696 		 * in advance how many semaphores are in this set.
697 		 * While we could just allocate the maximum size array
698 		 * and pass the actual size back to the caller, that
699 		 * won't work for SETALL since we can't copyin() more
700 		 * data than the user specified as we may return a
701 		 * spurious EFAULT.
702 		 *
703 		 * Note that the number of semaphores in a set is
704 		 * fixed for the life of that set.  The only way that
705 		 * the 'count' could change while are blocked in
706 		 * malloc() is if this semaphore set were destroyed
707 		 * and a new one created with the same index.
708 		 * However, semvalid() will catch that due to the
709 		 * sequence number unless exactly 0x8000 (or a
710 		 * multiple thereof) semaphore sets for the same index
711 		 * are created and destroyed while we are in malloc!
712 		 *
713 		 */
714 		count = semakptr->u.sem_nsems;
715 		mtx_unlock(sema_mtxp);
716 		array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK);
717 		mtx_lock(sema_mtxp);
718 		if ((error = semvalid(semid, semakptr)) != 0)
719 			goto done2;
720 		KASSERT(count == semakptr->u.sem_nsems, ("nsems changed"));
721 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
722 			goto done2;
723 		for (i = 0; i < semakptr->u.sem_nsems; i++)
724 			array[i] = semakptr->u.sem_base[i].semval;
725 		mtx_unlock(sema_mtxp);
726 		error = copyout(array, arg->array, count * sizeof(*array));
727 		mtx_lock(sema_mtxp);
728 		break;
729 
730 	case GETZCNT:
731 		if ((error = semvalid(semid, semakptr)) != 0)
732 			goto done2;
733 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
734 			goto done2;
735 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
736 			error = EINVAL;
737 			goto done2;
738 		}
739 		*rval = semakptr->u.sem_base[semnum].semzcnt;
740 		break;
741 
742 	case SETVAL:
743 		if ((error = semvalid(semid, semakptr)) != 0)
744 			goto done2;
745 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W)))
746 			goto done2;
747 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
748 			error = EINVAL;
749 			goto done2;
750 		}
751 		if (arg->val < 0 || arg->val > seminfo.semvmx) {
752 			error = ERANGE;
753 			goto done2;
754 		}
755 		semakptr->u.sem_base[semnum].semval = arg->val;
756 		SEMUNDO_LOCK();
757 		semundo_clear(semidx, semnum);
758 		SEMUNDO_UNLOCK();
759 		wakeup(semakptr);
760 		break;
761 
762 	case SETALL:
763 		/*
764 		 * See comment on GETALL for why 'count' shouldn't change
765 		 * and why we require a userland buffer.
766 		 */
767 		count = semakptr->u.sem_nsems;
768 		mtx_unlock(sema_mtxp);
769 		array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK);
770 		error = copyin(arg->array, array, count * sizeof(*array));
771 		mtx_lock(sema_mtxp);
772 		if (error)
773 			break;
774 		if ((error = semvalid(semid, semakptr)) != 0)
775 			goto done2;
776 		KASSERT(count == semakptr->u.sem_nsems, ("nsems changed"));
777 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W)))
778 			goto done2;
779 		for (i = 0; i < semakptr->u.sem_nsems; i++) {
780 			usval = array[i];
781 			if (usval > seminfo.semvmx) {
782 				error = ERANGE;
783 				break;
784 			}
785 			semakptr->u.sem_base[i].semval = usval;
786 		}
787 		SEMUNDO_LOCK();
788 		semundo_clear(semidx, -1);
789 		SEMUNDO_UNLOCK();
790 		wakeup(semakptr);
791 		break;
792 
793 	default:
794 		error = EINVAL;
795 		break;
796 	}
797 
798 done2:
799 	mtx_unlock(sema_mtxp);
800 	if (cmd == IPC_RMID)
801 		mtx_unlock(&sem_mtx);
802 	if (array != NULL)
803 		free(array, M_TEMP);
804 	return(error);
805 }
806 
807 #ifndef _SYS_SYSPROTO_H_
808 struct semget_args {
809 	key_t	key;
810 	int	nsems;
811 	int	semflg;
812 };
813 #endif
814 int
815 semget(struct thread *td, struct semget_args *uap)
816 {
817 	int semid, error = 0;
818 	int key = uap->key;
819 	int nsems = uap->nsems;
820 	int semflg = uap->semflg;
821 	struct ucred *cred = td->td_ucred;
822 
823 	DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
824 	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
825 		return (ENOSYS);
826 
827 	mtx_lock(&sem_mtx);
828 	if (key != IPC_PRIVATE) {
829 		for (semid = 0; semid < seminfo.semmni; semid++) {
830 			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) &&
831 			    sema[semid].u.sem_perm.key == key)
832 				break;
833 		}
834 		if (semid < seminfo.semmni) {
835 			DPRINTF(("found public key\n"));
836 			if ((error = ipcperm(td, &sema[semid].u.sem_perm,
837 			    semflg & 0700))) {
838 				goto done2;
839 			}
840 			if (nsems > 0 && sema[semid].u.sem_nsems < nsems) {
841 				DPRINTF(("too small\n"));
842 				error = EINVAL;
843 				goto done2;
844 			}
845 			if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
846 				DPRINTF(("not exclusive\n"));
847 				error = EEXIST;
848 				goto done2;
849 			}
850 #ifdef MAC
851 			error = mac_sysvsem_check_semget(cred, &sema[semid]);
852 			if (error != 0)
853 				goto done2;
854 #endif
855 			goto found;
856 		}
857 	}
858 
859 	DPRINTF(("need to allocate the semid_kernel\n"));
860 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
861 		if (nsems <= 0 || nsems > seminfo.semmsl) {
862 			DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
863 			    seminfo.semmsl));
864 			error = EINVAL;
865 			goto done2;
866 		}
867 		if (nsems > seminfo.semmns - semtot) {
868 			DPRINTF((
869 			    "not enough semaphores left (need %d, got %d)\n",
870 			    nsems, seminfo.semmns - semtot));
871 			error = ENOSPC;
872 			goto done2;
873 		}
874 		for (semid = 0; semid < seminfo.semmni; semid++) {
875 			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0)
876 				break;
877 		}
878 		if (semid == seminfo.semmni) {
879 			DPRINTF(("no more semid_kernel's available\n"));
880 			error = ENOSPC;
881 			goto done2;
882 		}
883 		DPRINTF(("semid %d is available\n", semid));
884 		mtx_lock(&sema_mtx[semid]);
885 		KASSERT((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0,
886 		    ("Lost semaphore %d", semid));
887 		sema[semid].u.sem_perm.key = key;
888 		sema[semid].u.sem_perm.cuid = cred->cr_uid;
889 		sema[semid].u.sem_perm.uid = cred->cr_uid;
890 		sema[semid].u.sem_perm.cgid = cred->cr_gid;
891 		sema[semid].u.sem_perm.gid = cred->cr_gid;
892 		sema[semid].u.sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
893 		sema[semid].u.sem_perm.seq =
894 		    (sema[semid].u.sem_perm.seq + 1) & 0x7fff;
895 		sema[semid].u.sem_nsems = nsems;
896 		sema[semid].u.sem_otime = 0;
897 		sema[semid].u.sem_ctime = time_second;
898 		sema[semid].u.sem_base = &sem[semtot];
899 		semtot += nsems;
900 		bzero(sema[semid].u.sem_base,
901 		    sizeof(sema[semid].u.sem_base[0])*nsems);
902 #ifdef MAC
903 		mac_sysvsem_create(cred, &sema[semid]);
904 #endif
905 		mtx_unlock(&sema_mtx[semid]);
906 		DPRINTF(("sembase = %p, next = %p\n",
907 		    sema[semid].u.sem_base, &sem[semtot]));
908 	} else {
909 		DPRINTF(("didn't find it and wasn't asked to create it\n"));
910 		error = ENOENT;
911 		goto done2;
912 	}
913 
914 found:
915 	td->td_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].u.sem_perm);
916 done2:
917 	mtx_unlock(&sem_mtx);
918 	return (error);
919 }
920 
921 #ifndef _SYS_SYSPROTO_H_
922 struct semop_args {
923 	int	semid;
924 	struct	sembuf *sops;
925 	size_t	nsops;
926 };
927 #endif
928 int
929 semop(struct thread *td, struct semop_args *uap)
930 {
931 #define SMALL_SOPS	8
932 	struct sembuf small_sops[SMALL_SOPS];
933 	int semid = uap->semid;
934 	size_t nsops = uap->nsops;
935 	struct sembuf *sops;
936 	struct semid_kernel *semakptr;
937 	struct sembuf *sopptr = 0;
938 	struct sem *semptr = 0;
939 	struct sem_undo *suptr;
940 	struct mtx *sema_mtxp;
941 	size_t i, j, k;
942 	int error;
943 	int do_wakeup, do_undos;
944 	unsigned short seq;
945 
946 #ifdef SEM_DEBUG
947 	sops = NULL;
948 #endif
949 	DPRINTF(("call to semop(%d, %p, %u)\n", semid, sops, nsops));
950 
951 	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
952 		return (ENOSYS);
953 
954 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
955 
956 	if (semid < 0 || semid >= seminfo.semmni)
957 		return (EINVAL);
958 
959 	/* Allocate memory for sem_ops */
960 	if (nsops <= SMALL_SOPS)
961 		sops = small_sops;
962 	else if (nsops <= seminfo.semopm)
963 		sops = malloc(nsops * sizeof(*sops), M_TEMP, M_WAITOK);
964 	else {
965 		DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm,
966 		    nsops));
967 		return (E2BIG);
968 	}
969 	if ((error = copyin(uap->sops, sops, nsops * sizeof(sops[0]))) != 0) {
970 		DPRINTF(("error = %d from copyin(%p, %p, %d)\n", error,
971 		    uap->sops, sops, nsops * sizeof(sops[0])));
972 		if (sops != small_sops)
973 			free(sops, M_SEM);
974 		return (error);
975 	}
976 
977 	semakptr = &sema[semid];
978 	sema_mtxp = &sema_mtx[semid];
979 	mtx_lock(sema_mtxp);
980 	if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) {
981 		error = EINVAL;
982 		goto done2;
983 	}
984 	seq = semakptr->u.sem_perm.seq;
985 	if (seq != IPCID_TO_SEQ(uap->semid)) {
986 		error = EINVAL;
987 		goto done2;
988 	}
989 	/*
990 	 * Initial pass thru sops to see what permissions are needed.
991 	 * Also perform any checks that don't need repeating on each
992 	 * attempt to satisfy the request vector.
993 	 */
994 	j = 0;		/* permission needed */
995 	do_undos = 0;
996 	for (i = 0; i < nsops; i++) {
997 		sopptr = &sops[i];
998 		if (sopptr->sem_num >= semakptr->u.sem_nsems) {
999 			error = EFBIG;
1000 			goto done2;
1001 		}
1002 		if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0)
1003 			do_undos = 1;
1004 		j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A;
1005 	}
1006 
1007 	if ((error = ipcperm(td, &semakptr->u.sem_perm, j))) {
1008 		DPRINTF(("error = %d from ipaccess\n", error));
1009 		goto done2;
1010 	}
1011 #ifdef MAC
1012 	error = mac_sysvsem_check_semop(td->td_ucred, semakptr, j);
1013 	if (error != 0)
1014 		goto done2;
1015 #endif
1016 
1017 	/*
1018 	 * Loop trying to satisfy the vector of requests.
1019 	 * If we reach a point where we must wait, any requests already
1020 	 * performed are rolled back and we go to sleep until some other
1021 	 * process wakes us up.  At this point, we start all over again.
1022 	 *
1023 	 * This ensures that from the perspective of other tasks, a set
1024 	 * of requests is atomic (never partially satisfied).
1025 	 */
1026 	for (;;) {
1027 		do_wakeup = 0;
1028 		error = 0;	/* error return if necessary */
1029 
1030 		for (i = 0; i < nsops; i++) {
1031 			sopptr = &sops[i];
1032 			semptr = &semakptr->u.sem_base[sopptr->sem_num];
1033 
1034 			DPRINTF((
1035 			    "semop:  semakptr=%p, sem_base=%p, "
1036 			    "semptr=%p, sem[%d]=%d : op=%d, flag=%s\n",
1037 			    semakptr, semakptr->u.sem_base, semptr,
1038 			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
1039 			    (sopptr->sem_flg & IPC_NOWAIT) ?
1040 			    "nowait" : "wait"));
1041 
1042 			if (sopptr->sem_op < 0) {
1043 				if (semptr->semval + sopptr->sem_op < 0) {
1044 					DPRINTF(("semop:  can't do it now\n"));
1045 					break;
1046 				} else {
1047 					semptr->semval += sopptr->sem_op;
1048 					if (semptr->semval == 0 &&
1049 					    semptr->semzcnt > 0)
1050 						do_wakeup = 1;
1051 				}
1052 			} else if (sopptr->sem_op == 0) {
1053 				if (semptr->semval != 0) {
1054 					DPRINTF(("semop:  not zero now\n"));
1055 					break;
1056 				}
1057 			} else if (semptr->semval + sopptr->sem_op >
1058 			    seminfo.semvmx) {
1059 				error = ERANGE;
1060 				break;
1061 			} else {
1062 				if (semptr->semncnt > 0)
1063 					do_wakeup = 1;
1064 				semptr->semval += sopptr->sem_op;
1065 			}
1066 		}
1067 
1068 		/*
1069 		 * Did we get through the entire vector?
1070 		 */
1071 		if (i >= nsops)
1072 			goto done;
1073 
1074 		/*
1075 		 * No ... rollback anything that we've already done
1076 		 */
1077 		DPRINTF(("semop:  rollback 0 through %d\n", i-1));
1078 		for (j = 0; j < i; j++)
1079 			semakptr->u.sem_base[sops[j].sem_num].semval -=
1080 			    sops[j].sem_op;
1081 
1082 		/* If we detected an error, return it */
1083 		if (error != 0)
1084 			goto done2;
1085 
1086 		/*
1087 		 * If the request that we couldn't satisfy has the
1088 		 * NOWAIT flag set then return with EAGAIN.
1089 		 */
1090 		if (sopptr->sem_flg & IPC_NOWAIT) {
1091 			error = EAGAIN;
1092 			goto done2;
1093 		}
1094 
1095 		if (sopptr->sem_op == 0)
1096 			semptr->semzcnt++;
1097 		else
1098 			semptr->semncnt++;
1099 
1100 		DPRINTF(("semop:  good night!\n"));
1101 		error = msleep(semakptr, sema_mtxp, (PZERO - 4) | PCATCH,
1102 		    "semwait", 0);
1103 		DPRINTF(("semop:  good morning (error=%d)!\n", error));
1104 		/* return code is checked below, after sem[nz]cnt-- */
1105 
1106 		/*
1107 		 * Make sure that the semaphore still exists
1108 		 */
1109 		seq = semakptr->u.sem_perm.seq;
1110 		if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
1111 		    seq != IPCID_TO_SEQ(uap->semid)) {
1112 			error = EIDRM;
1113 			goto done2;
1114 		}
1115 
1116 		/*
1117 		 * Renew the semaphore's pointer after wakeup since
1118 		 * during msleep sem_base may have been modified and semptr
1119 		 * is not valid any more
1120 		 */
1121 		semptr = &semakptr->u.sem_base[sopptr->sem_num];
1122 
1123 		/*
1124 		 * The semaphore is still alive.  Readjust the count of
1125 		 * waiting processes.
1126 		 */
1127 		if (sopptr->sem_op == 0)
1128 			semptr->semzcnt--;
1129 		else
1130 			semptr->semncnt--;
1131 
1132 		/*
1133 		 * Is it really morning, or was our sleep interrupted?
1134 		 * (Delayed check of msleep() return code because we
1135 		 * need to decrement sem[nz]cnt either way.)
1136 		 */
1137 		if (error != 0) {
1138 			error = EINTR;
1139 			goto done2;
1140 		}
1141 		DPRINTF(("semop:  good morning!\n"));
1142 	}
1143 
1144 done:
1145 	/*
1146 	 * Process any SEM_UNDO requests.
1147 	 */
1148 	if (do_undos) {
1149 		SEMUNDO_LOCK();
1150 		suptr = NULL;
1151 		for (i = 0; i < nsops; i++) {
1152 			/*
1153 			 * We only need to deal with SEM_UNDO's for non-zero
1154 			 * op's.
1155 			 */
1156 			int adjval;
1157 
1158 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
1159 				continue;
1160 			adjval = sops[i].sem_op;
1161 			if (adjval == 0)
1162 				continue;
1163 			error = semundo_adjust(td, &suptr, semid, seq,
1164 			    sops[i].sem_num, -adjval);
1165 			if (error == 0)
1166 				continue;
1167 
1168 			/*
1169 			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
1170 			 * Rollback the adjustments to this point and then
1171 			 * rollback the semaphore ups and down so we can return
1172 			 * with an error with all structures restored.  We
1173 			 * rollback the undo's in the exact reverse order that
1174 			 * we applied them.  This guarantees that we won't run
1175 			 * out of space as we roll things back out.
1176 			 */
1177 			for (j = 0; j < i; j++) {
1178 				k = i - j - 1;
1179 				if ((sops[k].sem_flg & SEM_UNDO) == 0)
1180 					continue;
1181 				adjval = sops[k].sem_op;
1182 				if (adjval == 0)
1183 					continue;
1184 				if (semundo_adjust(td, &suptr, semid, seq,
1185 				    sops[k].sem_num, adjval) != 0)
1186 					panic("semop - can't undo undos");
1187 			}
1188 
1189 			for (j = 0; j < nsops; j++)
1190 				semakptr->u.sem_base[sops[j].sem_num].semval -=
1191 				    sops[j].sem_op;
1192 
1193 			DPRINTF(("error = %d from semundo_adjust\n", error));
1194 			SEMUNDO_UNLOCK();
1195 			goto done2;
1196 		} /* loop through the sops */
1197 		SEMUNDO_UNLOCK();
1198 	} /* if (do_undos) */
1199 
1200 	/* We're definitely done - set the sempid's and time */
1201 	for (i = 0; i < nsops; i++) {
1202 		sopptr = &sops[i];
1203 		semptr = &semakptr->u.sem_base[sopptr->sem_num];
1204 		semptr->sempid = td->td_proc->p_pid;
1205 	}
1206 	semakptr->u.sem_otime = time_second;
1207 
1208 	/*
1209 	 * Do a wakeup if any semaphore was up'd whilst something was
1210 	 * sleeping on it.
1211 	 */
1212 	if (do_wakeup) {
1213 		DPRINTF(("semop:  doing wakeup\n"));
1214 		wakeup(semakptr);
1215 		DPRINTF(("semop:  back from wakeup\n"));
1216 	}
1217 	DPRINTF(("semop:  done\n"));
1218 	td->td_retval[0] = 0;
1219 done2:
1220 	mtx_unlock(sema_mtxp);
1221 	if (sops != small_sops)
1222 		free(sops, M_SEM);
1223 	return (error);
1224 }
1225 
1226 /*
1227  * Go through the undo structures for this process and apply the adjustments to
1228  * semaphores.
1229  */
1230 static void
1231 semexit_myhook(void *arg, struct proc *p)
1232 {
1233 	struct sem_undo *suptr;
1234 	struct semid_kernel *semakptr;
1235 	struct mtx *sema_mtxp;
1236 	int semid, semnum, adjval, ix;
1237 	unsigned short seq;
1238 
1239 	/*
1240 	 * Go through the chain of undo vectors looking for one
1241 	 * associated with this process.
1242 	 */
1243 	SEMUNDO_LOCK();
1244 	LIST_FOREACH(suptr, &semu_list, un_next) {
1245 		if (suptr->un_proc == p)
1246 			break;
1247 	}
1248 	if (suptr == NULL) {
1249 		SEMUNDO_UNLOCK();
1250 		return;
1251 	}
1252 	LIST_REMOVE(suptr, un_next);
1253 
1254 	DPRINTF(("proc @%p has undo structure with %d entries\n", p,
1255 	    suptr->un_cnt));
1256 
1257 	/*
1258 	 * If there are any active undo elements then process them.
1259 	 */
1260 	if (suptr->un_cnt > 0) {
1261 		SEMUNDO_UNLOCK();
1262 		for (ix = 0; ix < suptr->un_cnt; ix++) {
1263 			semid = suptr->un_ent[ix].un_id;
1264 			semnum = suptr->un_ent[ix].un_num;
1265 			adjval = suptr->un_ent[ix].un_adjval;
1266 			seq = suptr->un_ent[ix].un_seq;
1267 			semakptr = &sema[semid];
1268 			sema_mtxp = &sema_mtx[semid];
1269 
1270 			mtx_lock(sema_mtxp);
1271 			if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
1272 			    (semakptr->u.sem_perm.seq != seq)) {
1273 				mtx_unlock(sema_mtxp);
1274 				continue;
1275 			}
1276 			if (semnum >= semakptr->u.sem_nsems)
1277 				panic("semexit - semnum out of range");
1278 
1279 			DPRINTF((
1280 			    "semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
1281 			    suptr->un_proc, suptr->un_ent[ix].un_id,
1282 			    suptr->un_ent[ix].un_num,
1283 			    suptr->un_ent[ix].un_adjval,
1284 			    semakptr->u.sem_base[semnum].semval));
1285 
1286 			if (adjval < 0 && semakptr->u.sem_base[semnum].semval <
1287 			    -adjval)
1288 				semakptr->u.sem_base[semnum].semval = 0;
1289 			else
1290 				semakptr->u.sem_base[semnum].semval += adjval;
1291 
1292 			wakeup(semakptr);
1293 			DPRINTF(("semexit:  back from wakeup\n"));
1294 			mtx_unlock(sema_mtxp);
1295 		}
1296 		SEMUNDO_LOCK();
1297 	}
1298 
1299 	/*
1300 	 * Deallocate the undo vector.
1301 	 */
1302 	DPRINTF(("removing vector\n"));
1303 	suptr->un_proc = NULL;
1304 	suptr->un_cnt = 0;
1305 	LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
1306 	SEMUNDO_UNLOCK();
1307 }
1308 
1309 static int
1310 sysctl_sema(SYSCTL_HANDLER_ARGS)
1311 {
1312 
1313 	return (SYSCTL_OUT(req, sema,
1314 	    sizeof(struct semid_kernel) * seminfo.semmni));
1315 }
1316 
1317 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1318     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1319 SYSCALL_MODULE_HELPER(semsys);
1320 SYSCALL_MODULE_HELPER(freebsd7___semctl);
1321 
1322 /* XXX casting to (sy_call_t *) is bogus, as usual. */
1323 static sy_call_t *semcalls[] = {
1324 	(sy_call_t *)freebsd7___semctl, (sy_call_t *)semget,
1325 	(sy_call_t *)semop
1326 };
1327 
1328 /*
1329  * Entry point for all SEM calls.
1330  */
1331 int
1332 semsys(td, uap)
1333 	struct thread *td;
1334 	/* XXX actually varargs. */
1335 	struct semsys_args /* {
1336 		int	which;
1337 		int	a2;
1338 		int	a3;
1339 		int	a4;
1340 		int	a5;
1341 	} */ *uap;
1342 {
1343 	int error;
1344 
1345 	if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC))
1346 		return (ENOSYS);
1347 	if (uap->which < 0 ||
1348 	    uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
1349 		return (EINVAL);
1350 	error = (*semcalls[uap->which])(td, &uap->a2);
1351 	return (error);
1352 }
1353 
1354 #define CP(src, dst, fld)	do { (dst).fld = (src).fld; } while (0)
1355 
1356 #ifndef _SYS_SYSPROTO_H_
1357 struct freebsd7___semctl_args {
1358 	int	semid;
1359 	int	semnum;
1360 	int	cmd;
1361 	union	semun_old *arg;
1362 };
1363 #endif
1364 int
1365 freebsd7___semctl(struct thread *td, struct freebsd7___semctl_args *uap)
1366 {
1367 	struct semid_ds_old dsold;
1368 	struct semid_ds dsbuf;
1369 	union semun_old arg;
1370 	union semun semun;
1371 	register_t rval;
1372 	int error;
1373 
1374 	switch (uap->cmd) {
1375 	case SEM_STAT:
1376 	case IPC_SET:
1377 	case IPC_STAT:
1378 	case GETALL:
1379 	case SETVAL:
1380 	case SETALL:
1381 		error = copyin(uap->arg, &arg, sizeof(arg));
1382 		if (error)
1383 			return (error);
1384 		break;
1385 	}
1386 
1387 	switch (uap->cmd) {
1388 	case SEM_STAT:
1389 	case IPC_STAT:
1390 		semun.buf = &dsbuf;
1391 		break;
1392 	case IPC_SET:
1393 		error = copyin(arg.buf, &dsold, sizeof(dsold));
1394 		if (error)
1395 			return (error);
1396 		ipcperm_old2new(&dsold.sem_perm, &dsbuf.sem_perm);
1397 		CP(dsold, dsbuf, sem_base);
1398 		CP(dsold, dsbuf, sem_nsems);
1399 		CP(dsold, dsbuf, sem_otime);
1400 		CP(dsold, dsbuf, sem_ctime);
1401 		semun.buf = &dsbuf;
1402 		break;
1403 	case GETALL:
1404 	case SETALL:
1405 		semun.array = arg.array;
1406 		break;
1407 	case SETVAL:
1408 		semun.val = arg.val;
1409 		break;
1410 	}
1411 
1412 	error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun,
1413 	    &rval);
1414 	if (error)
1415 		return (error);
1416 
1417 	switch (uap->cmd) {
1418 	case SEM_STAT:
1419 	case IPC_STAT:
1420 		bzero(&dsold, sizeof(dsold));
1421 		ipcperm_new2old(&dsbuf.sem_perm, &dsold.sem_perm);
1422 		CP(dsbuf, dsold, sem_base);
1423 		CP(dsbuf, dsold, sem_nsems);
1424 		CP(dsbuf, dsold, sem_otime);
1425 		CP(dsbuf, dsold, sem_ctime);
1426 		error = copyout(&dsold, arg.buf, sizeof(dsold));
1427 		break;
1428 	}
1429 
1430 	if (error == 0)
1431 		td->td_retval[0] = rval;
1432 	return (error);
1433 }
1434 
1435 #undef CP
1436 
1437 #endif	/* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 ||
1438 	   COMPAT_FREEBSD7 */
1439