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