1/*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * Implementation of SVID semaphores
30 *
31 * Author:  Daniel Boulet
32 *
33 * This software is provided ``AS IS'' without any warranties of any kind.
34 */
35/*
36 * John Bellardo modified the implementation for Darwin. 12/2000
37 */
38/*
39 * NOTICE: This file was modified by McAfee Research in 2004 to introduce
40 * support for mandatory and extensible security protections.  This notice
41 * is included in support of clause 2.2 (b) of the Apple Public License,
42 * Version 2.0.
43 * Copyright (c) 2005-2006 SPARTA, Inc.
44 */
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/kernel.h>
49#include <sys/proc_internal.h>
50#include <sys/kauth.h>
51#include <sys/sem_internal.h>
52#include <sys/malloc.h>
53#include <mach/mach_types.h>
54
55#include <sys/filedesc.h>
56#include <sys/file_internal.h>
57#include <sys/sysctl.h>
58#include <sys/ipcs.h>
59#include <sys/sysent.h>
60#include <sys/sysproto.h>
61#if CONFIG_MACF
62#include <security/mac_framework.h>
63#endif
64
65#include <security/audit/audit.h>
66
67#if SYSV_SEM
68
69
70/* Uncomment this line to see the debugging output */
71/* #define SEM_DEBUG */
72
73/* Uncomment this line to see MAC debugging output. */
74/* #define	MAC_DEBUG */
75#if CONFIG_MACF_DEBUG
76#define	MPRINTF(a)	printf(a)
77#else
78#define	MPRINTF(a)
79#endif
80
81#define M_SYSVSEM	M_TEMP
82
83
84/* Hard system limits to avoid resource starvation / DOS attacks.
85 * These are not needed if we can make the semaphore pages swappable.
86 */
87static struct seminfo limitseminfo = {
88	SEMMAP,        /* # of entries in semaphore map */
89	SEMMNI,        /* # of semaphore identifiers */
90	SEMMNS,        /* # of semaphores in system */
91	SEMMNU,        /* # of undo structures in system */
92	SEMMSL,        /* max # of semaphores per id */
93	SEMOPM,        /* max # of operations per semop call */
94	SEMUME,        /* max # of undo entries per process */
95	SEMUSZ,        /* size in bytes of undo structure */
96	SEMVMX,        /* semaphore maximum value */
97	SEMAEM         /* adjust on exit max value */
98};
99
100/* Current system allocations.  We use this structure to track how many
101 * resources we have allocated so far.  This way we can set large hard limits
102 * and not allocate the memory for them up front.
103 */
104struct seminfo seminfo = {
105	SEMMAP,	/* Unused, # of entries in semaphore map */
106	0,	/* # of semaphore identifiers */
107	0,	/* # of semaphores in system */
108	0,	/* # of undo entries in system */
109	SEMMSL,	/* max # of semaphores per id */
110	SEMOPM,	/* max # of operations per semop call */
111	SEMUME,	/* max # of undo entries per process */
112	SEMUSZ,	/* size in bytes of undo structure */
113	SEMVMX,	/* semaphore maximum value */
114	SEMAEM	/* adjust on exit max value */
115};
116
117
118static int semu_alloc(struct proc *p);
119static int semundo_adjust(struct proc *p, int *supidx,
120		int semid, int semnum, int adjval);
121static void semundo_clear(int semid, int semnum);
122
123/* XXX casting to (sy_call_t *) is bogus, as usual. */
124static sy_call_t *semcalls[] = {
125	(sy_call_t *)semctl, (sy_call_t *)semget,
126	(sy_call_t *)semop
127};
128
129static int		semtot = 0;		/* # of used semaphores */
130struct semid_kernel	*sema = NULL;		/* semaphore id pool */
131struct sem		*sem_pool =  NULL;	/* semaphore pool */
132static int	 	semu_list_idx = -1;	/* active undo structures */
133struct sem_undo		*semu = NULL;		/* semaphore undo pool */
134
135
136void sysv_sem_lock_init(void);
137static lck_grp_t       *sysv_sem_subsys_lck_grp;
138static lck_grp_attr_t  *sysv_sem_subsys_lck_grp_attr;
139static lck_attr_t      *sysv_sem_subsys_lck_attr;
140static lck_mtx_t        sysv_sem_subsys_mutex;
141
142#define SYSV_SEM_SUBSYS_LOCK() lck_mtx_lock(&sysv_sem_subsys_mutex)
143#define SYSV_SEM_SUBSYS_UNLOCK() lck_mtx_unlock(&sysv_sem_subsys_mutex)
144
145
146__private_extern__ void
147sysv_sem_lock_init( void )
148{
149
150    sysv_sem_subsys_lck_grp_attr = lck_grp_attr_alloc_init();
151
152    sysv_sem_subsys_lck_grp = lck_grp_alloc_init("sysv_sem_subsys_lock", sysv_sem_subsys_lck_grp_attr);
153
154    sysv_sem_subsys_lck_attr = lck_attr_alloc_init();
155    lck_mtx_init(&sysv_sem_subsys_mutex, sysv_sem_subsys_lck_grp, sysv_sem_subsys_lck_attr);
156}
157
158static __inline__ user_time_t
159sysv_semtime(void)
160{
161	struct timeval	tv;
162	microtime(&tv);
163	return (tv.tv_sec);
164}
165
166/*
167 * XXX conversion of internal user_time_t to external tume_t loses
168 * XXX precision; not an issue for us now, since we are only ever
169 * XXX setting 32 bits worth of time into it.
170 *
171 * pad field contents are not moved correspondingly; contents will be lost
172 *
173 * NOTE: Source and target may *NOT* overlap! (target is smaller)
174 */
175static void
176semid_ds_kernelto32(struct user_semid_ds *in, struct user32_semid_ds *out)
177{
178	out->sem_perm = in->sem_perm;
179	out->sem_base = CAST_DOWN_EXPLICIT(__int32_t,in->sem_base);
180	out->sem_nsems = in->sem_nsems;
181	out->sem_otime = in->sem_otime;		/* XXX loses precision */
182	out->sem_ctime = in->sem_ctime;		/* XXX loses precision */
183}
184
185static void
186semid_ds_kernelto64(struct user_semid_ds *in, struct user64_semid_ds *out)
187{
188	out->sem_perm = in->sem_perm;
189	out->sem_base = CAST_DOWN_EXPLICIT(__int32_t,in->sem_base);
190	out->sem_nsems = in->sem_nsems;
191	out->sem_otime = in->sem_otime;		/* XXX loses precision */
192	out->sem_ctime = in->sem_ctime;		/* XXX loses precision */
193}
194
195/*
196 * pad field contents are not moved correspondingly; contents will be lost
197 *
198 * NOTE: Source and target may are permitted to overlap! (source is smaller);
199 * this works because we copy fields in order from the end of the struct to
200 * the beginning.
201 *
202 * XXX use CAST_USER_ADDR_T() for lack of a CAST_USER_TIME_T(); net effect
203 * XXX is the same.
204 */
205static void
206semid_ds_32tokernel(struct user32_semid_ds *in, struct user_semid_ds *out)
207{
208	out->sem_ctime = in->sem_ctime;
209	out->sem_otime = in->sem_otime;
210	out->sem_nsems = in->sem_nsems;
211	out->sem_base = (void *)(uintptr_t)in->sem_base;
212	out->sem_perm = in->sem_perm;
213}
214
215static void
216semid_ds_64tokernel(struct user64_semid_ds *in, struct user_semid_ds *out)
217{
218	out->sem_ctime = in->sem_ctime;
219	out->sem_otime = in->sem_otime;
220	out->sem_nsems = in->sem_nsems;
221	out->sem_base = (void *)(uintptr_t)in->sem_base;
222	out->sem_perm = in->sem_perm;
223}
224
225
226/*
227 * semsys
228 *
229 * Entry point for all SEM calls: semctl, semget, semop
230 *
231 * Parameters:	p	Process requesting the call
232 * 		uap	User argument descriptor (see below)
233 * 		retval	Return value of the selected sem call
234 *
235 * Indirect parameters:	uap->which	sem call to invoke (index in array of sem calls)
236 * 			uap->a2		User argument descriptor
237 *
238 * Returns:	0	Success
239 *		!0	Not success
240 *
241 * Implicit returns: retval	Return value of the selected sem call
242 *
243 * DEPRECATED:  This interface should not be used to call the other SEM
244 * 		functions (semctl, semget, semop). The correct usage is
245 * 		to call the other SEM functions directly.
246 *
247 */
248int
249semsys(struct proc *p, struct semsys_args *uap, int32_t *retval)
250{
251
252	/* The individual calls handling the locking now */
253
254	if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
255		return (EINVAL);
256	return ((*semcalls[uap->which])(p, &uap->a2, retval));
257}
258
259/*
260 * Expand the semu array to the given capacity.  If the expansion fails
261 * return 0, otherwise return 1.
262 *
263 * Assumes we already have the subsystem lock.
264 */
265static int
266grow_semu_array(int newSize)
267{
268	register int i;
269	register struct sem_undo *newSemu;
270
271	if (newSize <= seminfo.semmnu)
272		return 1;
273	if (newSize > limitseminfo.semmnu) /* enforce hard limit */
274	{
275#ifdef SEM_DEBUG
276		printf("undo structure hard limit of %d reached, requested %d\n",
277			limitseminfo.semmnu, newSize);
278#endif
279		return 0;
280	}
281	newSize = (newSize/SEMMNU_INC + 1) * SEMMNU_INC;
282	newSize = newSize > limitseminfo.semmnu ? limitseminfo.semmnu : newSize;
283
284#ifdef SEM_DEBUG
285	printf("growing semu[] from %d to %d\n", seminfo.semmnu, newSize);
286#endif
287	MALLOC(newSemu, struct sem_undo *, sizeof (struct sem_undo) * newSize,
288	       M_SYSVSEM, M_WAITOK | M_ZERO);
289	if (NULL == newSemu)
290	{
291#ifdef SEM_DEBUG
292		printf("allocation failed.  no changes made.\n");
293#endif
294		return 0;
295	}
296
297       	/* copy the old data to the new array */
298	for (i = 0; i < seminfo.semmnu; i++)
299	{
300		newSemu[i] = semu[i];
301	}
302	/*
303	 * The new elements (from newSemu[i] to newSemu[newSize-1]) have their
304	 * "un_proc" set to 0 (i.e. NULL) by the M_ZERO flag to MALLOC() above,
305	 * so they're already marked as "not in use".
306	 */
307
308	/* Clean up the old array */
309	if (semu)
310		FREE(semu, M_SYSVSEM);
311
312	semu = newSemu;
313	seminfo.semmnu = newSize;
314#ifdef SEM_DEBUG
315	printf("expansion successful\n");
316#endif
317	return 1;
318}
319
320/*
321 * Expand the sema array to the given capacity.  If the expansion fails
322 * we return 0, otherwise we return 1.
323 *
324 * Assumes we already have the subsystem lock.
325 */
326static int
327grow_sema_array(int newSize)
328{
329	register struct semid_kernel *newSema;
330	register int i;
331
332	if (newSize <= seminfo.semmni)
333		return 0;
334	if (newSize > limitseminfo.semmni) /* enforce hard limit */
335	{
336#ifdef SEM_DEBUG
337		printf("identifier hard limit of %d reached, requested %d\n",
338			limitseminfo.semmni, newSize);
339#endif
340		return 0;
341	}
342	newSize = (newSize/SEMMNI_INC + 1) * SEMMNI_INC;
343	newSize = newSize > limitseminfo.semmni ? limitseminfo.semmni : newSize;
344
345#ifdef SEM_DEBUG
346	printf("growing sema[] from %d to %d\n", seminfo.semmni, newSize);
347#endif
348	MALLOC(newSema, struct semid_kernel *,
349	       sizeof (struct semid_kernel) * newSize,
350	       M_SYSVSEM, M_WAITOK | M_ZERO);
351	if (NULL == newSema)
352	{
353#ifdef SEM_DEBUG
354		printf("allocation failed.  no changes made.\n");
355#endif
356		return 0;
357	}
358
359	/* copy over the old ids */
360	for (i = 0; i < seminfo.semmni; i++)
361	{
362		newSema[i] = sema[i];
363		/* This is a hack.  What we really want to be able to
364		 * do is change the value a process is waiting on
365		 * without waking it up, but I don't know how to do
366		 * this with the existing code, so we wake up the
367		 * process and let it do a lot of work to determine the
368		 * semaphore set is really not available yet, and then
369		 * sleep on the correct, reallocated semid_kernel pointer.
370		 */
371		if (sema[i].u.sem_perm.mode & SEM_ALLOC)
372			wakeup((caddr_t)&sema[i]);
373	}
374
375#if CONFIG_MACF
376	for (i = seminfo.semmni; i < newSize; i++)
377	{
378		mac_sysvsem_label_init(&newSema[i]);
379	}
380#endif
381
382	/*
383	 * The new elements (from newSema[i] to newSema[newSize-1]) have their
384	 * "sem_base" and "sem_perm.mode" set to 0 (i.e. NULL) by the M_ZERO
385	 * flag to MALLOC() above, so they're already marked as "not in use".
386	 */
387
388	/* Clean up the old array */
389	if (sema)
390		FREE(sema, M_SYSVSEM);
391
392	sema = newSema;
393	seminfo.semmni = newSize;
394#ifdef SEM_DEBUG
395	printf("expansion successful\n");
396#endif
397	return 1;
398}
399
400/*
401 * Expand the sem_pool array to the given capacity.  If the expansion fails
402 * we return 0 (fail), otherwise we return 1 (success).
403 *
404 * Assumes we already hold the subsystem lock.
405 */
406static int
407grow_sem_pool(int new_pool_size)
408{
409	struct sem *new_sem_pool = NULL;
410	struct sem *sem_free;
411	int i;
412
413	if (new_pool_size < semtot)
414		return 0;
415	/* enforce hard limit */
416	if (new_pool_size > limitseminfo.semmns) {
417#ifdef SEM_DEBUG
418		printf("semaphore hard limit of %d reached, requested %d\n",
419			limitseminfo.semmns, new_pool_size);
420#endif
421		return 0;
422	}
423
424	new_pool_size = (new_pool_size/SEMMNS_INC + 1) * SEMMNS_INC;
425	new_pool_size = new_pool_size > limitseminfo.semmns ? limitseminfo.semmns : new_pool_size;
426
427#ifdef SEM_DEBUG
428	printf("growing sem_pool array from %d to %d\n", seminfo.semmns, new_pool_size);
429#endif
430	MALLOC(new_sem_pool, struct sem *, sizeof (struct sem) * new_pool_size,
431	       M_SYSVSEM, M_WAITOK | M_ZERO);
432	if (NULL == new_sem_pool) {
433#ifdef SEM_DEBUG
434		printf("allocation failed.  no changes made.\n");
435#endif
436		return 0;
437	}
438
439	/* We have our new memory, now copy the old contents over */
440	if (sem_pool)
441		for(i = 0; i < seminfo.semmns; i++)
442			new_sem_pool[i] = sem_pool[i];
443
444	/* Update our id structures to point to the new semaphores */
445	for(i = 0; i < seminfo.semmni; i++) {
446		if (sema[i].u.sem_perm.mode & SEM_ALLOC)  /* ID in use */
447			sema[i].u.sem_base += (new_sem_pool - sem_pool);
448	}
449
450	sem_free = sem_pool;
451	sem_pool = new_sem_pool;
452
453	/* clean up the old array */
454	if (sem_free != NULL)
455		FREE(sem_free, M_SYSVSEM);
456
457	seminfo.semmns = new_pool_size;
458#ifdef SEM_DEBUG
459	printf("expansion complete\n");
460#endif
461	return 1;
462}
463
464/*
465 * Allocate a new sem_undo structure for a process
466 * (returns ptr to structure or NULL if no more room)
467 *
468 * Assumes we already hold the subsystem lock.
469 */
470
471static int
472semu_alloc(struct proc *p)
473{
474	register int i;
475	register struct sem_undo *suptr;
476	int *supidx;
477	int attempt;
478
479	/*
480	 * Try twice to allocate something.
481	 * (we'll purge any empty structures after the first pass so
482	 * two passes are always enough)
483	 */
484
485	for (attempt = 0; attempt < 2; attempt++) {
486		/*
487		 * Look for a free structure.
488		 * Fill it in and return it if we find one.
489		 */
490
491		for (i = 0; i < seminfo.semmnu; i++) {
492			suptr = SEMU(i);
493			if (suptr->un_proc == NULL) {
494				suptr->un_next_idx = semu_list_idx;
495				semu_list_idx = i;
496				suptr->un_cnt = 0;
497				suptr->un_ent = NULL;
498				suptr->un_proc = p;
499				return i;
500			}
501		}
502
503		/*
504		 * We didn't find a free one, if this is the first attempt
505		 * then try to free some structures.
506		 */
507
508		if (attempt == 0) {
509			/* All the structures are in use - try to free some */
510			int did_something = 0;
511
512			supidx = &semu_list_idx;
513			while (*supidx != -1) {
514				suptr = SEMU(*supidx);
515				if (suptr->un_cnt == 0)  {
516					suptr->un_proc = NULL;
517					*supidx = suptr->un_next_idx;
518					did_something = 1;
519				} else
520					supidx = &(suptr->un_next_idx);
521			}
522
523			/* If we didn't free anything. Try expanding
524			 * the semu[] array.  If that doesn't work
525			 * then fail.  We expand last to get the
526			 * most reuse out of existing resources.
527			 */
528			if (!did_something)
529				if (!grow_semu_array(seminfo.semmnu + 1))
530					return -1;
531		} else {
532			/*
533			 * The second pass failed even though we freed
534			 * something after the first pass!
535			 * This is IMPOSSIBLE!
536			 */
537			panic("semu_alloc - second attempt failed");
538		}
539	}
540	return -1;
541}
542
543/*
544 * Adjust a particular entry for a particular proc
545 *
546 * Assumes we already hold the subsystem lock.
547 */
548static int
549semundo_adjust(struct proc *p, int *supidx, int semid,
550	int semnum, int adjval)
551{
552	register struct sem_undo *suptr;
553	int suidx;
554	register struct undo *sueptr, **suepptr, *new_sueptr;
555	int i;
556
557	/*
558	 * Look for and remember the sem_undo if the caller doesn't provide it
559	 */
560
561	suidx = *supidx;
562	if (suidx == -1) {
563		for (suidx = semu_list_idx; suidx != -1;
564		    suidx = suptr->un_next_idx) {
565			suptr = SEMU(suidx);
566			if (suptr->un_proc == p) {
567				*supidx = suidx;
568				break;
569			}
570		}
571		if (suidx == -1) {
572			if (adjval == 0)
573				return(0);
574			suidx = semu_alloc(p);
575			if (suidx == -1)
576				return(ENOSPC);
577			*supidx = suidx;
578		}
579	}
580
581	/*
582	 * Look for the requested entry and adjust it (delete if adjval becomes
583	 * 0).
584	 */
585	suptr = SEMU(suidx);
586	new_sueptr = NULL;
587	for (i = 0, suepptr = &suptr->un_ent, sueptr = suptr->un_ent;
588	     i < suptr->un_cnt;
589	     i++, suepptr = &sueptr->une_next, sueptr = sueptr->une_next) {
590		if (sueptr->une_id != semid || sueptr->une_num != semnum)
591			continue;
592		if (adjval == 0)
593			sueptr->une_adjval = 0;
594		else
595			sueptr->une_adjval += adjval;
596		if (sueptr->une_adjval == 0) {
597			suptr->un_cnt--;
598			*suepptr = sueptr->une_next;
599			FREE(sueptr, M_SYSVSEM);
600			sueptr = NULL;
601		}
602		return 0;
603	}
604
605	/* Didn't find the right entry - create it */
606	if (adjval == 0) {
607		/* no adjustment: no need for a new entry */
608		return 0;
609	}
610
611	if (suptr->un_cnt == limitseminfo.semume) {
612		/* reached the limit number of semaphore undo entries */
613		return EINVAL;
614	}
615
616	/* allocate a new semaphore undo entry */
617	MALLOC(new_sueptr, struct undo *, sizeof (struct undo),
618	       M_SYSVSEM, M_WAITOK);
619	if (new_sueptr == NULL) {
620		return ENOMEM;
621	}
622
623	/* fill in the new semaphore undo entry */
624	new_sueptr->une_next = suptr->un_ent;
625	suptr->un_ent = new_sueptr;
626	suptr->un_cnt++;
627	new_sueptr->une_adjval = adjval;
628	new_sueptr->une_id = semid;
629	new_sueptr->une_num = semnum;
630
631	return 0;
632}
633
634/* Assumes we already hold the subsystem lock.
635 */
636static void
637semundo_clear(int semid, int semnum)
638{
639	struct sem_undo *suptr;
640	int suidx;
641
642	for (suidx = semu_list_idx; suidx != -1; suidx = suptr->un_next_idx) {
643		struct undo *sueptr;
644		struct undo **suepptr;
645		int i = 0;
646
647		suptr = SEMU(suidx);
648		sueptr = suptr->un_ent;
649		suepptr = &suptr->un_ent;
650		while (i < suptr->un_cnt) {
651			if (sueptr->une_id == semid) {
652				if (semnum == -1 || sueptr->une_num == semnum) {
653					suptr->un_cnt--;
654					*suepptr = sueptr->une_next;
655					FREE(sueptr, M_SYSVSEM);
656					sueptr = *suepptr;
657					continue;
658				}
659				if (semnum != -1)
660					break;
661			}
662			i++;
663			suepptr = &sueptr->une_next;
664			sueptr = sueptr->une_next;
665		}
666	}
667}
668
669/*
670 * Note that the user-mode half of this passes a union coerced to a
671 * user_addr_t.  The union contains either an int or a pointer, and
672 * so we have to coerce it back, variant on whether the calling
673 * process is 64 bit or not.  The coercion works for the 'val' element
674 * because the alignment is the same in user and kernel space.
675 */
676int
677semctl(struct proc *p, struct semctl_args *uap, int32_t *retval)
678{
679	int semid = uap->semid;
680	int semnum = uap->semnum;
681	int cmd = uap->cmd;
682	user_semun_t user_arg = (user_semun_t)uap->arg;
683	kauth_cred_t cred = kauth_cred_get();
684	int i, rval, eval;
685	struct user_semid_ds sbuf;
686	struct semid_kernel *semakptr;
687
688
689	AUDIT_ARG(svipc_cmd, cmd);
690	AUDIT_ARG(svipc_id, semid);
691
692	SYSV_SEM_SUBSYS_LOCK();
693
694#ifdef SEM_DEBUG
695	printf("call to semctl(%d, %d, %d, 0x%qx)\n", semid, semnum, cmd, user_arg);
696#endif
697
698	semid = IPCID_TO_IX(semid);
699
700	if (semid < 0 || semid >= seminfo.semmni) {
701#ifdef SEM_DEBUG
702		printf("Invalid semid\n");
703#endif
704		eval = EINVAL;
705		goto semctlout;
706	}
707
708	semakptr = &sema[semid];
709	if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
710	    semakptr->u.sem_perm._seq != IPCID_TO_SEQ(uap->semid)) {
711		eval = EINVAL;
712		goto semctlout;
713	}
714#if CONFIG_MACF
715	eval = mac_sysvsem_check_semctl(cred, semakptr, cmd);
716	if (eval)
717		goto semctlout;
718#endif
719
720	eval = 0;
721	rval = 0;
722
723	switch (cmd) {
724	case IPC_RMID:
725		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_M)))
726			goto semctlout;
727
728		semakptr->u.sem_perm.cuid = kauth_cred_getuid(cred);
729		semakptr->u.sem_perm.uid = kauth_cred_getuid(cred);
730		semtot -= semakptr->u.sem_nsems;
731		for (i = semakptr->u.sem_base - sem_pool; i < semtot; i++)
732			sem_pool[i] = sem_pool[i + semakptr->u.sem_nsems];
733		for (i = 0; i < seminfo.semmni; i++) {
734			if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
735			    sema[i].u.sem_base > semakptr->u.sem_base)
736				sema[i].u.sem_base -= semakptr->u.sem_nsems;
737		}
738		semakptr->u.sem_perm.mode = 0;
739#if CONFIG_MACF
740		mac_sysvsem_label_recycle(semakptr);
741#endif
742		semundo_clear(semid, -1);
743		wakeup((caddr_t)semakptr);
744		break;
745
746	case IPC_SET:
747		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_M)))
748				goto semctlout;
749
750		if (IS_64BIT_PROCESS(p)) {
751			struct user64_semid_ds ds64;
752			eval = copyin(user_arg.buf, &ds64, sizeof(ds64));
753			semid_ds_64tokernel(&ds64, &sbuf);
754		} else {
755			struct user32_semid_ds ds32;
756			eval = copyin(user_arg.buf, &ds32, sizeof(ds32));
757			semid_ds_32tokernel(&ds32, &sbuf);
758		}
759
760		if (eval != 0) {
761			goto semctlout;
762		}
763
764		semakptr->u.sem_perm.uid = sbuf.sem_perm.uid;
765		semakptr->u.sem_perm.gid = sbuf.sem_perm.gid;
766		semakptr->u.sem_perm.mode = (semakptr->u.sem_perm.mode &
767		    ~0777) | (sbuf.sem_perm.mode & 0777);
768		semakptr->u.sem_ctime = sysv_semtime();
769		break;
770
771	case IPC_STAT:
772		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R)))
773				goto semctlout;
774
775		if (IS_64BIT_PROCESS(p)) {
776			struct user64_semid_ds semid_ds64;
777			semid_ds_kernelto64(&semakptr->u, &semid_ds64);
778			eval = copyout(&semid_ds64, user_arg.buf, sizeof(semid_ds64));
779		} else {
780			struct user32_semid_ds semid_ds32;
781			semid_ds_kernelto32(&semakptr->u, &semid_ds32);
782			eval = copyout(&semid_ds32, user_arg.buf, sizeof(semid_ds32));
783		}
784		break;
785
786	case GETNCNT:
787		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R)))
788				goto semctlout;
789		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
790			eval = EINVAL;
791			goto semctlout;
792		}
793		rval = semakptr->u.sem_base[semnum].semncnt;
794		break;
795
796	case GETPID:
797		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R)))
798				goto semctlout;
799		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
800			eval = EINVAL;
801			goto semctlout;
802		}
803		rval = semakptr->u.sem_base[semnum].sempid;
804		break;
805
806	case GETVAL:
807		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R)))
808				goto semctlout;
809		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
810			eval = EINVAL;
811			goto semctlout;
812		}
813		rval = semakptr->u.sem_base[semnum].semval;
814		break;
815
816	case GETALL:
817		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R)))
818				goto semctlout;
819/* XXXXXXXXXXXXXXXX TBD XXXXXXXXXXXXXXXX */
820		for (i = 0; i < semakptr->u.sem_nsems; i++) {
821			/* XXX could be done in one go... */
822			eval = copyout((caddr_t)&semakptr->u.sem_base[i].semval,
823			    user_arg.array + (i * sizeof(unsigned short)),
824			    sizeof(unsigned short));
825			if (eval != 0)
826				break;
827		}
828		break;
829
830	case GETZCNT:
831		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_R)))
832				goto semctlout;
833		if (semnum < 0 || semnum >= semakptr->u.sem_nsems) {
834			eval = EINVAL;
835			goto semctlout;
836		}
837		rval = semakptr->u.sem_base[semnum].semzcnt;
838		break;
839
840	case SETVAL:
841		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_W)))
842                {
843#ifdef SEM_DEBUG
844			printf("Invalid credentials for write\n");
845#endif
846				goto semctlout;
847		}
848		if (semnum < 0 || semnum >= semakptr->u.sem_nsems)
849		{
850#ifdef SEM_DEBUG
851			printf("Invalid number out of range for set\n");
852#endif
853			eval = EINVAL;
854			goto semctlout;
855		}
856		/*
857		 * Cast down a pointer instead of using 'val' member directly
858		 * to avoid introducing endieness and a pad field into the
859		 * header file.  Ugly, but it works.
860		 */
861		semakptr->u.sem_base[semnum].semval = CAST_DOWN_EXPLICIT(int,user_arg.buf);
862		semakptr->u.sem_base[semnum].sempid = p->p_pid;
863		/* XXX scottl Should there be a MAC call here? */
864		semundo_clear(semid, semnum);
865		wakeup((caddr_t)semakptr);
866		break;
867
868	case SETALL:
869		if ((eval = ipcperm(cred, &semakptr->u.sem_perm, IPC_W)))
870				goto semctlout;
871/*** XXXXXXXXXXXX TBD ********/
872		for (i = 0; i < semakptr->u.sem_nsems; i++) {
873			/* XXX could be done in one go... */
874			eval = copyin(user_arg.array + (i * sizeof(unsigned short)),
875			    (caddr_t)&semakptr->u.sem_base[i].semval,
876			    sizeof(unsigned short));
877			if (eval != 0)
878				break;
879			semakptr->u.sem_base[i].sempid = p->p_pid;
880		}
881		/* XXX scottl Should there be a MAC call here? */
882		semundo_clear(semid, -1);
883		wakeup((caddr_t)semakptr);
884		break;
885
886	default:
887			eval = EINVAL;
888			goto semctlout;
889	}
890
891	if (eval == 0)
892		*retval = rval;
893semctlout:
894	SYSV_SEM_SUBSYS_UNLOCK();
895	return(eval);
896}
897
898int
899semget(__unused struct proc *p, struct semget_args *uap, int32_t *retval)
900{
901	int semid, eval;
902	int key = uap->key;
903	int nsems = uap->nsems;
904	int semflg = uap->semflg;
905	kauth_cred_t cred = kauth_cred_get();
906
907#ifdef SEM_DEBUG
908	if (key != IPC_PRIVATE)
909		printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
910	else
911		printf("semget(IPC_PRIVATE, %d, 0%o)\n", nsems, semflg);
912#endif
913
914
915	SYSV_SEM_SUBSYS_LOCK();
916
917
918	if (key != IPC_PRIVATE) {
919		for (semid = 0; semid < seminfo.semmni; semid++) {
920			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) &&
921			    sema[semid].u.sem_perm._key == key)
922				break;
923		}
924		if (semid < seminfo.semmni) {
925#ifdef SEM_DEBUG
926			printf("found public key\n");
927#endif
928			if ((eval = ipcperm(cred, &sema[semid].u.sem_perm,
929			    semflg & 0700)))
930				goto semgetout;
931			if (nsems < 0 || sema[semid].u.sem_nsems < nsems) {
932#ifdef SEM_DEBUG
933				printf("too small\n");
934#endif
935				eval = EINVAL;
936				goto semgetout;
937			}
938			if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
939#ifdef SEM_DEBUG
940				printf("not exclusive\n");
941#endif
942				eval = EEXIST;
943				goto semgetout;
944			}
945#if CONFIG_MACF
946			eval = mac_sysvsem_check_semget(cred, &sema[semid]);
947			if (eval)
948				goto semgetout;
949#endif
950			goto found;
951		}
952	}
953
954#ifdef SEM_DEBUG
955	printf("need to allocate an id for the request\n");
956#endif
957	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
958		if (nsems <= 0 || nsems > limitseminfo.semmsl) {
959#ifdef SEM_DEBUG
960			printf("nsems out of range (0<%d<=%d)\n", nsems,
961			    seminfo.semmsl);
962#endif
963			eval = EINVAL;
964			goto semgetout;
965		}
966		if (nsems > seminfo.semmns - semtot) {
967#ifdef SEM_DEBUG
968			printf("not enough semaphores left (need %d, got %d)\n",
969			    nsems, seminfo.semmns - semtot);
970#endif
971			if (!grow_sem_pool(semtot + nsems)) {
972#ifdef SEM_DEBUG
973				printf("failed to grow the sem array\n");
974#endif
975				eval = ENOSPC;
976				goto semgetout;
977			}
978		}
979		for (semid = 0; semid < seminfo.semmni; semid++) {
980			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0)
981				break;
982		}
983		if (semid == seminfo.semmni) {
984#ifdef SEM_DEBUG
985			printf("no more id's available\n");
986#endif
987			if (!grow_sema_array(seminfo.semmni + 1))
988			{
989#ifdef SEM_DEBUG
990				printf("failed to grow sema array\n");
991#endif
992				eval = ENOSPC;
993				goto semgetout;
994			}
995		}
996#ifdef SEM_DEBUG
997		printf("semid %d is available\n", semid);
998#endif
999		sema[semid].u.sem_perm._key = key;
1000		sema[semid].u.sem_perm.cuid = kauth_cred_getuid(cred);
1001		sema[semid].u.sem_perm.uid = kauth_cred_getuid(cred);
1002		sema[semid].u.sem_perm.cgid = kauth_cred_getgid(cred);
1003		sema[semid].u.sem_perm.gid = kauth_cred_getgid(cred);
1004		sema[semid].u.sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
1005		sema[semid].u.sem_perm._seq =
1006		    (sema[semid].u.sem_perm._seq + 1) & 0x7fff;
1007		sema[semid].u.sem_nsems = nsems;
1008		sema[semid].u.sem_otime = 0;
1009		sema[semid].u.sem_ctime = sysv_semtime();
1010		sema[semid].u.sem_base = &sem_pool[semtot];
1011		semtot += nsems;
1012		bzero(sema[semid].u.sem_base,
1013		    sizeof(sema[semid].u.sem_base[0])*nsems);
1014#if CONFIG_MACF
1015		mac_sysvsem_label_associate(cred, &sema[semid]);
1016#endif
1017#ifdef SEM_DEBUG
1018		printf("sembase = 0x%x, next = 0x%x\n", sema[semid].u.sem_base,
1019		    &sem_pool[semtot]);
1020#endif
1021	} else {
1022#ifdef SEM_DEBUG
1023		printf("didn't find it and wasn't asked to create it\n");
1024#endif
1025		eval = ENOENT;
1026		goto semgetout;
1027	}
1028
1029found:
1030	*retval = IXSEQ_TO_IPCID(semid, sema[semid].u.sem_perm);
1031	AUDIT_ARG(svipc_id, *retval);
1032#ifdef SEM_DEBUG
1033	printf("semget is done, returning %d\n", *retval);
1034#endif
1035	eval = 0;
1036
1037semgetout:
1038	SYSV_SEM_SUBSYS_UNLOCK();
1039	return(eval);
1040}
1041
1042int
1043semop(struct proc *p, struct semop_args *uap, int32_t *retval)
1044{
1045	int semid = uap->semid;
1046	int nsops = uap->nsops;
1047	struct sembuf sops[seminfo.semopm];
1048	register struct semid_kernel *semakptr;
1049	register struct sembuf *sopptr = NULL;	/* protected by 'semptr' */
1050	register struct sem *semptr = NULL;	/* protected by 'if' */
1051	int supidx = -1;
1052	int i, j, eval;
1053	int do_wakeup, do_undos;
1054
1055	AUDIT_ARG(svipc_id, uap->semid);
1056
1057	SYSV_SEM_SUBSYS_LOCK();
1058
1059#ifdef SEM_DEBUG
1060	printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops);
1061#endif
1062
1063	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
1064
1065	if (semid < 0 || semid >= seminfo.semmni) {
1066		eval = EINVAL;
1067		goto semopout;
1068	}
1069
1070	semakptr = &sema[semid];
1071	if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) {
1072		eval = EINVAL;
1073		goto semopout;
1074	}
1075	if (semakptr->u.sem_perm._seq != IPCID_TO_SEQ(uap->semid)) {
1076		eval = EINVAL;
1077		goto semopout;
1078	}
1079
1080	if ((eval = ipcperm(kauth_cred_get(), &semakptr->u.sem_perm, IPC_W))) {
1081#ifdef SEM_DEBUG
1082		printf("eval = %d from ipaccess\n", eval);
1083#endif
1084		goto semopout;
1085	}
1086
1087	if (nsops < 0 || nsops > seminfo.semopm) {
1088#ifdef SEM_DEBUG
1089		printf("too many sops (max=%d, nsops=%d)\n",
1090		    seminfo.semopm, nsops);
1091#endif
1092		eval = E2BIG;
1093		goto semopout;
1094	}
1095
1096	/*  OK for LP64, since sizeof(struct sembuf) is currently invariant */
1097	if ((eval = copyin(uap->sops, &sops, nsops * sizeof(struct sembuf))) != 0) {
1098#ifdef SEM_DEBUG
1099		printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval,
1100		    uap->sops, &sops, nsops * sizeof(struct sembuf));
1101#endif
1102		goto semopout;
1103	}
1104
1105#if CONFIG_MACF
1106	/*
1107	 * Initial pass thru sops to see what permissions are needed.
1108	 */
1109	j = 0;		/* permission needed */
1110	for (i = 0; i < nsops; i++)
1111		j |= (sops[i].sem_op == 0) ? SEM_R : SEM_A;
1112
1113	/*
1114	 * The MAC hook checks whether the thread has read (and possibly
1115	 * write) permissions to the semaphore array based on the
1116	 * sopptr->sem_op value.
1117	 */
1118	eval = mac_sysvsem_check_semop(kauth_cred_get(), semakptr, j);
1119	if (eval)
1120		goto semopout;
1121#endif
1122
1123	/*
1124	 * Loop trying to satisfy the vector of requests.
1125	 * If we reach a point where we must wait, any requests already
1126	 * performed are rolled back and we go to sleep until some other
1127	 * process wakes us up.  At this point, we start all over again.
1128	 *
1129	 * This ensures that from the perspective of other tasks, a set
1130	 * of requests is atomic (never partially satisfied).
1131	 */
1132	do_undos = 0;
1133
1134	for (;;) {
1135		do_wakeup = 0;
1136
1137		for (i = 0; i < nsops; i++) {
1138			sopptr = &sops[i];
1139
1140			if (sopptr->sem_num >= semakptr->u.sem_nsems) {
1141				eval = EFBIG;
1142				goto semopout;
1143			}
1144
1145			semptr = &semakptr->u.sem_base[sopptr->sem_num];
1146
1147#ifdef SEM_DEBUG
1148			printf("semop:  semakptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
1149			    semakptr, semakptr->u.sem_base, semptr,
1150			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
1151			    (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
1152#endif
1153
1154			if (sopptr->sem_op < 0) {
1155				if (semptr->semval + sopptr->sem_op < 0) {
1156#ifdef SEM_DEBUG
1157					printf("semop:  can't do it now\n");
1158#endif
1159					break;
1160				} else {
1161					semptr->semval += sopptr->sem_op;
1162					if (semptr->semval == 0 &&
1163					    semptr->semzcnt > 0)
1164						do_wakeup = 1;
1165				}
1166				if (sopptr->sem_flg & SEM_UNDO)
1167					do_undos = 1;
1168			} else if (sopptr->sem_op == 0) {
1169				if (semptr->semval > 0) {
1170#ifdef SEM_DEBUG
1171					printf("semop:  not zero now\n");
1172#endif
1173					break;
1174				}
1175			} else {
1176				if (semptr->semncnt > 0)
1177					do_wakeup = 1;
1178				semptr->semval += sopptr->sem_op;
1179				if (sopptr->sem_flg & SEM_UNDO)
1180					do_undos = 1;
1181			}
1182		}
1183
1184		/*
1185		 * Did we get through the entire vector?
1186		 */
1187		if (i >= nsops)
1188			goto done;
1189
1190		/*
1191		 * No ... rollback anything that we've already done
1192		 */
1193#ifdef SEM_DEBUG
1194		printf("semop:  rollback 0 through %d\n", i-1);
1195#endif
1196		for (j = 0; j < i; j++)
1197			semakptr->u.sem_base[sops[j].sem_num].semval -=
1198			    sops[j].sem_op;
1199
1200		/*
1201		 * If the request that we couldn't satisfy has the
1202		 * NOWAIT flag set then return with EAGAIN.
1203		 */
1204		if (sopptr->sem_flg & IPC_NOWAIT) {
1205			eval = EAGAIN;
1206			goto semopout;
1207		}
1208
1209		if (sopptr->sem_op == 0)
1210			semptr->semzcnt++;
1211		else
1212			semptr->semncnt++;
1213
1214#ifdef SEM_DEBUG
1215		printf("semop:  good night!\n");
1216#endif
1217		/* Release our lock on the semaphore subsystem so
1218		 * another thread can get at the semaphore we are
1219		 * waiting for. We will get the lock back after we
1220		 * wake up.
1221		 */
1222		eval = msleep((caddr_t)semakptr, &sysv_sem_subsys_mutex , (PZERO - 4) | PCATCH,
1223		    "semwait", 0);
1224
1225#ifdef SEM_DEBUG
1226		printf("semop:  good morning (eval=%d)!\n", eval);
1227#endif
1228		if (eval != 0) {
1229			eval = EINTR;
1230		}
1231
1232		/*
1233		 * IMPORTANT: while we were asleep, the semaphore array might
1234		 * have been reallocated somewhere else (see grow_sema_array()).
1235		 * When we wake up, we have to re-lookup the semaphore
1236		 * structures and re-validate them.
1237		 */
1238
1239		semptr = NULL;
1240
1241		/*
1242		 * Make sure that the semaphore still exists
1243		 *
1244		 * XXX POSIX: Third test this 'if' and 'EINTR' precedence may
1245		 * fail testing; if so, we will need to revert this code.
1246		 */
1247	 	semakptr = &sema[semid];   /* sema may have been reallocated */
1248		if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
1249		    semakptr->u.sem_perm._seq != IPCID_TO_SEQ(uap->semid) ||
1250		    sopptr->sem_num >= semakptr->u.sem_nsems) {
1251			/* The man page says to return EIDRM. */
1252			/* Unfortunately, BSD doesn't define that code! */
1253			if (eval == EINTR) {
1254				/*
1255				 * EINTR takes precedence over the fact that
1256				 * the semaphore disappeared while we were
1257				 * sleeping...
1258				 */
1259			} else {
1260#ifdef EIDRM
1261				eval = EIDRM;
1262#else
1263				eval = EINVAL;		/* Ancient past */
1264#endif
1265			}
1266			goto semopout;
1267		}
1268
1269		/*
1270		 * The semaphore is still alive.  Readjust the count of
1271		 * waiting processes. semptr needs to be recomputed
1272		 * because the sem[] may have been reallocated while
1273		 * we were sleeping, updating our sem_base pointer.
1274		 */
1275		semptr = &semakptr->u.sem_base[sopptr->sem_num];
1276		if (sopptr->sem_op == 0)
1277			semptr->semzcnt--;
1278		else
1279			semptr->semncnt--;
1280
1281		if (eval != 0) { /* EINTR */
1282			goto semopout;
1283		}
1284	}
1285
1286done:
1287	/*
1288	 * Process any SEM_UNDO requests.
1289	 */
1290	if (do_undos) {
1291		for (i = 0; i < nsops; i++) {
1292			/*
1293			 * We only need to deal with SEM_UNDO's for non-zero
1294			 * op's.
1295			 */
1296			int adjval;
1297
1298			if ((sops[i].sem_flg & SEM_UNDO) == 0)
1299				continue;
1300			adjval = sops[i].sem_op;
1301			if (adjval == 0)
1302				continue;
1303			eval = semundo_adjust(p, &supidx, semid,
1304			    sops[i].sem_num, -adjval);
1305			if (eval == 0)
1306				continue;
1307
1308			/*
1309			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
1310			 * Rollback the adjustments to this point and then
1311			 * rollback the semaphore ups and down so we can return
1312			 * with an error with all structures restored.  We
1313			 * rollback the undo's in the exact reverse order that
1314			 * we applied them.  This guarantees that we won't run
1315			 * out of space as we roll things back out.
1316			 */
1317			for (j = i - 1; j >= 0; j--) {
1318				if ((sops[j].sem_flg & SEM_UNDO) == 0)
1319					continue;
1320				adjval = sops[j].sem_op;
1321				if (adjval == 0)
1322					continue;
1323				if (semundo_adjust(p, &supidx, semid,
1324				    sops[j].sem_num, adjval) != 0)
1325					panic("semop - can't undo undos");
1326			}
1327
1328			for (j = 0; j < nsops; j++)
1329				semakptr->u.sem_base[sops[j].sem_num].semval -=
1330				    sops[j].sem_op;
1331
1332#ifdef SEM_DEBUG
1333			printf("eval = %d from semundo_adjust\n", eval);
1334#endif
1335			goto semopout;
1336		} /* loop through the sops */
1337	} /* if (do_undos) */
1338
1339	/* We're definitely done - set the sempid's */
1340	for (i = 0; i < nsops; i++) {
1341		sopptr = &sops[i];
1342		semptr = &semakptr->u.sem_base[sopptr->sem_num];
1343		semptr->sempid = p->p_pid;
1344	}
1345	semakptr->u.sem_otime = sysv_semtime();
1346
1347	if (do_wakeup) {
1348#ifdef SEM_DEBUG
1349		printf("semop:  doing wakeup\n");
1350#ifdef SEM_WAKEUP
1351		sem_wakeup((caddr_t)semakptr);
1352#else
1353		wakeup((caddr_t)semakptr);
1354#endif
1355		printf("semop:  back from wakeup\n");
1356#else
1357		wakeup((caddr_t)semakptr);
1358#endif
1359	}
1360#ifdef SEM_DEBUG
1361	printf("semop:  done\n");
1362#endif
1363	*retval = 0;
1364	eval = 0;
1365semopout:
1366	SYSV_SEM_SUBSYS_UNLOCK();
1367	return(eval);
1368}
1369
1370/*
1371 * Go through the undo structures for this process and apply the adjustments to
1372 * semaphores.
1373 */
1374void
1375semexit(struct proc *p)
1376{
1377	register struct sem_undo *suptr = NULL;
1378	int suidx;
1379	int *supidx;
1380	int did_something;
1381
1382	/* If we have not allocated our semaphores yet there can't be
1383	 * anything to undo, but we need the lock to prevent
1384	 * dynamic memory race conditions.
1385	 */
1386	SYSV_SEM_SUBSYS_LOCK();
1387
1388	if (!sem_pool)
1389	{
1390		SYSV_SEM_SUBSYS_UNLOCK();
1391		return;
1392	}
1393	did_something = 0;
1394
1395	/*
1396	 * Go through the chain of undo vectors looking for one
1397	 * associated with this process.
1398	 */
1399
1400	for (supidx = &semu_list_idx; (suidx = *supidx) != -1;
1401	    supidx = &suptr->un_next_idx) {
1402		suptr = SEMU(suidx);
1403		if (suptr->un_proc == p)
1404			break;
1405	}
1406
1407	if (suidx == -1)
1408		goto unlock;
1409
1410#ifdef SEM_DEBUG
1411	printf("proc @%08x has undo structure with %d entries\n", p,
1412	    suptr->un_cnt);
1413#endif
1414
1415	/*
1416	 * If there are any active undo elements then process them.
1417	 */
1418	if (suptr->un_cnt > 0) {
1419		while (suptr->un_ent != NULL) {
1420			struct undo *sueptr;
1421			int semid;
1422			int semnum;
1423			int adjval;
1424			struct semid_kernel *semakptr;
1425
1426			sueptr = suptr->un_ent;
1427			semid = sueptr->une_id;
1428			semnum = sueptr->une_num;
1429			adjval = sueptr->une_adjval;
1430
1431			semakptr = &sema[semid];
1432			if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0)
1433				panic("semexit - semid not allocated");
1434			if (semnum >= semakptr->u.sem_nsems)
1435				panic("semexit - semnum out of range");
1436
1437#ifdef SEM_DEBUG
1438			printf("semexit:  %08x id=%d num=%d(adj=%d) ; sem=%d\n",
1439			       suptr->un_proc,
1440			       semid,
1441			       semnum,
1442			       adjval,
1443			       semakptr->u.sem_base[semnum].semval);
1444#endif
1445
1446			if (adjval < 0) {
1447				if (semakptr->u.sem_base[semnum].semval < -adjval)
1448					semakptr->u.sem_base[semnum].semval = 0;
1449				else
1450					semakptr->u.sem_base[semnum].semval +=
1451					    adjval;
1452			} else
1453				semakptr->u.sem_base[semnum].semval += adjval;
1454
1455		/* Maybe we should build a list of semakptr's to wake
1456		 * up, finish all access to data structures, release the
1457		 * subsystem lock, and wake all the processes.  Something
1458		 * to think about.  It wouldn't buy us anything unless
1459		 * wakeup had the potential to block, or the syscall
1460		 * funnel state was changed to allow multiple threads
1461		 * in the BSD code at once.
1462		 */
1463#ifdef SEM_WAKEUP
1464			sem_wakeup((caddr_t)semakptr);
1465#else
1466			wakeup((caddr_t)semakptr);
1467#endif
1468#ifdef SEM_DEBUG
1469			printf("semexit:  back from wakeup\n");
1470#endif
1471			suptr->un_cnt--;
1472			suptr->un_ent = sueptr->une_next;
1473			FREE(sueptr, M_SYSVSEM);
1474			sueptr = NULL;
1475		}
1476	}
1477
1478	/*
1479	 * Deallocate the undo vector.
1480	 */
1481#ifdef SEM_DEBUG
1482	printf("removing vector\n");
1483#endif
1484	suptr->un_proc = NULL;
1485	*supidx = suptr->un_next_idx;
1486
1487unlock:
1488	/*
1489         * There is a semaphore leak (i.e. memory leak) in this code.
1490         * We should be deleting the IPC_PRIVATE semaphores when they are
1491         * no longer needed, and we dont. We would have to track which processes
1492         * know about which IPC_PRIVATE semaphores, updating the list after
1493         * every fork.  We can't just delete them semaphore when the process
1494         * that created it dies, because that process may well have forked
1495         * some children.  So we need to wait until all of it's children have
1496         * died, and so on.  Maybe we should tag each IPC_PRIVATE sempahore
1497         * with the creating group ID, count the number of processes left in
1498         * that group, and delete the semaphore when the group is gone.
1499         * Until that code gets implemented we will leak IPC_PRIVATE semaphores.
1500         * There is an upper bound on the size of our semaphore array, so
1501         * leaking the semaphores should not work as a DOS attack.
1502         *
1503         * Please note that the original BSD code this file is based on had the
1504         * same leaky semaphore problem.
1505         */
1506
1507	SYSV_SEM_SUBSYS_UNLOCK();
1508}
1509
1510
1511/* (struct sysctl_oid *oidp, void *arg1, int arg2, \
1512        struct sysctl_req *req) */
1513static int
1514sysctl_seminfo(__unused struct sysctl_oid *oidp, void *arg1,
1515	__unused int arg2, struct sysctl_req *req)
1516{
1517	int error = 0;
1518
1519	error = SYSCTL_OUT(req, arg1, sizeof(int));
1520	if (error || req->newptr == USER_ADDR_NULL)
1521		return(error);
1522
1523	SYSV_SEM_SUBSYS_LOCK();
1524
1525	/* Set the values only if shared memory is not initialised */
1526	if ((sem_pool == NULL) &&
1527		(sema == NULL) &&
1528		(semu == NULL) &&
1529		(semu_list_idx == -1)) {
1530			if ((error = SYSCTL_IN(req, arg1, sizeof(int)))) {
1531				goto out;
1532			}
1533	} else
1534		error = EINVAL;
1535out:
1536	SYSV_SEM_SUBSYS_UNLOCK();
1537	return(error);
1538
1539}
1540
1541/* SYSCTL_NODE(_kern, KERN_SYSV, sysv, CTLFLAG_RW, 0, "SYSV"); */
1542extern struct sysctl_oid_list sysctl__kern_sysv_children;
1543SYSCTL_PROC(_kern_sysv, OID_AUTO, semmni, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
1544    &limitseminfo.semmni, 0, &sysctl_seminfo ,"I","semmni");
1545
1546SYSCTL_PROC(_kern_sysv, OID_AUTO, semmns, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
1547    &limitseminfo.semmns, 0, &sysctl_seminfo ,"I","semmns");
1548
1549SYSCTL_PROC(_kern_sysv, OID_AUTO, semmnu, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
1550    &limitseminfo.semmnu, 0, &sysctl_seminfo ,"I","semmnu");
1551
1552SYSCTL_PROC(_kern_sysv, OID_AUTO, semmsl, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
1553    &limitseminfo.semmsl, 0, &sysctl_seminfo ,"I","semmsl");
1554
1555SYSCTL_PROC(_kern_sysv, OID_AUTO, semume, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
1556    &limitseminfo.semume, 0, &sysctl_seminfo ,"I","semume");
1557
1558
1559static int
1560IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
1561	__unused int arg2, struct sysctl_req *req)
1562{
1563	int error;
1564	int cursor;
1565	union {
1566		struct user32_IPCS_command u32;
1567		struct user_IPCS_command u64;
1568	} ipcs;
1569	struct user32_semid_ds semid_ds32;	/* post conversion, 32 bit version */
1570	struct user64_semid_ds semid_ds64;	/* post conversion, 64 bit version */
1571	void *semid_dsp;
1572	size_t ipcs_sz;
1573	size_t semid_ds_sz;
1574	struct proc *p = current_proc();
1575
1576	if (IS_64BIT_PROCESS(p)) {
1577		ipcs_sz = sizeof(struct user_IPCS_command);
1578		semid_ds_sz = sizeof(struct user64_semid_ds);
1579	} else {
1580		ipcs_sz = sizeof(struct user32_IPCS_command);
1581		semid_ds_sz = sizeof(struct user32_semid_ds);
1582	}
1583
1584	/* Copy in the command structure */
1585	if ((error = SYSCTL_IN(req, &ipcs, ipcs_sz)) != 0) {
1586		return(error);
1587	}
1588
1589	if (!IS_64BIT_PROCESS(p)) /* convert in place */
1590		ipcs.u64.ipcs_data = CAST_USER_ADDR_T(ipcs.u32.ipcs_data);
1591
1592	/* Let us version this interface... */
1593	if (ipcs.u64.ipcs_magic != IPCS_MAGIC) {
1594		return(EINVAL);
1595	}
1596
1597	SYSV_SEM_SUBSYS_LOCK();
1598	switch(ipcs.u64.ipcs_op) {
1599	case IPCS_SEM_CONF:	/* Obtain global configuration data */
1600		if (ipcs.u64.ipcs_datalen != sizeof(struct seminfo)) {
1601			error = ERANGE;
1602			break;
1603		}
1604		if (ipcs.u64.ipcs_cursor != 0) {	/* fwd. compat. */
1605			error = EINVAL;
1606			break;
1607		}
1608		error = copyout(&seminfo, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
1609		break;
1610
1611	case IPCS_SEM_ITER:	/* Iterate over existing segments */
1612		cursor = ipcs.u64.ipcs_cursor;
1613		if (cursor < 0 || cursor >= seminfo.semmni) {
1614			error = ERANGE;
1615			break;
1616		}
1617		if (ipcs.u64.ipcs_datalen != (int)semid_ds_sz ) {
1618			error = EINVAL;
1619			break;
1620		}
1621		for( ; cursor < seminfo.semmni; cursor++) {
1622			if (sema[cursor].u.sem_perm.mode & SEM_ALLOC)
1623				break;
1624			continue;
1625		}
1626		if (cursor == seminfo.semmni) {
1627			error = ENOENT;
1628			break;
1629		}
1630
1631		semid_dsp = &sema[cursor].u;	/* default: 64 bit */
1632
1633		/*
1634		 * If necessary, convert the 64 bit kernel segment
1635		 * descriptor to a 32 bit user one.
1636		 */
1637		if (!IS_64BIT_PROCESS(p)) {
1638			semid_ds_kernelto32(semid_dsp, &semid_ds32);
1639			semid_dsp = &semid_ds32;
1640		} else {
1641			semid_ds_kernelto64(semid_dsp, &semid_ds64);
1642			semid_dsp = &semid_ds64;
1643		}
1644
1645		error = copyout(semid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
1646		if (!error) {
1647			/* update cursor */
1648			ipcs.u64.ipcs_cursor = cursor + 1;
1649
1650			if (!IS_64BIT_PROCESS(p))       /* convert in place */
1651				ipcs.u32.ipcs_data = CAST_DOWN_EXPLICIT(user32_addr_t,ipcs.u64.ipcs_data);
1652
1653			error = SYSCTL_OUT(req, &ipcs, ipcs_sz);
1654		}
1655		break;
1656
1657	default:
1658		error = EINVAL;
1659		break;
1660	}
1661	SYSV_SEM_SUBSYS_UNLOCK();
1662	return(error);
1663}
1664
1665SYSCTL_DECL(_kern_sysv_ipcs);
1666SYSCTL_PROC(_kern_sysv_ipcs, OID_AUTO, sem, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
1667	0, 0, IPCS_sem_sysctl,
1668	"S,IPCS_sem_command",
1669	"ipcs sem command interface");
1670
1671#endif /* SYSV_SEM */
1672