xref: /freebsd/sys/kern/sysv_sem.c (revision 732a02b4e77866604a120a275c082bb6221bd2ff)
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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
10  *
11  * Copyright (c) 2003-2005 McAfee, Inc.
12  * Copyright (c) 2016-2017 Robert N. M. Watson
13  * All rights reserved.
14  *
15  * This software was developed for the FreeBSD Project in part by McAfee
16  * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
17  * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
18  * program.
19  *
20  * Portions of this software were developed by BAE Systems, the University of
21  * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
22  * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
23  * Computing (TC) research program.
24  *
25  * Redistribution and use in source and binary forms, with or without
26  * modification, are permitted provided that the following conditions
27  * are met:
28  * 1. Redistributions of source code must retain the above copyright
29  *    notice, this list of conditions and the following disclaimer.
30  * 2. Redistributions in binary form must reproduce the above copyright
31  *    notice, this list of conditions and the following disclaimer in the
32  *    documentation and/or other materials provided with the distribution.
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
35  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
38  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  */
46 
47 #include <sys/cdefs.h>
48 __FBSDID("$FreeBSD$");
49 
50 #include "opt_sysvipc.h"
51 
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/sysproto.h>
55 #include <sys/abi_compat.h>
56 #include <sys/eventhandler.h>
57 #include <sys/kernel.h>
58 #include <sys/proc.h>
59 #include <sys/lock.h>
60 #include <sys/module.h>
61 #include <sys/mutex.h>
62 #include <sys/racct.h>
63 #include <sys/sem.h>
64 #include <sys/sx.h>
65 #include <sys/syscall.h>
66 #include <sys/syscallsubr.h>
67 #include <sys/sysent.h>
68 #include <sys/sysctl.h>
69 #include <sys/uio.h>
70 #include <sys/malloc.h>
71 #include <sys/jail.h>
72 
73 #include <security/audit/audit.h>
74 #include <security/mac/mac_framework.h>
75 
76 FEATURE(sysv_sem, "System V semaphores support");
77 
78 static MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores");
79 
80 #ifdef SEM_DEBUG
81 #define DPRINTF(a)	printf a
82 #else
83 #define DPRINTF(a)
84 #endif
85 
86 static int seminit(void);
87 static int sysvsem_modload(struct module *, int, void *);
88 static int semunload(void);
89 static void semexit_myhook(void *arg, struct proc *p);
90 static int sysctl_sema(SYSCTL_HANDLER_ARGS);
91 static int semvalid(int semid, struct prison *rpr,
92     struct semid_kernel *semakptr);
93 static void sem_remove(int semidx, struct ucred *cred);
94 static struct prison *sem_find_prison(struct ucred *);
95 static int sem_prison_cansee(struct prison *, struct semid_kernel *);
96 static int sem_prison_check(void *, void *);
97 static int sem_prison_set(void *, void *);
98 static int sem_prison_get(void *, void *);
99 static int sem_prison_remove(void *, void *);
100 static void sem_prison_cleanup(struct prison *);
101 
102 #ifndef _SYS_SYSPROTO_H_
103 struct __semctl_args;
104 int __semctl(struct thread *td, struct __semctl_args *uap);
105 struct semget_args;
106 int semget(struct thread *td, struct semget_args *uap);
107 struct semop_args;
108 int semop(struct thread *td, struct semop_args *uap);
109 #endif
110 
111 static struct sem_undo *semu_alloc(struct thread *td);
112 static int semundo_adjust(struct thread *td, struct sem_undo **supptr,
113     int semid, int semseq, int semnum, int adjval);
114 static void semundo_clear(int semid, int semnum);
115 
116 static struct mtx	sem_mtx;	/* semaphore global lock */
117 static struct mtx sem_undo_mtx;
118 static int	semtot = 0;
119 static struct semid_kernel *sema;	/* semaphore id pool */
120 static struct mtx *sema_mtx;	/* semaphore id pool mutexes*/
121 static struct sem *sem;		/* semaphore pool */
122 LIST_HEAD(, sem_undo) semu_list;	/* list of active undo structures */
123 LIST_HEAD(, sem_undo) semu_free_list;	/* list of free undo structures */
124 static int	*semu;		/* undo structure pool */
125 static eventhandler_tag semexit_tag;
126 static unsigned sem_prison_slot;	/* prison OSD slot */
127 
128 #define SEMUNDO_MTX		sem_undo_mtx
129 #define SEMUNDO_LOCK()		mtx_lock(&SEMUNDO_MTX);
130 #define SEMUNDO_UNLOCK()	mtx_unlock(&SEMUNDO_MTX);
131 #define SEMUNDO_LOCKASSERT(how)	mtx_assert(&SEMUNDO_MTX, (how));
132 
133 struct sem {
134 	u_short	semval;		/* semaphore value */
135 	pid_t	sempid;		/* pid of last operation */
136 	u_short	semncnt;	/* # awaiting semval > cval */
137 	u_short	semzcnt;	/* # awaiting semval = 0 */
138 };
139 
140 /*
141  * Undo structure (one per process)
142  */
143 struct sem_undo {
144 	LIST_ENTRY(sem_undo) un_next;	/* ptr to next active undo structure */
145 	struct	proc *un_proc;		/* owner of this structure */
146 	short	un_cnt;			/* # of active entries */
147 	struct undo {
148 		short	un_adjval;	/* adjust on exit values */
149 		short	un_num;		/* semaphore # */
150 		int	un_id;		/* semid */
151 		unsigned short un_seq;
152 	} un_ent[1];			/* undo entries */
153 };
154 
155 /*
156  * Configuration parameters
157  */
158 #ifndef SEMMNI
159 #define SEMMNI	50		/* # of semaphore identifiers */
160 #endif
161 #ifndef SEMMNS
162 #define SEMMNS	340		/* # of semaphores in system */
163 #endif
164 #ifndef SEMUME
165 #define SEMUME	50		/* max # of undo entries per process */
166 #endif
167 #ifndef SEMMNU
168 #define SEMMNU	150		/* # of undo structures in system */
169 #endif
170 
171 /* shouldn't need tuning */
172 #ifndef SEMMSL
173 #define SEMMSL	SEMMNS		/* max # of semaphores per id */
174 #endif
175 #ifndef SEMOPM
176 #define SEMOPM	100		/* max # of operations per semop call */
177 #endif
178 
179 #define SEMVMX	32767		/* semaphore maximum value */
180 #define SEMAEM	16384		/* adjust on exit max value */
181 
182 /*
183  * Due to the way semaphore memory is allocated, we have to ensure that
184  * SEMUSZ is properly aligned.
185  */
186 
187 #define	SEM_ALIGN(bytes) roundup2(bytes, sizeof(long))
188 
189 /* actual size of an undo structure */
190 #define SEMUSZ	SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME]))
191 
192 /*
193  * Macro to find a particular sem_undo vector
194  */
195 #define SEMU(ix) \
196 	((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz))
197 
198 /*
199  * semaphore info struct
200  */
201 struct seminfo seminfo = {
202 	.semmni =	SEMMNI,	/* # of semaphore identifiers */
203 	.semmns =	SEMMNS,	/* # of semaphores in system */
204 	.semmnu =	SEMMNU,	/* # of undo structures in system */
205 	.semmsl =	SEMMSL,	/* max # of semaphores per id */
206 	.semopm =	SEMOPM,	/* max # of operations per semop call */
207 	.semume =	SEMUME,	/* max # of undo entries per process */
208 	.semusz =	SEMUSZ,	/* size in bytes of undo structure */
209 	.semvmx =	SEMVMX,	/* semaphore maximum value */
210 	.semaem =	SEMAEM,	/* adjust on exit max value */
211 };
212 
213 SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RDTUN, &seminfo.semmni, 0,
214     "Number of semaphore identifiers");
215 SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RDTUN, &seminfo.semmns, 0,
216     "Maximum number of semaphores in the system");
217 SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RDTUN, &seminfo.semmnu, 0,
218     "Maximum number of undo structures in the system");
219 SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RWTUN, &seminfo.semmsl, 0,
220     "Max semaphores per id");
221 SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RDTUN, &seminfo.semopm, 0,
222     "Max operations per semop call");
223 SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RDTUN, &seminfo.semume, 0,
224     "Max undo entries per process");
225 SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RDTUN, &seminfo.semusz, 0,
226     "Size in bytes of undo structure");
227 SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RWTUN, &seminfo.semvmx, 0,
228     "Semaphore maximum value");
229 SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RWTUN, &seminfo.semaem, 0,
230     "Adjust on exit max value");
231 SYSCTL_PROC(_kern_ipc, OID_AUTO, sema,
232     CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE,
233     NULL, 0, sysctl_sema, "",
234     "Array of struct semid_kernel for each potential semaphore");
235 
236 static struct syscall_helper_data sem_syscalls[] = {
237 	SYSCALL_INIT_HELPER(__semctl),
238 	SYSCALL_INIT_HELPER(semget),
239 	SYSCALL_INIT_HELPER(semop),
240 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
241     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
242 	SYSCALL_INIT_HELPER(semsys),
243 	SYSCALL_INIT_HELPER_COMPAT(freebsd7___semctl),
244 #endif
245 	SYSCALL_INIT_LAST
246 };
247 
248 #ifdef COMPAT_FREEBSD32
249 #include <compat/freebsd32/freebsd32.h>
250 #include <compat/freebsd32/freebsd32_ipc.h>
251 #include <compat/freebsd32/freebsd32_proto.h>
252 #include <compat/freebsd32/freebsd32_signal.h>
253 #include <compat/freebsd32/freebsd32_syscall.h>
254 #include <compat/freebsd32/freebsd32_util.h>
255 
256 static struct syscall_helper_data sem32_syscalls[] = {
257 	SYSCALL32_INIT_HELPER(freebsd32_semctl),
258 	SYSCALL32_INIT_HELPER_COMPAT(semget),
259 	SYSCALL32_INIT_HELPER_COMPAT(semop),
260 	SYSCALL32_INIT_HELPER(freebsd32_semsys),
261 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
262     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
263 	SYSCALL32_INIT_HELPER(freebsd7_freebsd32_semctl),
264 #endif
265 	SYSCALL_INIT_LAST
266 };
267 #endif
268 
269 static int
270 seminit(void)
271 {
272 	struct prison *pr;
273 	void **rsv;
274 	int i, error;
275 	osd_method_t methods[PR_MAXMETHOD] = {
276 	    [PR_METHOD_CHECK] =		sem_prison_check,
277 	    [PR_METHOD_SET] =		sem_prison_set,
278 	    [PR_METHOD_GET] =		sem_prison_get,
279 	    [PR_METHOD_REMOVE] =	sem_prison_remove,
280 	};
281 
282 	sem = malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK);
283 	sema = malloc(sizeof(struct semid_kernel) * seminfo.semmni, M_SEM,
284 	    M_WAITOK | M_ZERO);
285 	sema_mtx = malloc(sizeof(struct mtx) * seminfo.semmni, M_SEM,
286 	    M_WAITOK | M_ZERO);
287 	semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK);
288 
289 	for (i = 0; i < seminfo.semmni; i++) {
290 		sema[i].u.__sem_base = 0;
291 		sema[i].u.sem_perm.mode = 0;
292 		sema[i].u.sem_perm.seq = 0;
293 #ifdef MAC
294 		mac_sysvsem_init(&sema[i]);
295 #endif
296 	}
297 	for (i = 0; i < seminfo.semmni; i++)
298 		mtx_init(&sema_mtx[i], "semid", NULL, MTX_DEF);
299 	LIST_INIT(&semu_free_list);
300 	for (i = 0; i < seminfo.semmnu; i++) {
301 		struct sem_undo *suptr = SEMU(i);
302 		suptr->un_proc = NULL;
303 		LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
304 	}
305 	LIST_INIT(&semu_list);
306 	mtx_init(&sem_mtx, "sem", NULL, MTX_DEF);
307 	mtx_init(&sem_undo_mtx, "semu", NULL, MTX_DEF);
308 	semexit_tag = EVENTHANDLER_REGISTER(process_exit, semexit_myhook, NULL,
309 	    EVENTHANDLER_PRI_ANY);
310 
311 	/* Set current prisons according to their allow.sysvipc. */
312 	sem_prison_slot = osd_jail_register(NULL, methods);
313 	rsv = osd_reserve(sem_prison_slot);
314 	prison_lock(&prison0);
315 	(void)osd_jail_set_reserved(&prison0, sem_prison_slot, rsv, &prison0);
316 	prison_unlock(&prison0);
317 	rsv = NULL;
318 	sx_slock(&allprison_lock);
319 	TAILQ_FOREACH(pr, &allprison, pr_list) {
320 		if (rsv == NULL)
321 			rsv = osd_reserve(sem_prison_slot);
322 		prison_lock(pr);
323 		if ((pr->pr_allow & PR_ALLOW_SYSVIPC) && pr->pr_ref > 0) {
324 			(void)osd_jail_set_reserved(pr, sem_prison_slot, rsv,
325 			    &prison0);
326 			rsv = NULL;
327 		}
328 		prison_unlock(pr);
329 	}
330 	if (rsv != NULL)
331 		osd_free_reserved(rsv);
332 	sx_sunlock(&allprison_lock);
333 
334 	error = syscall_helper_register(sem_syscalls, SY_THR_STATIC_KLD);
335 	if (error != 0)
336 		return (error);
337 #ifdef COMPAT_FREEBSD32
338 	error = syscall32_helper_register(sem32_syscalls, SY_THR_STATIC_KLD);
339 	if (error != 0)
340 		return (error);
341 #endif
342 	return (0);
343 }
344 
345 static int
346 semunload(void)
347 {
348 	int i;
349 
350 	/* XXXKIB */
351 	if (semtot != 0)
352 		return (EBUSY);
353 
354 #ifdef COMPAT_FREEBSD32
355 	syscall32_helper_unregister(sem32_syscalls);
356 #endif
357 	syscall_helper_unregister(sem_syscalls);
358 	EVENTHANDLER_DEREGISTER(process_exit, semexit_tag);
359 	if (sem_prison_slot != 0)
360 		osd_jail_deregister(sem_prison_slot);
361 #ifdef MAC
362 	for (i = 0; i < seminfo.semmni; i++)
363 		mac_sysvsem_destroy(&sema[i]);
364 #endif
365 	free(sem, M_SEM);
366 	free(sema, M_SEM);
367 	free(semu, M_SEM);
368 	for (i = 0; i < seminfo.semmni; i++)
369 		mtx_destroy(&sema_mtx[i]);
370 	free(sema_mtx, M_SEM);
371 	mtx_destroy(&sem_mtx);
372 	mtx_destroy(&sem_undo_mtx);
373 	return (0);
374 }
375 
376 static int
377 sysvsem_modload(struct module *module, int cmd, void *arg)
378 {
379 	int error = 0;
380 
381 	switch (cmd) {
382 	case MOD_LOAD:
383 		error = seminit();
384 		if (error != 0)
385 			semunload();
386 		break;
387 	case MOD_UNLOAD:
388 		error = semunload();
389 		break;
390 	case MOD_SHUTDOWN:
391 		break;
392 	default:
393 		error = EINVAL;
394 		break;
395 	}
396 	return (error);
397 }
398 
399 static moduledata_t sysvsem_mod = {
400 	"sysvsem",
401 	&sysvsem_modload,
402 	NULL
403 };
404 
405 DECLARE_MODULE(sysvsem, sysvsem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
406 MODULE_VERSION(sysvsem, 1);
407 
408 /*
409  * Allocate a new sem_undo structure for a process
410  * (returns ptr to structure or NULL if no more room)
411  */
412 
413 static struct sem_undo *
414 semu_alloc(struct thread *td)
415 {
416 	struct sem_undo *suptr;
417 
418 	SEMUNDO_LOCKASSERT(MA_OWNED);
419 	if ((suptr = LIST_FIRST(&semu_free_list)) == NULL)
420 		return (NULL);
421 	LIST_REMOVE(suptr, un_next);
422 	LIST_INSERT_HEAD(&semu_list, suptr, un_next);
423 	suptr->un_cnt = 0;
424 	suptr->un_proc = td->td_proc;
425 	return (suptr);
426 }
427 
428 static int
429 semu_try_free(struct sem_undo *suptr)
430 {
431 
432 	SEMUNDO_LOCKASSERT(MA_OWNED);
433 
434 	if (suptr->un_cnt != 0)
435 		return (0);
436 	LIST_REMOVE(suptr, un_next);
437 	LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
438 	return (1);
439 }
440 
441 /*
442  * Adjust a particular entry for a particular proc
443  */
444 
445 static int
446 semundo_adjust(struct thread *td, struct sem_undo **supptr, int semid,
447     int semseq, int semnum, int adjval)
448 {
449 	struct proc *p = td->td_proc;
450 	struct sem_undo *suptr;
451 	struct undo *sunptr;
452 	int i;
453 
454 	SEMUNDO_LOCKASSERT(MA_OWNED);
455 	/* Look for and remember the sem_undo if the caller doesn't provide
456 	   it */
457 
458 	suptr = *supptr;
459 	if (suptr == NULL) {
460 		LIST_FOREACH(suptr, &semu_list, un_next) {
461 			if (suptr->un_proc == p) {
462 				*supptr = suptr;
463 				break;
464 			}
465 		}
466 		if (suptr == NULL) {
467 			if (adjval == 0)
468 				return(0);
469 			suptr = semu_alloc(td);
470 			if (suptr == NULL)
471 				return (ENOSPC);
472 			*supptr = suptr;
473 		}
474 	}
475 
476 	/*
477 	 * Look for the requested entry and adjust it (delete if adjval becomes
478 	 * 0).
479 	 */
480 	sunptr = &suptr->un_ent[0];
481 	for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
482 		if (sunptr->un_id != semid || sunptr->un_num != semnum)
483 			continue;
484 		if (adjval != 0) {
485 			adjval += sunptr->un_adjval;
486 			if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
487 				return (ERANGE);
488 		}
489 		sunptr->un_adjval = adjval;
490 		if (sunptr->un_adjval == 0) {
491 			suptr->un_cnt--;
492 			if (i < suptr->un_cnt)
493 				suptr->un_ent[i] =
494 				    suptr->un_ent[suptr->un_cnt];
495 			if (suptr->un_cnt == 0)
496 				semu_try_free(suptr);
497 		}
498 		return (0);
499 	}
500 
501 	/* Didn't find the right entry - create it */
502 	if (adjval == 0)
503 		return (0);
504 	if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
505 		return (ERANGE);
506 	if (suptr->un_cnt != seminfo.semume) {
507 		sunptr = &suptr->un_ent[suptr->un_cnt];
508 		suptr->un_cnt++;
509 		sunptr->un_adjval = adjval;
510 		sunptr->un_id = semid;
511 		sunptr->un_num = semnum;
512 		sunptr->un_seq = semseq;
513 	} else
514 		return (EINVAL);
515 	return (0);
516 }
517 
518 static void
519 semundo_clear(int semid, int semnum)
520 {
521 	struct sem_undo *suptr, *suptr1;
522 	struct undo *sunptr;
523 	int i;
524 
525 	SEMUNDO_LOCKASSERT(MA_OWNED);
526 	LIST_FOREACH_SAFE(suptr, &semu_list, un_next, suptr1) {
527 		sunptr = &suptr->un_ent[0];
528 		for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
529 			if (sunptr->un_id != semid)
530 				continue;
531 			if (semnum == -1 || sunptr->un_num == semnum) {
532 				suptr->un_cnt--;
533 				if (i < suptr->un_cnt) {
534 					suptr->un_ent[i] =
535 					    suptr->un_ent[suptr->un_cnt];
536 					continue;
537 				}
538 				semu_try_free(suptr);
539 			}
540 			if (semnum != -1)
541 				break;
542 		}
543 	}
544 }
545 
546 static int
547 semvalid(int semid, struct prison *rpr, struct semid_kernel *semakptr)
548 {
549 
550 	return ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
551 	    semakptr->u.sem_perm.seq != IPCID_TO_SEQ(semid) ||
552 	    sem_prison_cansee(rpr, semakptr) ? EINVAL : 0);
553 }
554 
555 static void
556 sem_remove(int semidx, struct ucred *cred)
557 {
558 	struct semid_kernel *semakptr;
559 	int i;
560 
561 	KASSERT(semidx >= 0 && semidx < seminfo.semmni,
562 	    ("semidx out of bounds"));
563 	mtx_assert(&sem_mtx, MA_OWNED);
564 	semakptr = &sema[semidx];
565 	KASSERT(semakptr->u.__sem_base - sem + semakptr->u.sem_nsems <= semtot,
566 	    ("sem_remove: sema %d corrupted sem pointer %p %p %d %d",
567 	    semidx, semakptr->u.__sem_base, sem, semakptr->u.sem_nsems,
568 	    semtot));
569 
570 	semakptr->u.sem_perm.cuid = cred ? cred->cr_uid : 0;
571 	semakptr->u.sem_perm.uid = cred ? cred->cr_uid : 0;
572 	semakptr->u.sem_perm.mode = 0;
573 	racct_sub_cred(semakptr->cred, RACCT_NSEM, semakptr->u.sem_nsems);
574 	crfree(semakptr->cred);
575 	semakptr->cred = NULL;
576 	SEMUNDO_LOCK();
577 	semundo_clear(semidx, -1);
578 	SEMUNDO_UNLOCK();
579 #ifdef MAC
580 	mac_sysvsem_cleanup(semakptr);
581 #endif
582 	wakeup(semakptr);
583 	for (i = 0; i < seminfo.semmni; i++) {
584 		if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
585 		    sema[i].u.__sem_base > semakptr->u.__sem_base)
586 			mtx_lock_flags(&sema_mtx[i], LOP_DUPOK);
587 	}
588 	for (i = semakptr->u.__sem_base - sem + semakptr->u.sem_nsems;
589 	    i < semtot; i++)
590 		sem[i - semakptr->u.sem_nsems] = sem[i];
591 	for (i = 0; i < seminfo.semmni; i++) {
592 		if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
593 		    sema[i].u.__sem_base > semakptr->u.__sem_base) {
594 			sema[i].u.__sem_base -= semakptr->u.sem_nsems;
595 			mtx_unlock(&sema_mtx[i]);
596 		}
597 	}
598 	semtot -= semakptr->u.sem_nsems;
599 }
600 
601 static struct prison *
602 sem_find_prison(struct ucred *cred)
603 {
604 	struct prison *pr, *rpr;
605 
606 	pr = cred->cr_prison;
607 	prison_lock(pr);
608 	rpr = osd_jail_get(pr, sem_prison_slot);
609 	prison_unlock(pr);
610 	return rpr;
611 }
612 
613 static int
614 sem_prison_cansee(struct prison *rpr, struct semid_kernel *semakptr)
615 {
616 
617 	if (semakptr->cred == NULL ||
618 	    !(rpr == semakptr->cred->cr_prison ||
619 	      prison_ischild(rpr, semakptr->cred->cr_prison)))
620 		return (EINVAL);
621 	return (0);
622 }
623 
624 /*
625  * Note that the user-mode half of this passes a union, not a pointer.
626  */
627 #ifndef _SYS_SYSPROTO_H_
628 struct __semctl_args {
629 	int	semid;
630 	int	semnum;
631 	int	cmd;
632 	union	semun *arg;
633 };
634 #endif
635 int
636 sys___semctl(struct thread *td, struct __semctl_args *uap)
637 {
638 	struct semid_ds dsbuf;
639 	union semun arg, semun;
640 	register_t rval;
641 	int error;
642 
643 	switch (uap->cmd) {
644 	case SEM_STAT:
645 	case IPC_SET:
646 	case IPC_STAT:
647 	case GETALL:
648 	case SETVAL:
649 	case SETALL:
650 		error = copyin(uap->arg, &arg, sizeof(arg));
651 		if (error)
652 			return (error);
653 		break;
654 	}
655 
656 	switch (uap->cmd) {
657 	case SEM_STAT:
658 	case IPC_STAT:
659 		semun.buf = &dsbuf;
660 		break;
661 	case IPC_SET:
662 		error = copyin(arg.buf, &dsbuf, sizeof(dsbuf));
663 		if (error)
664 			return (error);
665 		semun.buf = &dsbuf;
666 		break;
667 	case GETALL:
668 	case SETALL:
669 		semun.array = arg.array;
670 		break;
671 	case SETVAL:
672 		semun.val = arg.val;
673 		break;
674 	}
675 
676 	error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun,
677 	    &rval);
678 	if (error)
679 		return (error);
680 
681 	switch (uap->cmd) {
682 	case SEM_STAT:
683 	case IPC_STAT:
684 		error = copyout(&dsbuf, arg.buf, sizeof(dsbuf));
685 		break;
686 	}
687 
688 	if (error == 0)
689 		td->td_retval[0] = rval;
690 	return (error);
691 }
692 
693 int
694 kern_semctl(struct thread *td, int semid, int semnum, int cmd,
695     union semun *arg, register_t *rval)
696 {
697 	u_short *array;
698 	struct ucred *cred = td->td_ucred;
699 	int i, error;
700 	struct prison *rpr;
701 	struct semid_ds *sbuf;
702 	struct semid_kernel *semakptr;
703 	struct mtx *sema_mtxp;
704 	u_short usval, count;
705 	int semidx;
706 
707 	DPRINTF(("call to semctl(%d, %d, %d, 0x%p)\n",
708 	    semid, semnum, cmd, arg));
709 
710 	AUDIT_ARG_SVIPC_CMD(cmd);
711 	AUDIT_ARG_SVIPC_ID(semid);
712 
713 	rpr = sem_find_prison(td->td_ucred);
714 	if (sem == NULL)
715 		return (ENOSYS);
716 
717 	array = NULL;
718 
719 	switch(cmd) {
720 	case SEM_STAT:
721 		/*
722 		 * For this command we assume semid is an array index
723 		 * rather than an IPC id.
724 		 */
725 		if (semid < 0 || semid >= seminfo.semmni)
726 			return (EINVAL);
727 		semakptr = &sema[semid];
728 		sema_mtxp = &sema_mtx[semid];
729 		mtx_lock(sema_mtxp);
730 		if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) {
731 			error = EINVAL;
732 			goto done2;
733 		}
734 		if ((error = sem_prison_cansee(rpr, semakptr)))
735 			goto done2;
736 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
737 			goto done2;
738 #ifdef MAC
739 		error = mac_sysvsem_check_semctl(cred, semakptr, cmd);
740 		if (error != 0)
741 			goto done2;
742 #endif
743 		bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds));
744 		if (cred->cr_prison != semakptr->cred->cr_prison)
745 			arg->buf->sem_perm.key = IPC_PRIVATE;
746 		*rval = IXSEQ_TO_IPCID(semid, semakptr->u.sem_perm);
747 		mtx_unlock(sema_mtxp);
748 		return (0);
749 	}
750 
751 	semidx = IPCID_TO_IX(semid);
752 	if (semidx < 0 || semidx >= seminfo.semmni)
753 		return (EINVAL);
754 
755 	semakptr = &sema[semidx];
756 	sema_mtxp = &sema_mtx[semidx];
757 	if (cmd == IPC_RMID)
758 		mtx_lock(&sem_mtx);
759 	mtx_lock(sema_mtxp);
760 
761 #ifdef MAC
762 	error = mac_sysvsem_check_semctl(cred, semakptr, cmd);
763 	if (error != 0)
764 		goto done2;
765 #endif
766 
767 	error = 0;
768 	*rval = 0;
769 
770 	switch (cmd) {
771 	case IPC_RMID:
772 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
773 			goto done2;
774 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M)))
775 			goto done2;
776 		sem_remove(semidx, cred);
777 		break;
778 
779 	case IPC_SET:
780 		AUDIT_ARG_SVIPC_PERM(&arg->buf->sem_perm);
781 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
782 			goto done2;
783 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M)))
784 			goto done2;
785 		sbuf = arg->buf;
786 		semakptr->u.sem_perm.uid = sbuf->sem_perm.uid;
787 		semakptr->u.sem_perm.gid = sbuf->sem_perm.gid;
788 		semakptr->u.sem_perm.mode = (semakptr->u.sem_perm.mode &
789 		    ~0777) | (sbuf->sem_perm.mode & 0777);
790 		semakptr->u.sem_ctime = time_second;
791 		break;
792 
793 	case IPC_STAT:
794 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
795 			goto done2;
796 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
797 			goto done2;
798 		bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds));
799 		if (cred->cr_prison != semakptr->cred->cr_prison)
800 			arg->buf->sem_perm.key = IPC_PRIVATE;
801 		break;
802 
803 	case GETNCNT:
804 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
805 			goto done2;
806 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
807 			goto done2;
808 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
809 			error = EINVAL;
810 			goto done2;
811 		}
812 		*rval = semakptr->u.__sem_base[semnum].semncnt;
813 		break;
814 
815 	case GETPID:
816 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
817 			goto done2;
818 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
819 			goto done2;
820 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
821 			error = EINVAL;
822 			goto done2;
823 		}
824 		*rval = semakptr->u.__sem_base[semnum].sempid;
825 		break;
826 
827 	case GETVAL:
828 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
829 			goto done2;
830 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
831 			goto done2;
832 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
833 			error = EINVAL;
834 			goto done2;
835 		}
836 		*rval = semakptr->u.__sem_base[semnum].semval;
837 		break;
838 
839 	case GETALL:
840 		/*
841 		 * Unfortunately, callers of this function don't know
842 		 * in advance how many semaphores are in this set.
843 		 * While we could just allocate the maximum size array
844 		 * and pass the actual size back to the caller, that
845 		 * won't work for SETALL since we can't copyin() more
846 		 * data than the user specified as we may return a
847 		 * spurious EFAULT.
848 		 *
849 		 * Note that the number of semaphores in a set is
850 		 * fixed for the life of that set.  The only way that
851 		 * the 'count' could change while are blocked in
852 		 * malloc() is if this semaphore set were destroyed
853 		 * and a new one created with the same index.
854 		 * However, semvalid() will catch that due to the
855 		 * sequence number unless exactly 0x8000 (or a
856 		 * multiple thereof) semaphore sets for the same index
857 		 * are created and destroyed while we are in malloc!
858 		 *
859 		 */
860 		count = semakptr->u.sem_nsems;
861 		mtx_unlock(sema_mtxp);
862 		array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK);
863 		mtx_lock(sema_mtxp);
864 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
865 			goto done2;
866 		KASSERT(count == semakptr->u.sem_nsems, ("nsems changed"));
867 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
868 			goto done2;
869 		for (i = 0; i < semakptr->u.sem_nsems; i++)
870 			array[i] = semakptr->u.__sem_base[i].semval;
871 		mtx_unlock(sema_mtxp);
872 		error = copyout(array, arg->array, count * sizeof(*array));
873 		mtx_lock(sema_mtxp);
874 		break;
875 
876 	case GETZCNT:
877 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
878 			goto done2;
879 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
880 			goto done2;
881 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
882 			error = EINVAL;
883 			goto done2;
884 		}
885 		*rval = semakptr->u.__sem_base[semnum].semzcnt;
886 		break;
887 
888 	case SETVAL:
889 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
890 			goto done2;
891 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W)))
892 			goto done2;
893 		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
894 			error = EINVAL;
895 			goto done2;
896 		}
897 		if (arg->val < 0 || arg->val > seminfo.semvmx) {
898 			error = ERANGE;
899 			goto done2;
900 		}
901 		semakptr->u.__sem_base[semnum].semval = arg->val;
902 		SEMUNDO_LOCK();
903 		semundo_clear(semidx, semnum);
904 		SEMUNDO_UNLOCK();
905 		wakeup(semakptr);
906 		break;
907 
908 	case SETALL:
909 		/*
910 		 * See comment on GETALL for why 'count' shouldn't change
911 		 * and why we require a userland buffer.
912 		 */
913 		count = semakptr->u.sem_nsems;
914 		mtx_unlock(sema_mtxp);
915 		array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK);
916 		error = copyin(arg->array, array, count * sizeof(*array));
917 		mtx_lock(sema_mtxp);
918 		if (error)
919 			break;
920 		if ((error = semvalid(semid, rpr, semakptr)) != 0)
921 			goto done2;
922 		KASSERT(count == semakptr->u.sem_nsems, ("nsems changed"));
923 		if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W)))
924 			goto done2;
925 		for (i = 0; i < semakptr->u.sem_nsems; i++) {
926 			usval = array[i];
927 			if (usval > seminfo.semvmx) {
928 				error = ERANGE;
929 				break;
930 			}
931 			semakptr->u.__sem_base[i].semval = usval;
932 		}
933 		SEMUNDO_LOCK();
934 		semundo_clear(semidx, -1);
935 		SEMUNDO_UNLOCK();
936 		wakeup(semakptr);
937 		break;
938 
939 	default:
940 		error = EINVAL;
941 		break;
942 	}
943 
944 done2:
945 	mtx_unlock(sema_mtxp);
946 	if (cmd == IPC_RMID)
947 		mtx_unlock(&sem_mtx);
948 	if (array != NULL)
949 		free(array, M_TEMP);
950 	return(error);
951 }
952 
953 #ifndef _SYS_SYSPROTO_H_
954 struct semget_args {
955 	key_t	key;
956 	int	nsems;
957 	int	semflg;
958 };
959 #endif
960 int
961 sys_semget(struct thread *td, struct semget_args *uap)
962 {
963 	int semid, error = 0;
964 	int key = uap->key;
965 	int nsems = uap->nsems;
966 	int semflg = uap->semflg;
967 	struct ucred *cred = td->td_ucred;
968 
969 	DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
970 
971 	AUDIT_ARG_VALUE(semflg);
972 
973 	if (sem_find_prison(cred) == NULL)
974 		return (ENOSYS);
975 
976 	mtx_lock(&sem_mtx);
977 	if (key != IPC_PRIVATE) {
978 		for (semid = 0; semid < seminfo.semmni; semid++) {
979 			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) &&
980 			    sema[semid].cred != NULL &&
981 			    sema[semid].cred->cr_prison == cred->cr_prison &&
982 			    sema[semid].u.sem_perm.key == key)
983 				break;
984 		}
985 		if (semid < seminfo.semmni) {
986 			AUDIT_ARG_SVIPC_ID(semid);
987 			DPRINTF(("found public key\n"));
988 			if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
989 				DPRINTF(("not exclusive\n"));
990 				error = EEXIST;
991 				goto done2;
992 			}
993 			if ((error = ipcperm(td, &sema[semid].u.sem_perm,
994 			    semflg & 0700))) {
995 				goto done2;
996 			}
997 			if (nsems > 0 && sema[semid].u.sem_nsems < nsems) {
998 				DPRINTF(("too small\n"));
999 				error = EINVAL;
1000 				goto done2;
1001 			}
1002 #ifdef MAC
1003 			error = mac_sysvsem_check_semget(cred, &sema[semid]);
1004 			if (error != 0)
1005 				goto done2;
1006 #endif
1007 			goto found;
1008 		}
1009 	}
1010 
1011 	DPRINTF(("need to allocate the semid_kernel\n"));
1012 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
1013 		if (nsems <= 0 || nsems > seminfo.semmsl) {
1014 			DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
1015 			    seminfo.semmsl));
1016 			error = EINVAL;
1017 			goto done2;
1018 		}
1019 		if (nsems > seminfo.semmns - semtot) {
1020 			DPRINTF((
1021 			    "not enough semaphores left (need %d, got %d)\n",
1022 			    nsems, seminfo.semmns - semtot));
1023 			error = ENOSPC;
1024 			goto done2;
1025 		}
1026 		for (semid = 0; semid < seminfo.semmni; semid++) {
1027 			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0)
1028 				break;
1029 		}
1030 		if (semid == seminfo.semmni) {
1031 			DPRINTF(("no more semid_kernel's available\n"));
1032 			error = ENOSPC;
1033 			goto done2;
1034 		}
1035 #ifdef RACCT
1036 		if (racct_enable) {
1037 			PROC_LOCK(td->td_proc);
1038 			error = racct_add(td->td_proc, RACCT_NSEM, nsems);
1039 			PROC_UNLOCK(td->td_proc);
1040 			if (error != 0) {
1041 				error = ENOSPC;
1042 				goto done2;
1043 			}
1044 		}
1045 #endif
1046 		DPRINTF(("semid %d is available\n", semid));
1047 		mtx_lock(&sema_mtx[semid]);
1048 		KASSERT((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0,
1049 		    ("Lost semaphore %d", semid));
1050 		sema[semid].u.sem_perm.key = key;
1051 		sema[semid].u.sem_perm.cuid = cred->cr_uid;
1052 		sema[semid].u.sem_perm.uid = cred->cr_uid;
1053 		sema[semid].u.sem_perm.cgid = cred->cr_gid;
1054 		sema[semid].u.sem_perm.gid = cred->cr_gid;
1055 		sema[semid].u.sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
1056 		sema[semid].cred = crhold(cred);
1057 		sema[semid].u.sem_perm.seq =
1058 		    (sema[semid].u.sem_perm.seq + 1) & 0x7fff;
1059 		sema[semid].u.sem_nsems = nsems;
1060 		sema[semid].u.sem_otime = 0;
1061 		sema[semid].u.sem_ctime = time_second;
1062 		sema[semid].u.__sem_base = &sem[semtot];
1063 		semtot += nsems;
1064 		bzero(sema[semid].u.__sem_base,
1065 		    sizeof(sema[semid].u.__sem_base[0])*nsems);
1066 #ifdef MAC
1067 		mac_sysvsem_create(cred, &sema[semid]);
1068 #endif
1069 		mtx_unlock(&sema_mtx[semid]);
1070 		DPRINTF(("sembase = %p, next = %p\n",
1071 		    sema[semid].u.__sem_base, &sem[semtot]));
1072 	} else {
1073 		DPRINTF(("didn't find it and wasn't asked to create it\n"));
1074 		error = ENOENT;
1075 		goto done2;
1076 	}
1077 
1078 found:
1079 	td->td_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].u.sem_perm);
1080 done2:
1081 	mtx_unlock(&sem_mtx);
1082 	return (error);
1083 }
1084 
1085 #ifndef _SYS_SYSPROTO_H_
1086 struct semop_args {
1087 	int	semid;
1088 	struct	sembuf *sops;
1089 	size_t	nsops;
1090 };
1091 #endif
1092 int
1093 sys_semop(struct thread *td, struct semop_args *uap)
1094 {
1095 #define SMALL_SOPS	8
1096 	struct sembuf small_sops[SMALL_SOPS];
1097 	int semid = uap->semid;
1098 	size_t nsops = uap->nsops;
1099 	struct prison *rpr;
1100 	struct sembuf *sops;
1101 	struct semid_kernel *semakptr;
1102 	struct sembuf *sopptr = NULL;
1103 	struct sem *semptr = NULL;
1104 	struct sem_undo *suptr;
1105 	struct mtx *sema_mtxp;
1106 	size_t i, j, k;
1107 	int error;
1108 	int do_wakeup, do_undos;
1109 	unsigned short seq;
1110 
1111 #ifdef SEM_DEBUG
1112 	sops = NULL;
1113 #endif
1114 	DPRINTF(("call to semop(%d, %p, %u)\n", semid, sops, nsops));
1115 
1116 	AUDIT_ARG_SVIPC_ID(semid);
1117 
1118 	rpr = sem_find_prison(td->td_ucred);
1119 	if (sem == NULL)
1120 		return (ENOSYS);
1121 
1122 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
1123 
1124 	if (semid < 0 || semid >= seminfo.semmni)
1125 		return (EINVAL);
1126 
1127 	/* Allocate memory for sem_ops */
1128 	if (nsops <= SMALL_SOPS)
1129 		sops = small_sops;
1130 	else if (nsops > seminfo.semopm) {
1131 		DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm,
1132 		    nsops));
1133 		return (E2BIG);
1134 	} else {
1135 #ifdef RACCT
1136 		if (racct_enable) {
1137 			PROC_LOCK(td->td_proc);
1138 			if (nsops >
1139 			    racct_get_available(td->td_proc, RACCT_NSEMOP)) {
1140 				PROC_UNLOCK(td->td_proc);
1141 				return (E2BIG);
1142 			}
1143 			PROC_UNLOCK(td->td_proc);
1144 		}
1145 #endif
1146 
1147 		sops = malloc(nsops * sizeof(*sops), M_TEMP, M_WAITOK);
1148 	}
1149 	if ((error = copyin(uap->sops, sops, nsops * sizeof(sops[0]))) != 0) {
1150 		DPRINTF(("error = %d from copyin(%p, %p, %d)\n", error,
1151 		    uap->sops, sops, nsops * sizeof(sops[0])));
1152 		if (sops != small_sops)
1153 			free(sops, M_TEMP);
1154 		return (error);
1155 	}
1156 
1157 	semakptr = &sema[semid];
1158 	sema_mtxp = &sema_mtx[semid];
1159 	mtx_lock(sema_mtxp);
1160 	if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) {
1161 		error = EINVAL;
1162 		goto done2;
1163 	}
1164 	seq = semakptr->u.sem_perm.seq;
1165 	if (seq != IPCID_TO_SEQ(uap->semid)) {
1166 		error = EINVAL;
1167 		goto done2;
1168 	}
1169 	if ((error = sem_prison_cansee(rpr, semakptr)) != 0)
1170 		goto done2;
1171 	/*
1172 	 * Initial pass through sops to see what permissions are needed.
1173 	 * Also perform any checks that don't need repeating on each
1174 	 * attempt to satisfy the request vector.
1175 	 */
1176 	j = 0;		/* permission needed */
1177 	do_undos = 0;
1178 	for (i = 0; i < nsops; i++) {
1179 		sopptr = &sops[i];
1180 		if (sopptr->sem_num >= semakptr->u.sem_nsems) {
1181 			error = EFBIG;
1182 			goto done2;
1183 		}
1184 		if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0)
1185 			do_undos = 1;
1186 		j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A;
1187 	}
1188 
1189 	if ((error = ipcperm(td, &semakptr->u.sem_perm, j))) {
1190 		DPRINTF(("error = %d from ipaccess\n", error));
1191 		goto done2;
1192 	}
1193 #ifdef MAC
1194 	error = mac_sysvsem_check_semop(td->td_ucred, semakptr, j);
1195 	if (error != 0)
1196 		goto done2;
1197 #endif
1198 
1199 	/*
1200 	 * Loop trying to satisfy the vector of requests.
1201 	 * If we reach a point where we must wait, any requests already
1202 	 * performed are rolled back and we go to sleep until some other
1203 	 * process wakes us up.  At this point, we start all over again.
1204 	 *
1205 	 * This ensures that from the perspective of other tasks, a set
1206 	 * of requests is atomic (never partially satisfied).
1207 	 */
1208 	for (;;) {
1209 		do_wakeup = 0;
1210 		error = 0;	/* error return if necessary */
1211 
1212 		for (i = 0; i < nsops; i++) {
1213 			sopptr = &sops[i];
1214 			semptr = &semakptr->u.__sem_base[sopptr->sem_num];
1215 
1216 			DPRINTF((
1217 			    "semop:  semakptr=%p, __sem_base=%p, "
1218 			    "semptr=%p, sem[%d]=%d : op=%d, flag=%s\n",
1219 			    semakptr, semakptr->u.__sem_base, semptr,
1220 			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
1221 			    (sopptr->sem_flg & IPC_NOWAIT) ?
1222 			    "nowait" : "wait"));
1223 
1224 			if (sopptr->sem_op < 0) {
1225 				if (semptr->semval + sopptr->sem_op < 0) {
1226 					DPRINTF(("semop:  can't do it now\n"));
1227 					break;
1228 				} else {
1229 					semptr->semval += sopptr->sem_op;
1230 					if (semptr->semval == 0 &&
1231 					    semptr->semzcnt > 0)
1232 						do_wakeup = 1;
1233 				}
1234 			} else if (sopptr->sem_op == 0) {
1235 				if (semptr->semval != 0) {
1236 					DPRINTF(("semop:  not zero now\n"));
1237 					break;
1238 				}
1239 			} else if (semptr->semval + sopptr->sem_op >
1240 			    seminfo.semvmx) {
1241 				error = ERANGE;
1242 				break;
1243 			} else {
1244 				if (semptr->semncnt > 0)
1245 					do_wakeup = 1;
1246 				semptr->semval += sopptr->sem_op;
1247 			}
1248 		}
1249 
1250 		/*
1251 		 * Did we get through the entire vector?
1252 		 */
1253 		if (i >= nsops)
1254 			goto done;
1255 
1256 		/*
1257 		 * No ... rollback anything that we've already done
1258 		 */
1259 		DPRINTF(("semop:  rollback 0 through %d\n", i-1));
1260 		for (j = 0; j < i; j++)
1261 			semakptr->u.__sem_base[sops[j].sem_num].semval -=
1262 			    sops[j].sem_op;
1263 
1264 		/* If we detected an error, return it */
1265 		if (error != 0)
1266 			goto done2;
1267 
1268 		/*
1269 		 * If the request that we couldn't satisfy has the
1270 		 * NOWAIT flag set then return with EAGAIN.
1271 		 */
1272 		if (sopptr->sem_flg & IPC_NOWAIT) {
1273 			error = EAGAIN;
1274 			goto done2;
1275 		}
1276 
1277 		if (sopptr->sem_op == 0)
1278 			semptr->semzcnt++;
1279 		else
1280 			semptr->semncnt++;
1281 
1282 		DPRINTF(("semop:  good night!\n"));
1283 		error = msleep(semakptr, sema_mtxp, (PZERO - 4) | PCATCH,
1284 		    "semwait", 0);
1285 		DPRINTF(("semop:  good morning (error=%d)!\n", error));
1286 		/* return code is checked below, after sem[nz]cnt-- */
1287 
1288 		/*
1289 		 * Make sure that the semaphore still exists
1290 		 */
1291 		seq = semakptr->u.sem_perm.seq;
1292 		if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
1293 		    seq != IPCID_TO_SEQ(uap->semid)) {
1294 			error = EIDRM;
1295 			goto done2;
1296 		}
1297 
1298 		/*
1299 		 * Renew the semaphore's pointer after wakeup since
1300 		 * during msleep __sem_base may have been modified and semptr
1301 		 * is not valid any more
1302 		 */
1303 		semptr = &semakptr->u.__sem_base[sopptr->sem_num];
1304 
1305 		/*
1306 		 * The semaphore is still alive.  Readjust the count of
1307 		 * waiting processes.
1308 		 */
1309 		if (sopptr->sem_op == 0)
1310 			semptr->semzcnt--;
1311 		else
1312 			semptr->semncnt--;
1313 
1314 		/*
1315 		 * Is it really morning, or was our sleep interrupted?
1316 		 * (Delayed check of msleep() return code because we
1317 		 * need to decrement sem[nz]cnt either way.)
1318 		 */
1319 		if (error != 0) {
1320 			error = EINTR;
1321 			goto done2;
1322 		}
1323 		DPRINTF(("semop:  good morning!\n"));
1324 	}
1325 
1326 done:
1327 	/*
1328 	 * Process any SEM_UNDO requests.
1329 	 */
1330 	if (do_undos) {
1331 		SEMUNDO_LOCK();
1332 		suptr = NULL;
1333 		for (i = 0; i < nsops; i++) {
1334 			/*
1335 			 * We only need to deal with SEM_UNDO's for non-zero
1336 			 * op's.
1337 			 */
1338 			int adjval;
1339 
1340 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
1341 				continue;
1342 			adjval = sops[i].sem_op;
1343 			if (adjval == 0)
1344 				continue;
1345 			error = semundo_adjust(td, &suptr, semid, seq,
1346 			    sops[i].sem_num, -adjval);
1347 			if (error == 0)
1348 				continue;
1349 
1350 			/*
1351 			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
1352 			 * Rollback the adjustments to this point and then
1353 			 * rollback the semaphore ups and down so we can return
1354 			 * with an error with all structures restored.  We
1355 			 * rollback the undo's in the exact reverse order that
1356 			 * we applied them.  This guarantees that we won't run
1357 			 * out of space as we roll things back out.
1358 			 */
1359 			for (j = 0; j < i; j++) {
1360 				k = i - j - 1;
1361 				if ((sops[k].sem_flg & SEM_UNDO) == 0)
1362 					continue;
1363 				adjval = sops[k].sem_op;
1364 				if (adjval == 0)
1365 					continue;
1366 				if (semundo_adjust(td, &suptr, semid, seq,
1367 				    sops[k].sem_num, adjval) != 0)
1368 					panic("semop - can't undo undos");
1369 			}
1370 
1371 			for (j = 0; j < nsops; j++)
1372 				semakptr->u.__sem_base[sops[j].sem_num].semval -=
1373 				    sops[j].sem_op;
1374 
1375 			DPRINTF(("error = %d from semundo_adjust\n", error));
1376 			SEMUNDO_UNLOCK();
1377 			goto done2;
1378 		} /* loop through the sops */
1379 		SEMUNDO_UNLOCK();
1380 	} /* if (do_undos) */
1381 
1382 	/* We're definitely done - set the sempid's and time */
1383 	for (i = 0; i < nsops; i++) {
1384 		sopptr = &sops[i];
1385 		semptr = &semakptr->u.__sem_base[sopptr->sem_num];
1386 		semptr->sempid = td->td_proc->p_pid;
1387 	}
1388 	semakptr->u.sem_otime = time_second;
1389 
1390 	/*
1391 	 * Do a wakeup if any semaphore was up'd whilst something was
1392 	 * sleeping on it.
1393 	 */
1394 	if (do_wakeup) {
1395 		DPRINTF(("semop:  doing wakeup\n"));
1396 		wakeup(semakptr);
1397 		DPRINTF(("semop:  back from wakeup\n"));
1398 	}
1399 	DPRINTF(("semop:  done\n"));
1400 	td->td_retval[0] = 0;
1401 done2:
1402 	mtx_unlock(sema_mtxp);
1403 	if (sops != small_sops)
1404 		free(sops, M_TEMP);
1405 	return (error);
1406 }
1407 
1408 /*
1409  * Go through the undo structures for this process and apply the adjustments to
1410  * semaphores.
1411  */
1412 static void
1413 semexit_myhook(void *arg, struct proc *p)
1414 {
1415 	struct sem_undo *suptr;
1416 	struct semid_kernel *semakptr;
1417 	struct mtx *sema_mtxp;
1418 	int semid, semnum, adjval, ix;
1419 	unsigned short seq;
1420 
1421 	/*
1422 	 * Go through the chain of undo vectors looking for one
1423 	 * associated with this process.
1424 	 */
1425 	if (LIST_EMPTY(&semu_list))
1426 		return;
1427 	SEMUNDO_LOCK();
1428 	LIST_FOREACH(suptr, &semu_list, un_next) {
1429 		if (suptr->un_proc == p)
1430 			break;
1431 	}
1432 	if (suptr == NULL) {
1433 		SEMUNDO_UNLOCK();
1434 		return;
1435 	}
1436 	LIST_REMOVE(suptr, un_next);
1437 
1438 	DPRINTF(("proc @%p has undo structure with %d entries\n", p,
1439 	    suptr->un_cnt));
1440 
1441 	/*
1442 	 * If there are any active undo elements then process them.
1443 	 */
1444 	if (suptr->un_cnt > 0) {
1445 		SEMUNDO_UNLOCK();
1446 		for (ix = 0; ix < suptr->un_cnt; ix++) {
1447 			semid = suptr->un_ent[ix].un_id;
1448 			semnum = suptr->un_ent[ix].un_num;
1449 			adjval = suptr->un_ent[ix].un_adjval;
1450 			seq = suptr->un_ent[ix].un_seq;
1451 			semakptr = &sema[semid];
1452 			sema_mtxp = &sema_mtx[semid];
1453 
1454 			mtx_lock(sema_mtxp);
1455 			if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
1456 			    (semakptr->u.sem_perm.seq != seq)) {
1457 				mtx_unlock(sema_mtxp);
1458 				continue;
1459 			}
1460 			if (semnum >= semakptr->u.sem_nsems)
1461 				panic("semexit - semnum out of range");
1462 
1463 			DPRINTF((
1464 			    "semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
1465 			    suptr->un_proc, suptr->un_ent[ix].un_id,
1466 			    suptr->un_ent[ix].un_num,
1467 			    suptr->un_ent[ix].un_adjval,
1468 			    semakptr->u.__sem_base[semnum].semval));
1469 
1470 			if (adjval < 0 && semakptr->u.__sem_base[semnum].semval <
1471 			    -adjval)
1472 				semakptr->u.__sem_base[semnum].semval = 0;
1473 			else
1474 				semakptr->u.__sem_base[semnum].semval += adjval;
1475 
1476 			wakeup(semakptr);
1477 			DPRINTF(("semexit:  back from wakeup\n"));
1478 			mtx_unlock(sema_mtxp);
1479 		}
1480 		SEMUNDO_LOCK();
1481 	}
1482 
1483 	/*
1484 	 * Deallocate the undo vector.
1485 	 */
1486 	DPRINTF(("removing vector\n"));
1487 	suptr->un_proc = NULL;
1488 	suptr->un_cnt = 0;
1489 	LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
1490 	SEMUNDO_UNLOCK();
1491 }
1492 
1493 static int
1494 sysctl_sema(SYSCTL_HANDLER_ARGS)
1495 {
1496 	struct prison *pr, *rpr;
1497 	struct semid_kernel tsemak;
1498 #ifdef COMPAT_FREEBSD32
1499 	struct semid_kernel32 tsemak32;
1500 #endif
1501 	void *outaddr;
1502 	size_t outsize;
1503 	int error, i;
1504 
1505 	pr = req->td->td_ucred->cr_prison;
1506 	rpr = sem_find_prison(req->td->td_ucred);
1507 	error = 0;
1508 	for (i = 0; i < seminfo.semmni; i++) {
1509 		mtx_lock(&sema_mtx[i]);
1510 		if ((sema[i].u.sem_perm.mode & SEM_ALLOC) == 0 ||
1511 		    rpr == NULL || sem_prison_cansee(rpr, &sema[i]) != 0)
1512 			bzero(&tsemak, sizeof(tsemak));
1513 		else {
1514 			tsemak = sema[i];
1515 			if (tsemak.cred->cr_prison != pr)
1516 				tsemak.u.sem_perm.key = IPC_PRIVATE;
1517 		}
1518 		mtx_unlock(&sema_mtx[i]);
1519 #ifdef COMPAT_FREEBSD32
1520 		if (SV_CURPROC_FLAG(SV_ILP32)) {
1521 			bzero(&tsemak32, sizeof(tsemak32));
1522 			freebsd32_ipcperm_out(&tsemak.u.sem_perm,
1523 			    &tsemak32.u.sem_perm);
1524 			/* Don't copy u.__sem_base */
1525 			CP(tsemak, tsemak32, u.sem_nsems);
1526 			CP(tsemak, tsemak32, u.sem_otime);
1527 			CP(tsemak, tsemak32, u.sem_ctime);
1528 			/* Don't copy label or cred */
1529 			outaddr = &tsemak32;
1530 			outsize = sizeof(tsemak32);
1531 		} else
1532 #endif
1533 		{
1534 			tsemak.u.__sem_base = NULL;
1535 			tsemak.label = NULL;
1536 			tsemak.cred = NULL;
1537 			outaddr = &tsemak;
1538 			outsize = sizeof(tsemak);
1539 		}
1540 		error = SYSCTL_OUT(req, outaddr, outsize);
1541 		if (error != 0)
1542 			break;
1543 	}
1544 	return (error);
1545 }
1546 
1547 static int
1548 sem_prison_check(void *obj, void *data)
1549 {
1550 	struct prison *pr = obj;
1551 	struct prison *prpr;
1552 	struct vfsoptlist *opts = data;
1553 	int error, jsys;
1554 
1555 	/*
1556 	 * sysvsem is a jailsys integer.
1557 	 * It must be "disable" if the parent jail is disabled.
1558 	 */
1559 	error = vfs_copyopt(opts, "sysvsem", &jsys, sizeof(jsys));
1560 	if (error != ENOENT) {
1561 		if (error != 0)
1562 			return (error);
1563 		switch (jsys) {
1564 		case JAIL_SYS_DISABLE:
1565 			break;
1566 		case JAIL_SYS_NEW:
1567 		case JAIL_SYS_INHERIT:
1568 			prison_lock(pr->pr_parent);
1569 			prpr = osd_jail_get(pr->pr_parent, sem_prison_slot);
1570 			prison_unlock(pr->pr_parent);
1571 			if (prpr == NULL)
1572 				return (EPERM);
1573 			break;
1574 		default:
1575 			return (EINVAL);
1576 		}
1577 	}
1578 
1579 	return (0);
1580 }
1581 
1582 static int
1583 sem_prison_set(void *obj, void *data)
1584 {
1585 	struct prison *pr = obj;
1586 	struct prison *tpr, *orpr, *nrpr, *trpr;
1587 	struct vfsoptlist *opts = data;
1588 	void *rsv;
1589 	int jsys, descend;
1590 
1591 	/*
1592 	 * sysvsem controls which jail is the root of the associated sems (this
1593 	 * jail or same as the parent), or if the feature is available at all.
1594 	 */
1595 	if (vfs_copyopt(opts, "sysvsem", &jsys, sizeof(jsys)) == ENOENT)
1596 		jsys = vfs_flagopt(opts, "allow.sysvipc", NULL, 0)
1597 		    ? JAIL_SYS_INHERIT
1598 		    : vfs_flagopt(opts, "allow.nosysvipc", NULL, 0)
1599 		    ? JAIL_SYS_DISABLE
1600 		    : -1;
1601 	if (jsys == JAIL_SYS_DISABLE) {
1602 		prison_lock(pr);
1603 		orpr = osd_jail_get(pr, sem_prison_slot);
1604 		if (orpr != NULL)
1605 			osd_jail_del(pr, sem_prison_slot);
1606 		prison_unlock(pr);
1607 		if (orpr != NULL) {
1608 			if (orpr == pr)
1609 				sem_prison_cleanup(pr);
1610 			/* Disable all child jails as well. */
1611 			FOREACH_PRISON_DESCENDANT(pr, tpr, descend) {
1612 				prison_lock(tpr);
1613 				trpr = osd_jail_get(tpr, sem_prison_slot);
1614 				if (trpr != NULL) {
1615 					osd_jail_del(tpr, sem_prison_slot);
1616 					prison_unlock(tpr);
1617 					if (trpr == tpr)
1618 						sem_prison_cleanup(tpr);
1619 				} else {
1620 					prison_unlock(tpr);
1621 					descend = 0;
1622 				}
1623 			}
1624 		}
1625 	} else if (jsys != -1) {
1626 		if (jsys == JAIL_SYS_NEW)
1627 			nrpr = pr;
1628 		else {
1629 			prison_lock(pr->pr_parent);
1630 			nrpr = osd_jail_get(pr->pr_parent, sem_prison_slot);
1631 			prison_unlock(pr->pr_parent);
1632 		}
1633 		rsv = osd_reserve(sem_prison_slot);
1634 		prison_lock(pr);
1635 		orpr = osd_jail_get(pr, sem_prison_slot);
1636 		if (orpr != nrpr)
1637 			(void)osd_jail_set_reserved(pr, sem_prison_slot, rsv,
1638 			    nrpr);
1639 		else
1640 			osd_free_reserved(rsv);
1641 		prison_unlock(pr);
1642 		if (orpr != nrpr) {
1643 			if (orpr == pr)
1644 				sem_prison_cleanup(pr);
1645 			if (orpr != NULL) {
1646 				/* Change child jails matching the old root, */
1647 				FOREACH_PRISON_DESCENDANT(pr, tpr, descend) {
1648 					prison_lock(tpr);
1649 					trpr = osd_jail_get(tpr,
1650 					    sem_prison_slot);
1651 					if (trpr == orpr) {
1652 						(void)osd_jail_set(tpr,
1653 						    sem_prison_slot, nrpr);
1654 						prison_unlock(tpr);
1655 						if (trpr == tpr)
1656 							sem_prison_cleanup(tpr);
1657 					} else {
1658 						prison_unlock(tpr);
1659 						descend = 0;
1660 					}
1661 				}
1662 			}
1663 		}
1664 	}
1665 
1666 	return (0);
1667 }
1668 
1669 static int
1670 sem_prison_get(void *obj, void *data)
1671 {
1672 	struct prison *pr = obj;
1673 	struct prison *rpr;
1674 	struct vfsoptlist *opts = data;
1675 	int error, jsys;
1676 
1677 	/* Set sysvsem based on the jail's root prison. */
1678 	prison_lock(pr);
1679 	rpr = osd_jail_get(pr, sem_prison_slot);
1680 	prison_unlock(pr);
1681 	jsys = rpr == NULL ? JAIL_SYS_DISABLE
1682 	    : rpr == pr ? JAIL_SYS_NEW : JAIL_SYS_INHERIT;
1683 	error = vfs_setopt(opts, "sysvsem", &jsys, sizeof(jsys));
1684 	if (error == ENOENT)
1685 		error = 0;
1686 	return (error);
1687 }
1688 
1689 static int
1690 sem_prison_remove(void *obj, void *data __unused)
1691 {
1692 	struct prison *pr = obj;
1693 	struct prison *rpr;
1694 
1695 	prison_lock(pr);
1696 	rpr = osd_jail_get(pr, sem_prison_slot);
1697 	prison_unlock(pr);
1698 	if (rpr == pr)
1699 		sem_prison_cleanup(pr);
1700 	return (0);
1701 }
1702 
1703 static void
1704 sem_prison_cleanup(struct prison *pr)
1705 {
1706 	int i;
1707 
1708 	/* Remove any sems that belong to this jail. */
1709 	mtx_lock(&sem_mtx);
1710 	for (i = 0; i < seminfo.semmni; i++) {
1711 		if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
1712 		    sema[i].cred != NULL && sema[i].cred->cr_prison == pr) {
1713 			mtx_lock(&sema_mtx[i]);
1714 			sem_remove(i, NULL);
1715 			mtx_unlock(&sema_mtx[i]);
1716 		}
1717 	}
1718 	mtx_unlock(&sem_mtx);
1719 }
1720 
1721 SYSCTL_JAIL_PARAM_SYS_NODE(sysvsem, CTLFLAG_RW, "SYSV semaphores");
1722 
1723 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1724     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1725 
1726 /* XXX casting to (sy_call_t *) is bogus, as usual. */
1727 static sy_call_t *semcalls[] = {
1728 	(sy_call_t *)freebsd7___semctl, (sy_call_t *)sys_semget,
1729 	(sy_call_t *)sys_semop
1730 };
1731 
1732 /*
1733  * Entry point for all SEM calls.
1734  */
1735 int
1736 sys_semsys(td, uap)
1737 	struct thread *td;
1738 	/* XXX actually varargs. */
1739 	struct semsys_args /* {
1740 		int	which;
1741 		int	a2;
1742 		int	a3;
1743 		int	a4;
1744 		int	a5;
1745 	} */ *uap;
1746 {
1747 	int error;
1748 
1749 	AUDIT_ARG_SVIPC_WHICH(uap->which);
1750 	if (uap->which < 0 || uap->which >= nitems(semcalls))
1751 		return (EINVAL);
1752 	error = (*semcalls[uap->which])(td, &uap->a2);
1753 	return (error);
1754 }
1755 
1756 #ifndef _SYS_SYSPROTO_H_
1757 struct freebsd7___semctl_args {
1758 	int	semid;
1759 	int	semnum;
1760 	int	cmd;
1761 	union	semun_old *arg;
1762 };
1763 #endif
1764 int
1765 freebsd7___semctl(struct thread *td, struct freebsd7___semctl_args *uap)
1766 {
1767 	struct semid_ds_old dsold;
1768 	struct semid_ds dsbuf;
1769 	union semun_old arg;
1770 	union semun semun;
1771 	register_t rval;
1772 	int error;
1773 
1774 	switch (uap->cmd) {
1775 	case SEM_STAT:
1776 	case IPC_SET:
1777 	case IPC_STAT:
1778 	case GETALL:
1779 	case SETVAL:
1780 	case SETALL:
1781 		error = copyin(uap->arg, &arg, sizeof(arg));
1782 		if (error)
1783 			return (error);
1784 		break;
1785 	}
1786 
1787 	switch (uap->cmd) {
1788 	case SEM_STAT:
1789 	case IPC_STAT:
1790 		semun.buf = &dsbuf;
1791 		break;
1792 	case IPC_SET:
1793 		error = copyin(arg.buf, &dsold, sizeof(dsold));
1794 		if (error)
1795 			return (error);
1796 		ipcperm_old2new(&dsold.sem_perm, &dsbuf.sem_perm);
1797 		CP(dsold, dsbuf, __sem_base);
1798 		CP(dsold, dsbuf, sem_nsems);
1799 		CP(dsold, dsbuf, sem_otime);
1800 		CP(dsold, dsbuf, sem_ctime);
1801 		semun.buf = &dsbuf;
1802 		break;
1803 	case GETALL:
1804 	case SETALL:
1805 		semun.array = arg.array;
1806 		break;
1807 	case SETVAL:
1808 		semun.val = arg.val;
1809 		break;
1810 	}
1811 
1812 	error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun,
1813 	    &rval);
1814 	if (error)
1815 		return (error);
1816 
1817 	switch (uap->cmd) {
1818 	case SEM_STAT:
1819 	case IPC_STAT:
1820 		bzero(&dsold, sizeof(dsold));
1821 		ipcperm_new2old(&dsbuf.sem_perm, &dsold.sem_perm);
1822 		CP(dsbuf, dsold, __sem_base);
1823 		CP(dsbuf, dsold, sem_nsems);
1824 		CP(dsbuf, dsold, sem_otime);
1825 		CP(dsbuf, dsold, sem_ctime);
1826 		error = copyout(&dsold, arg.buf, sizeof(dsold));
1827 		break;
1828 	}
1829 
1830 	if (error == 0)
1831 		td->td_retval[0] = rval;
1832 	return (error);
1833 }
1834 
1835 #endif /* COMPAT_FREEBSD{4,5,6,7} */
1836 
1837 #ifdef COMPAT_FREEBSD32
1838 
1839 int
1840 freebsd32_semsys(struct thread *td, struct freebsd32_semsys_args *uap)
1841 {
1842 
1843 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1844     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1845 	AUDIT_ARG_SVIPC_WHICH(uap->which);
1846 	switch (uap->which) {
1847 	case 0:
1848 		return (freebsd7_freebsd32_semctl(td,
1849 		    (struct freebsd7_freebsd32_semctl_args *)&uap->a2));
1850 	default:
1851 		return (sys_semsys(td, (struct semsys_args *)uap));
1852 	}
1853 #else
1854 	return (nosys(td, NULL));
1855 #endif
1856 }
1857 
1858 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
1859     defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7)
1860 int
1861 freebsd7_freebsd32_semctl(struct thread *td,
1862     struct freebsd7_freebsd32_semctl_args *uap)
1863 {
1864 	struct semid_ds32_old dsbuf32;
1865 	struct semid_ds dsbuf;
1866 	union semun semun;
1867 	union semun32 arg;
1868 	register_t rval;
1869 	int error;
1870 
1871 	switch (uap->cmd) {
1872 	case SEM_STAT:
1873 	case IPC_SET:
1874 	case IPC_STAT:
1875 	case GETALL:
1876 	case SETVAL:
1877 	case SETALL:
1878 		error = copyin(uap->arg, &arg, sizeof(arg));
1879 		if (error)
1880 			return (error);
1881 		break;
1882 	}
1883 
1884 	switch (uap->cmd) {
1885 	case SEM_STAT:
1886 	case IPC_STAT:
1887 		semun.buf = &dsbuf;
1888 		break;
1889 	case IPC_SET:
1890 		error = copyin(PTRIN(arg.buf), &dsbuf32, sizeof(dsbuf32));
1891 		if (error)
1892 			return (error);
1893 		freebsd32_ipcperm_old_in(&dsbuf32.sem_perm, &dsbuf.sem_perm);
1894 		PTRIN_CP(dsbuf32, dsbuf, __sem_base);
1895 		CP(dsbuf32, dsbuf, sem_nsems);
1896 		CP(dsbuf32, dsbuf, sem_otime);
1897 		CP(dsbuf32, dsbuf, sem_ctime);
1898 		semun.buf = &dsbuf;
1899 		break;
1900 	case GETALL:
1901 	case SETALL:
1902 		semun.array = PTRIN(arg.array);
1903 		break;
1904 	case SETVAL:
1905 		semun.val = arg.val;
1906 		break;
1907 	}
1908 
1909 	error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun,
1910 	    &rval);
1911 	if (error)
1912 		return (error);
1913 
1914 	switch (uap->cmd) {
1915 	case SEM_STAT:
1916 	case IPC_STAT:
1917 		bzero(&dsbuf32, sizeof(dsbuf32));
1918 		freebsd32_ipcperm_old_out(&dsbuf.sem_perm, &dsbuf32.sem_perm);
1919 		PTROUT_CP(dsbuf, dsbuf32, __sem_base);
1920 		CP(dsbuf, dsbuf32, sem_nsems);
1921 		CP(dsbuf, dsbuf32, sem_otime);
1922 		CP(dsbuf, dsbuf32, sem_ctime);
1923 		error = copyout(&dsbuf32, PTRIN(arg.buf), sizeof(dsbuf32));
1924 		break;
1925 	}
1926 
1927 	if (error == 0)
1928 		td->td_retval[0] = rval;
1929 	return (error);
1930 }
1931 #endif
1932 
1933 int
1934 freebsd32_semctl(struct thread *td, struct freebsd32_semctl_args *uap)
1935 {
1936 	struct semid_ds32 dsbuf32;
1937 	struct semid_ds dsbuf;
1938 	union semun semun;
1939 	union semun32 arg;
1940 	register_t rval;
1941 	int error;
1942 
1943 	switch (uap->cmd) {
1944 	case SEM_STAT:
1945 	case IPC_SET:
1946 	case IPC_STAT:
1947 	case GETALL:
1948 	case SETVAL:
1949 	case SETALL:
1950 		error = copyin(uap->arg, &arg, sizeof(arg));
1951 		if (error)
1952 			return (error);
1953 		break;
1954 	}
1955 
1956 	switch (uap->cmd) {
1957 	case SEM_STAT:
1958 	case IPC_STAT:
1959 		semun.buf = &dsbuf;
1960 		break;
1961 	case IPC_SET:
1962 		error = copyin(PTRIN(arg.buf), &dsbuf32, sizeof(dsbuf32));
1963 		if (error)
1964 			return (error);
1965 		freebsd32_ipcperm_in(&dsbuf32.sem_perm, &dsbuf.sem_perm);
1966 		PTRIN_CP(dsbuf32, dsbuf, __sem_base);
1967 		CP(dsbuf32, dsbuf, sem_nsems);
1968 		CP(dsbuf32, dsbuf, sem_otime);
1969 		CP(dsbuf32, dsbuf, sem_ctime);
1970 		semun.buf = &dsbuf;
1971 		break;
1972 	case GETALL:
1973 	case SETALL:
1974 		semun.array = PTRIN(arg.array);
1975 		break;
1976 	case SETVAL:
1977 		semun.val = arg.val;
1978 		break;
1979 	}
1980 
1981 	error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun,
1982 	    &rval);
1983 	if (error)
1984 		return (error);
1985 
1986 	switch (uap->cmd) {
1987 	case SEM_STAT:
1988 	case IPC_STAT:
1989 		bzero(&dsbuf32, sizeof(dsbuf32));
1990 		freebsd32_ipcperm_out(&dsbuf.sem_perm, &dsbuf32.sem_perm);
1991 		PTROUT_CP(dsbuf, dsbuf32, __sem_base);
1992 		CP(dsbuf, dsbuf32, sem_nsems);
1993 		CP(dsbuf, dsbuf32, sem_otime);
1994 		CP(dsbuf, dsbuf32, sem_ctime);
1995 		error = copyout(&dsbuf32, PTRIN(arg.buf), sizeof(dsbuf32));
1996 		break;
1997 	}
1998 
1999 	if (error == 0)
2000 		td->td_retval[0] = rval;
2001 	return (error);
2002 }
2003 
2004 #endif /* COMPAT_FREEBSD32 */
2005