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