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