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