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