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