]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sysv_sem.c
xnu-1504.9.17.tar.gz
[apple/xnu.git] / bsd / kern / sysv_sem.c
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 */
87 static 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 */
104 struct 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
118 static int semu_alloc(struct proc *p);
119 static int semundo_adjust(struct proc *p, int *supidx,
120 int semid, int semnum, int adjval);
121 static void semundo_clear(int semid, int semnum);
122
123 /* XXX casting to (sy_call_t *) is bogus, as usual. */
124 static sy_call_t *semcalls[] = {
125 (sy_call_t *)semctl, (sy_call_t *)semget,
126 (sy_call_t *)semop
127 };
128
129 static int semtot = 0; /* # of used semaphores */
130 struct semid_kernel *sema = NULL; /* semaphore id pool */
131 struct sem *sem_pool = NULL; /* semaphore pool */
132 static int semu_list_idx = -1; /* active undo structures */
133 struct sem_undo *semu = NULL; /* semaphore undo pool */
134
135
136 void sysv_sem_lock_init(void);
137 static lck_grp_t *sysv_sem_subsys_lck_grp;
138 static lck_grp_attr_t *sysv_sem_subsys_lck_grp_attr;
139 static lck_attr_t *sysv_sem_subsys_lck_attr;
140 static 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
147 sysv_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
158 static __inline__ user_time_t
159 sysv_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 */
175 static void
176 semid_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
185 static void
186 semid_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 */
205 static void
206 semid_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
215 static void
216 semid_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 */
248 int
249 semsys(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 */
265 static int
266 grow_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 */
326 static int
327 grow_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 */
406 static int
407 grow_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
471 static int
472 semu_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 */
548 static int
549 semundo_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 */
636 static void
637 semundo_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 */
676 int
677 semctl(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;
893 semctlout:
894 SYSV_SEM_SUBSYS_UNLOCK();
895 return(eval);
896 }
897
898 int
899 semget(__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 = cred->cr_gid;
1003 sema[semid].u.sem_perm.gid = cred->cr_gid;
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
1029 found:
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
1037 semgetout:
1038 SYSV_SEM_SUBSYS_UNLOCK();
1039 return(eval);
1040 }
1041
1042 int
1043 semop(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[MAX_SOPS];
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 > MAX_SOPS) {
1088 #ifdef SEM_DEBUG
1089 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops);
1090 #endif
1091 eval = E2BIG;
1092 goto semopout;
1093 }
1094
1095 #if CONFIG_MACF
1096 /*
1097 * Initial pass thru sops to see what permissions are needed.
1098 */
1099 j = 0; /* permission needed */
1100 for (i = 0; i < nsops; i++)
1101 j |= (sops[i].sem_op == 0) ? SEM_R : SEM_A;
1102
1103 /*
1104 * The MAC hook checks whether the thread has read (and possibly
1105 * write) permissions to the semaphore array based on the
1106 * sopptr->sem_op value.
1107 */
1108 eval = mac_sysvsem_check_semop(kauth_cred_get(), semakptr, j);
1109 if (eval)
1110 goto semopout;
1111 #endif
1112
1113 /* OK for LP64, since sizeof(struct sembuf) is currently invariant */
1114 if ((eval = copyin(uap->sops, &sops, nsops * sizeof(struct sembuf))) != 0) {
1115 #ifdef SEM_DEBUG
1116 printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval,
1117 uap->sops, &sops, nsops * sizeof(struct sembuf));
1118 #endif
1119 goto semopout;
1120 }
1121
1122 /*
1123 * Loop trying to satisfy the vector of requests.
1124 * If we reach a point where we must wait, any requests already
1125 * performed are rolled back and we go to sleep until some other
1126 * process wakes us up. At this point, we start all over again.
1127 *
1128 * This ensures that from the perspective of other tasks, a set
1129 * of requests is atomic (never partially satisfied).
1130 */
1131 do_undos = 0;
1132
1133 for (;;) {
1134 do_wakeup = 0;
1135
1136 for (i = 0; i < nsops; i++) {
1137 sopptr = &sops[i];
1138
1139 if (sopptr->sem_num >= semakptr->u.sem_nsems) {
1140 eval = EFBIG;
1141 goto semopout;
1142 }
1143
1144 semptr = &semakptr->u.sem_base[sopptr->sem_num];
1145
1146 #ifdef SEM_DEBUG
1147 printf("semop: semakptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
1148 semakptr, semakptr->u.sem_base, semptr,
1149 sopptr->sem_num, semptr->semval, sopptr->sem_op,
1150 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
1151 #endif
1152
1153 if (sopptr->sem_op < 0) {
1154 if (semptr->semval + sopptr->sem_op < 0) {
1155 #ifdef SEM_DEBUG
1156 printf("semop: can't do it now\n");
1157 #endif
1158 break;
1159 } else {
1160 semptr->semval += sopptr->sem_op;
1161 if (semptr->semval == 0 &&
1162 semptr->semzcnt > 0)
1163 do_wakeup = 1;
1164 }
1165 if (sopptr->sem_flg & SEM_UNDO)
1166 do_undos = 1;
1167 } else if (sopptr->sem_op == 0) {
1168 if (semptr->semval > 0) {
1169 #ifdef SEM_DEBUG
1170 printf("semop: not zero now\n");
1171 #endif
1172 break;
1173 }
1174 } else {
1175 if (semptr->semncnt > 0)
1176 do_wakeup = 1;
1177 semptr->semval += sopptr->sem_op;
1178 if (sopptr->sem_flg & SEM_UNDO)
1179 do_undos = 1;
1180 }
1181 }
1182
1183 /*
1184 * Did we get through the entire vector?
1185 */
1186 if (i >= nsops)
1187 goto done;
1188
1189 /*
1190 * No ... rollback anything that we've already done
1191 */
1192 #ifdef SEM_DEBUG
1193 printf("semop: rollback 0 through %d\n", i-1);
1194 #endif
1195 for (j = 0; j < i; j++)
1196 semakptr->u.sem_base[sops[j].sem_num].semval -=
1197 sops[j].sem_op;
1198
1199 /*
1200 * If the request that we couldn't satisfy has the
1201 * NOWAIT flag set then return with EAGAIN.
1202 */
1203 if (sopptr->sem_flg & IPC_NOWAIT) {
1204 eval = EAGAIN;
1205 goto semopout;
1206 }
1207
1208 if (sopptr->sem_op == 0)
1209 semptr->semzcnt++;
1210 else
1211 semptr->semncnt++;
1212
1213 #ifdef SEM_DEBUG
1214 printf("semop: good night!\n");
1215 #endif
1216 /* Release our lock on the semaphore subsystem so
1217 * another thread can get at the semaphore we are
1218 * waiting for. We will get the lock back after we
1219 * wake up.
1220 */
1221 eval = msleep((caddr_t)semakptr, &sysv_sem_subsys_mutex , (PZERO - 4) | PCATCH,
1222 "semwait", 0);
1223
1224 #ifdef SEM_DEBUG
1225 printf("semop: good morning (eval=%d)!\n", eval);
1226 #endif
1227 if (eval != 0) {
1228 eval = EINTR;
1229 }
1230
1231 /*
1232 * IMPORTANT: while we were asleep, the semaphore array might
1233 * have been reallocated somewhere else (see grow_sema_array()).
1234 * When we wake up, we have to re-lookup the semaphore
1235 * structures and re-validate them.
1236 */
1237
1238 semptr = NULL;
1239
1240 /*
1241 * Make sure that the semaphore still exists
1242 *
1243 * XXX POSIX: Third test this 'if' and 'EINTR' precedence may
1244 * fail testing; if so, we will need to revert this code.
1245 */
1246 semakptr = &sema[semid]; /* sema may have been reallocated */
1247 if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
1248 semakptr->u.sem_perm._seq != IPCID_TO_SEQ(uap->semid) ||
1249 sopptr->sem_num >= semakptr->u.sem_nsems) {
1250 /* The man page says to return EIDRM. */
1251 /* Unfortunately, BSD doesn't define that code! */
1252 if (eval == EINTR) {
1253 /*
1254 * EINTR takes precedence over the fact that
1255 * the semaphore disappeared while we were
1256 * sleeping...
1257 */
1258 } else {
1259 #ifdef EIDRM
1260 eval = EIDRM;
1261 #else
1262 eval = EINVAL; /* Ancient past */
1263 #endif
1264 }
1265 goto semopout;
1266 }
1267
1268 /*
1269 * The semaphore is still alive. Readjust the count of
1270 * waiting processes. semptr needs to be recomputed
1271 * because the sem[] may have been reallocated while
1272 * we were sleeping, updating our sem_base pointer.
1273 */
1274 semptr = &semakptr->u.sem_base[sopptr->sem_num];
1275 if (sopptr->sem_op == 0)
1276 semptr->semzcnt--;
1277 else
1278 semptr->semncnt--;
1279
1280 if (eval != 0) { /* EINTR */
1281 goto semopout;
1282 }
1283 }
1284
1285 done:
1286 /*
1287 * Process any SEM_UNDO requests.
1288 */
1289 if (do_undos) {
1290 for (i = 0; i < nsops; i++) {
1291 /*
1292 * We only need to deal with SEM_UNDO's for non-zero
1293 * op's.
1294 */
1295 int adjval;
1296
1297 if ((sops[i].sem_flg & SEM_UNDO) == 0)
1298 continue;
1299 adjval = sops[i].sem_op;
1300 if (adjval == 0)
1301 continue;
1302 eval = semundo_adjust(p, &supidx, semid,
1303 sops[i].sem_num, -adjval);
1304 if (eval == 0)
1305 continue;
1306
1307 /*
1308 * Oh-Oh! We ran out of either sem_undo's or undo's.
1309 * Rollback the adjustments to this point and then
1310 * rollback the semaphore ups and down so we can return
1311 * with an error with all structures restored. We
1312 * rollback the undo's in the exact reverse order that
1313 * we applied them. This guarantees that we won't run
1314 * out of space as we roll things back out.
1315 */
1316 for (j = i - 1; j >= 0; j--) {
1317 if ((sops[j].sem_flg & SEM_UNDO) == 0)
1318 continue;
1319 adjval = sops[j].sem_op;
1320 if (adjval == 0)
1321 continue;
1322 if (semundo_adjust(p, &supidx, semid,
1323 sops[j].sem_num, adjval) != 0)
1324 panic("semop - can't undo undos");
1325 }
1326
1327 for (j = 0; j < nsops; j++)
1328 semakptr->u.sem_base[sops[j].sem_num].semval -=
1329 sops[j].sem_op;
1330
1331 #ifdef SEM_DEBUG
1332 printf("eval = %d from semundo_adjust\n", eval);
1333 #endif
1334 goto semopout;
1335 } /* loop through the sops */
1336 } /* if (do_undos) */
1337
1338 /* We're definitely done - set the sempid's */
1339 for (i = 0; i < nsops; i++) {
1340 sopptr = &sops[i];
1341 semptr = &semakptr->u.sem_base[sopptr->sem_num];
1342 semptr->sempid = p->p_pid;
1343 }
1344 semakptr->u.sem_otime = sysv_semtime();
1345
1346 if (do_wakeup) {
1347 #ifdef SEM_DEBUG
1348 printf("semop: doing wakeup\n");
1349 #ifdef SEM_WAKEUP
1350 sem_wakeup((caddr_t)semakptr);
1351 #else
1352 wakeup((caddr_t)semakptr);
1353 #endif
1354 printf("semop: back from wakeup\n");
1355 #else
1356 wakeup((caddr_t)semakptr);
1357 #endif
1358 }
1359 #ifdef SEM_DEBUG
1360 printf("semop: done\n");
1361 #endif
1362 *retval = 0;
1363 eval = 0;
1364 semopout:
1365 SYSV_SEM_SUBSYS_UNLOCK();
1366 return(eval);
1367 }
1368
1369 /*
1370 * Go through the undo structures for this process and apply the adjustments to
1371 * semaphores.
1372 */
1373 void
1374 semexit(struct proc *p)
1375 {
1376 register struct sem_undo *suptr = NULL;
1377 int suidx;
1378 int *supidx;
1379 int did_something;
1380
1381 /* If we have not allocated our semaphores yet there can't be
1382 * anything to undo, but we need the lock to prevent
1383 * dynamic memory race conditions.
1384 */
1385 SYSV_SEM_SUBSYS_LOCK();
1386
1387 if (!sem_pool)
1388 {
1389 SYSV_SEM_SUBSYS_UNLOCK();
1390 return;
1391 }
1392 did_something = 0;
1393
1394 /*
1395 * Go through the chain of undo vectors looking for one
1396 * associated with this process.
1397 */
1398
1399 for (supidx = &semu_list_idx; (suidx = *supidx) != -1;
1400 supidx = &suptr->un_next_idx) {
1401 suptr = SEMU(suidx);
1402 if (suptr->un_proc == p)
1403 break;
1404 }
1405
1406 if (suidx == -1)
1407 goto unlock;
1408
1409 #ifdef SEM_DEBUG
1410 printf("proc @%08x has undo structure with %d entries\n", p,
1411 suptr->un_cnt);
1412 #endif
1413
1414 /*
1415 * If there are any active undo elements then process them.
1416 */
1417 if (suptr->un_cnt > 0) {
1418 while (suptr->un_ent != NULL) {
1419 struct undo *sueptr;
1420 int semid;
1421 int semnum;
1422 int adjval;
1423 struct semid_kernel *semakptr;
1424
1425 sueptr = suptr->un_ent;
1426 semid = sueptr->une_id;
1427 semnum = sueptr->une_num;
1428 adjval = sueptr->une_adjval;
1429
1430 semakptr = &sema[semid];
1431 if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0)
1432 panic("semexit - semid not allocated");
1433 if (semnum >= semakptr->u.sem_nsems)
1434 panic("semexit - semnum out of range");
1435
1436 #ifdef SEM_DEBUG
1437 printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",
1438 suptr->un_proc,
1439 semid,
1440 semnum,
1441 adjval,
1442 semakptr->u.sem_base[semnum].semval);
1443 #endif
1444
1445 if (adjval < 0) {
1446 if (semakptr->u.sem_base[semnum].semval < -adjval)
1447 semakptr->u.sem_base[semnum].semval = 0;
1448 else
1449 semakptr->u.sem_base[semnum].semval +=
1450 adjval;
1451 } else
1452 semakptr->u.sem_base[semnum].semval += adjval;
1453
1454 /* Maybe we should build a list of semakptr's to wake
1455 * up, finish all access to data structures, release the
1456 * subsystem lock, and wake all the processes. Something
1457 * to think about. It wouldn't buy us anything unless
1458 * wakeup had the potential to block, or the syscall
1459 * funnel state was changed to allow multiple threads
1460 * in the BSD code at once.
1461 */
1462 #ifdef SEM_WAKEUP
1463 sem_wakeup((caddr_t)semakptr);
1464 #else
1465 wakeup((caddr_t)semakptr);
1466 #endif
1467 #ifdef SEM_DEBUG
1468 printf("semexit: back from wakeup\n");
1469 #endif
1470 suptr->un_cnt--;
1471 suptr->un_ent = sueptr->une_next;
1472 FREE(sueptr, M_SYSVSEM);
1473 sueptr = NULL;
1474 }
1475 }
1476
1477 /*
1478 * Deallocate the undo vector.
1479 */
1480 #ifdef SEM_DEBUG
1481 printf("removing vector\n");
1482 #endif
1483 suptr->un_proc = NULL;
1484 *supidx = suptr->un_next_idx;
1485
1486 unlock:
1487 /*
1488 * There is a semaphore leak (i.e. memory leak) in this code.
1489 * We should be deleting the IPC_PRIVATE semaphores when they are
1490 * no longer needed, and we dont. We would have to track which processes
1491 * know about which IPC_PRIVATE semaphores, updating the list after
1492 * every fork. We can't just delete them semaphore when the process
1493 * that created it dies, because that process may well have forked
1494 * some children. So we need to wait until all of it's children have
1495 * died, and so on. Maybe we should tag each IPC_PRIVATE sempahore
1496 * with the creating group ID, count the number of processes left in
1497 * that group, and delete the semaphore when the group is gone.
1498 * Until that code gets implemented we will leak IPC_PRIVATE semaphores.
1499 * There is an upper bound on the size of our semaphore array, so
1500 * leaking the semaphores should not work as a DOS attack.
1501 *
1502 * Please note that the original BSD code this file is based on had the
1503 * same leaky semaphore problem.
1504 */
1505
1506 SYSV_SEM_SUBSYS_UNLOCK();
1507 }
1508
1509
1510 /* (struct sysctl_oid *oidp, void *arg1, int arg2, \
1511 struct sysctl_req *req) */
1512 static int
1513 sysctl_seminfo(__unused struct sysctl_oid *oidp, void *arg1,
1514 __unused int arg2, struct sysctl_req *req)
1515 {
1516 int error = 0;
1517
1518 error = SYSCTL_OUT(req, arg1, sizeof(int));
1519 if (error || req->newptr == USER_ADDR_NULL)
1520 return(error);
1521
1522 SYSV_SEM_SUBSYS_LOCK();
1523
1524 /* Set the values only if shared memory is not initialised */
1525 if ((sem_pool == NULL) &&
1526 (sema == NULL) &&
1527 (semu == NULL) &&
1528 (semu_list_idx == -1)) {
1529 if ((error = SYSCTL_IN(req, arg1, sizeof(int)))) {
1530 goto out;
1531 }
1532 } else
1533 error = EINVAL;
1534 out:
1535 SYSV_SEM_SUBSYS_UNLOCK();
1536 return(error);
1537
1538 }
1539
1540 /* SYSCTL_NODE(_kern, KERN_SYSV, sysv, CTLFLAG_RW, 0, "SYSV"); */
1541 extern struct sysctl_oid_list sysctl__kern_sysv_children;
1542 SYSCTL_PROC(_kern_sysv, OID_AUTO, semmni, CTLTYPE_INT | CTLFLAG_RW,
1543 &limitseminfo.semmni, 0, &sysctl_seminfo ,"I","semmni");
1544
1545 SYSCTL_PROC(_kern_sysv, OID_AUTO, semmns, CTLTYPE_INT | CTLFLAG_RW,
1546 &limitseminfo.semmns, 0, &sysctl_seminfo ,"I","semmns");
1547
1548 SYSCTL_PROC(_kern_sysv, OID_AUTO, semmnu, CTLTYPE_INT | CTLFLAG_RW,
1549 &limitseminfo.semmnu, 0, &sysctl_seminfo ,"I","semmnu");
1550
1551 SYSCTL_PROC(_kern_sysv, OID_AUTO, semmsl, CTLTYPE_INT | CTLFLAG_RW,
1552 &limitseminfo.semmsl, 0, &sysctl_seminfo ,"I","semmsl");
1553
1554 SYSCTL_PROC(_kern_sysv, OID_AUTO, semume, CTLTYPE_INT | CTLFLAG_RW,
1555 &limitseminfo.semume, 0, &sysctl_seminfo ,"I","semume");
1556
1557
1558 static int
1559 IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
1560 __unused int arg2, struct sysctl_req *req)
1561 {
1562 int error;
1563 int cursor;
1564 union {
1565 struct user32_IPCS_command u32;
1566 struct user_IPCS_command u64;
1567 } ipcs;
1568 struct user32_semid_ds semid_ds32; /* post conversion, 32 bit version */
1569 struct user64_semid_ds semid_ds64; /* post conversion, 64 bit version */
1570 void *semid_dsp;
1571 size_t ipcs_sz;
1572 size_t semid_ds_sz;
1573 struct proc *p = current_proc();
1574
1575 if (IS_64BIT_PROCESS(p)) {
1576 ipcs_sz = sizeof(struct user_IPCS_command);
1577 semid_ds_sz = sizeof(struct user64_semid_ds);
1578 } else {
1579 ipcs_sz = sizeof(struct user32_IPCS_command);
1580 semid_ds_sz = sizeof(struct user32_semid_ds);
1581 }
1582
1583 /* Copy in the command structure */
1584 if ((error = SYSCTL_IN(req, &ipcs, ipcs_sz)) != 0) {
1585 return(error);
1586 }
1587
1588 if (!IS_64BIT_PROCESS(p)) /* convert in place */
1589 ipcs.u64.ipcs_data = CAST_USER_ADDR_T(ipcs.u32.ipcs_data);
1590
1591 /* Let us version this interface... */
1592 if (ipcs.u64.ipcs_magic != IPCS_MAGIC) {
1593 return(EINVAL);
1594 }
1595
1596 SYSV_SEM_SUBSYS_LOCK();
1597 switch(ipcs.u64.ipcs_op) {
1598 case IPCS_SEM_CONF: /* Obtain global configuration data */
1599 if (ipcs.u64.ipcs_datalen != sizeof(struct seminfo)) {
1600 error = ERANGE;
1601 break;
1602 }
1603 if (ipcs.u64.ipcs_cursor != 0) { /* fwd. compat. */
1604 error = EINVAL;
1605 break;
1606 }
1607 error = copyout(&seminfo, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
1608 break;
1609
1610 case IPCS_SEM_ITER: /* Iterate over existing segments */
1611 cursor = ipcs.u64.ipcs_cursor;
1612 if (cursor < 0 || cursor >= seminfo.semmni) {
1613 error = ERANGE;
1614 break;
1615 }
1616 if (ipcs.u64.ipcs_datalen != (int)semid_ds_sz ) {
1617 error = EINVAL;
1618 break;
1619 }
1620 for( ; cursor < seminfo.semmni; cursor++) {
1621 if (sema[cursor].u.sem_perm.mode & SEM_ALLOC)
1622 break;
1623 continue;
1624 }
1625 if (cursor == seminfo.semmni) {
1626 error = ENOENT;
1627 break;
1628 }
1629
1630 semid_dsp = &sema[cursor].u; /* default: 64 bit */
1631
1632 /*
1633 * If necessary, convert the 64 bit kernel segment
1634 * descriptor to a 32 bit user one.
1635 */
1636 if (!IS_64BIT_PROCESS(p)) {
1637 semid_ds_kernelto32(semid_dsp, &semid_ds32);
1638 semid_dsp = &semid_ds32;
1639 } else {
1640 semid_ds_kernelto64(semid_dsp, &semid_ds64);
1641 semid_dsp = &semid_ds64;
1642 }
1643
1644 error = copyout(semid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
1645 if (!error) {
1646 /* update cursor */
1647 ipcs.u64.ipcs_cursor = cursor + 1;
1648
1649 if (!IS_64BIT_PROCESS(p)) /* convert in place */
1650 ipcs.u32.ipcs_data = CAST_DOWN_EXPLICIT(user32_addr_t,ipcs.u64.ipcs_data);
1651
1652 error = SYSCTL_OUT(req, &ipcs, ipcs_sz);
1653 }
1654 break;
1655
1656 default:
1657 error = EINVAL;
1658 break;
1659 }
1660 SYSV_SEM_SUBSYS_UNLOCK();
1661 return(error);
1662 }
1663
1664 SYSCTL_DECL(_kern_sysv_ipcs);
1665 SYSCTL_PROC(_kern_sysv_ipcs, OID_AUTO, sem, CTLFLAG_RW|CTLFLAG_ANYBODY,
1666 0, 0, IPCS_sem_sysctl,
1667 "S,IPCS_sem_command",
1668 "ipcs sem command interface");
1669
1670 #endif /* SYSV_SEM */