2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Implementation of SVID semaphores
25 * Author: Daniel Boulet
27 * This software is provided ``AS IS'' without any warranties of any kind.
30 * John Bellardo modified the implementation for Darwin. 12/2000
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/proc_internal.h>
37 #include <sys/kauth.h>
38 #include <sys/sem_internal.h>
39 #include <sys/malloc.h>
40 #include <mach/mach_types.h>
42 #include <sys/filedesc.h>
43 #include <sys/file_internal.h>
44 #include <sys/sysctl.h>
46 #include <sys/sysent.h>
47 #include <sys/sysproto.h>
49 #include <bsm/audit_kernel.h>
52 /* Uncomment this line to see the debugging output */
53 /* #define SEM_DEBUG */
55 #define M_SYSVSEM M_TEMP
58 /* Hard system limits to avoid resource starvation / DOS attacks.
59 * These are not needed if we can make the semaphore pages swappable.
61 static struct seminfo limitseminfo
= {
62 SEMMAP
, /* # of entries in semaphore map */
63 SEMMNI
, /* # of semaphore identifiers */
64 SEMMNS
, /* # of semaphores in system */
65 SEMMNU
, /* # of undo structures in system */
66 SEMMSL
, /* max # of semaphores per id */
67 SEMOPM
, /* max # of operations per semop call */
68 SEMUME
, /* max # of undo entries per process */
69 SEMUSZ
, /* size in bytes of undo structure */
70 SEMVMX
, /* semaphore maximum value */
71 SEMAEM
/* adjust on exit max value */
74 /* Current system allocations. We use this structure to track how many
75 * resources we have allocated so far. This way we can set large hard limits
76 * and not allocate the memory for them up front.
78 struct seminfo seminfo
= {
79 SEMMAP
, /* Unused, # of entries in semaphore map */
80 0, /* # of semaphore identifiers */
81 0, /* # of semaphores in system */
82 0, /* # of undo entries in system */
83 SEMMSL
, /* max # of semaphores per id */
84 SEMOPM
, /* max # of operations per semop call */
85 SEMUME
, /* max # of undo entries per process */
86 SEMUSZ
, /* size in bytes of undo structure */
87 SEMVMX
, /* semaphore maximum value */
88 SEMAEM
/* adjust on exit max value */
92 static struct sem_undo
*semu_alloc(struct proc
*p
);
93 static int semundo_adjust(struct proc
*p
, struct sem_undo
**supptr
,
94 int semid
, int semnum
, int adjval
);
95 static void semundo_clear(int semid
, int semnum
);
97 /* XXX casting to (sy_call_t *) is bogus, as usual. */
98 static sy_call_t
*semcalls
[] = {
99 (sy_call_t
*)semctl
, (sy_call_t
*)semget
,
100 (sy_call_t
*)semop
, (sy_call_t
*)semconfig
103 static int semtot
= 0; /* # of used semaphores */
104 struct user_semid_ds
*sema
= NULL
; /* semaphore id pool */
105 struct sem
*sem_pool
= NULL
; /* semaphore pool */
106 static struct sem_undo
*semu_list
= NULL
; /* active undo structures */
107 struct sem_undo
*semu
= NULL
; /* semaphore undo pool */
110 void sysv_sem_lock_init(void);
111 static lck_grp_t
*sysv_sem_subsys_lck_grp
;
112 static lck_grp_attr_t
*sysv_sem_subsys_lck_grp_attr
;
113 static lck_attr_t
*sysv_sem_subsys_lck_attr
;
114 static lck_mtx_t sysv_sem_subsys_mutex
;
116 #define SYSV_SEM_SUBSYS_LOCK() lck_mtx_lock(&sysv_sem_subsys_mutex)
117 #define SYSV_SEM_SUBSYS_UNLOCK() lck_mtx_unlock(&sysv_sem_subsys_mutex)
120 __private_extern__
void
121 sysv_sem_lock_init( void )
124 sysv_sem_subsys_lck_grp_attr
= lck_grp_attr_alloc_init();
125 lck_grp_attr_setstat(sysv_sem_subsys_lck_grp_attr
);
127 sysv_sem_subsys_lck_grp
= lck_grp_alloc_init("sysv_shm_subsys_lock", sysv_sem_subsys_lck_grp_attr
);
129 sysv_sem_subsys_lck_attr
= lck_attr_alloc_init();
130 lck_attr_setdebug(sysv_sem_subsys_lck_attr
);
131 lck_mtx_init(&sysv_sem_subsys_mutex
, sysv_sem_subsys_lck_grp
, sysv_sem_subsys_lck_attr
);
134 static __inline__ user_time_t
143 * XXX conversion of internal user_time_t to external tume_t loses
144 * XXX precision; not an issue for us now, since we are only ever
145 * XXX setting 32 bits worth of time into it.
147 * pad field contents are not moved correspondingly; contents will be lost
149 * NOTE: Source and target may *NOT* overlap! (target is smaller)
152 semid_ds_64to32(struct user_semid_ds
*in
, struct semid_ds
*out
)
154 out
->sem_perm
= in
->sem_perm
;
155 out
->sem_base
= (__int32_t
)in
->sem_base
;
156 out
->sem_nsems
= in
->sem_nsems
;
157 out
->sem_otime
= in
->sem_otime
; /* XXX loses precision */
158 out
->sem_ctime
= in
->sem_ctime
; /* XXX loses precision */
162 * pad field contents are not moved correspondingly; contents will be lost
164 * NOTE: Source and target may are permitted to overlap! (source is smaller);
165 * this works because we copy fields in order from the end of the struct to
168 * XXX use CAST_USER_ADDR_T() for lack of a CAST_USER_TIME_T(); net effect
172 semid_ds_32to64(struct semid_ds
*in
, struct user_semid_ds
*out
)
174 out
->sem_ctime
= in
->sem_ctime
;
175 out
->sem_otime
= in
->sem_otime
;
176 out
->sem_nsems
= in
->sem_nsems
;
177 out
->sem_base
= (void *)in
->sem_base
;
178 out
->sem_perm
= in
->sem_perm
;
183 * Entry point for all SEM calls
185 * In Darwin this is no longer the entry point. It will be removed after
186 * the code has been tested better.
188 /* XXX actually varargs. */
190 semsys(struct proc
*p
, struct semsys_args
*uap
, register_t
*retval
)
193 /* The individual calls handling the locking now */
195 if (uap
->which
>= sizeof(semcalls
)/sizeof(semcalls
[0]))
197 return ((*semcalls
[uap
->which
])(p
, &uap
->a2
, retval
));
201 * Lock or unlock the entire semaphore facility.
203 * This will probably eventually evolve into a general purpose semaphore
204 * facility status enquiry mechanism (I don't like the "read /dev/kmem"
205 * approach currently taken by ipcs and the amount of info that we want
206 * to be able to extract for ipcs is probably beyond what the capability
207 * of the getkerninfo facility.
209 * At the time that the current version of semconfig was written, ipcs is
210 * the only user of the semconfig facility. It uses it to ensure that the
211 * semaphore facility data structures remain static while it fishes around
216 semconfig(__unused
struct proc
*p
, struct semconfig_args
*uap
, register_t
*retval
)
221 case SEM_CONFIG_FREEZE
:
222 SYSV_SEM_SUBSYS_LOCK();
225 case SEM_CONFIG_THAW
:
226 SYSV_SEM_SUBSYS_UNLOCK();
230 printf("semconfig: unknown flag parameter value (%d) - ignored\n",
241 * Expand the semu array to the given capacity. If the expansion fails
242 * return 0, otherwise return 1.
244 * Assumes we already have the subsystem lock.
247 grow_semu_array(int newSize
)
250 register struct sem_undo
*newSemu
;
251 static boolean_t grow_semu_array_in_progress
= FALSE
;
253 while (grow_semu_array_in_progress
) {
254 msleep(&grow_semu_array_in_progress
, &sysv_sem_subsys_mutex
,
255 PPAUSE
, "grow_semu_array", NULL
);
258 if (newSize
<= seminfo
.semmnu
)
260 if (newSize
> limitseminfo
.semmnu
) /* enforce hard limit */
263 printf("undo structure hard limit of %d reached, requested %d\n",
264 limitseminfo
.semmnu
, newSize
);
268 newSize
= (newSize
/SEMMNU_INC
+ 1) * SEMMNU_INC
;
269 newSize
= newSize
> limitseminfo
.semmnu
? limitseminfo
.semmnu
: newSize
;
272 printf("growing semu[] from %d to %d\n", seminfo
.semmnu
, newSize
);
274 grow_semu_array_in_progress
= TRUE
;
275 SYSV_SEM_SUBSYS_UNLOCK();
276 MALLOC(newSemu
, struct sem_undo
*, sizeof(struct sem_undo
) * newSize
,
277 M_SYSVSEM
, M_WAITOK
);
278 SYSV_SEM_SUBSYS_LOCK();
279 grow_semu_array_in_progress
= FALSE
;
280 wakeup((caddr_t
) &grow_semu_array_in_progress
);
284 printf("allocation failed. no changes made.\n");
289 /* Initialize our structure. */
290 for (i
= 0; i
< seminfo
.semmnu
; i
++)
292 newSemu
[i
] = semu
[i
];
294 for (i
= seminfo
.semmnu
; i
< newSize
; i
++)
296 newSemu
[i
].un_proc
= NULL
;
299 /* Clean up the old array */
301 FREE(semu
, M_SYSVSEM
);
304 seminfo
.semmnu
= newSize
;
306 printf("expansion successful\n");
312 * Expand the sema array to the given capacity. If the expansion fails
313 * we return 0, otherwise we return 1.
315 * Assumes we already have the subsystem lock.
318 grow_sema_array(int newSize
)
320 register struct user_semid_ds
*newSema
;
323 if (newSize
<= seminfo
.semmni
)
325 if (newSize
> limitseminfo
.semmni
) /* enforce hard limit */
328 printf("identifier hard limit of %d reached, requested %d\n",
329 limitseminfo
.semmni
, newSize
);
333 newSize
= (newSize
/SEMMNI_INC
+ 1) * SEMMNI_INC
;
334 newSize
= newSize
> limitseminfo
.semmni
? limitseminfo
.semmni
: newSize
;
337 printf("growing sema[] from %d to %d\n", seminfo
.semmni
, newSize
);
339 MALLOC(newSema
, struct user_semid_ds
*, sizeof(struct user_semid_ds
) * newSize
,
340 M_SYSVSEM
, M_WAITOK
);
344 printf("allocation failed. no changes made.\n");
349 /* Initialize our new ids, and copy over the old ones */
350 for (i
= 0; i
< seminfo
.semmni
; i
++)
352 newSema
[i
] = sema
[i
];
353 /* This is a hack. What we really want to be able to
354 * do is change the value a process is waiting on
355 * without waking it up, but I don't know how to do
356 * this with the existing code, so we wake up the
357 * process and let it do a lot of work to determine the
358 * semaphore set is really not available yet, and then
359 * sleep on the correct, reallocated user_semid_ds pointer.
361 if (sema
[i
].sem_perm
.mode
& SEM_ALLOC
)
362 wakeup((caddr_t
)&sema
[i
]);
365 for (i
= seminfo
.semmni
; i
< newSize
; i
++)
367 newSema
[i
].sem_base
= NULL
;
368 newSema
[i
].sem_perm
.mode
= 0;
371 /* Clean up the old array */
373 FREE(sema
, M_SYSVSEM
);
376 seminfo
.semmni
= newSize
;
378 printf("expansion successful\n");
384 * Expand the sem_pool array to the given capacity. If the expansion fails
385 * we return 0 (fail), otherwise we return 1 (success).
387 * Assumes we already hold the subsystem lock.
390 grow_sem_pool(int new_pool_size
)
392 struct sem
*new_sem_pool
= NULL
;
393 struct sem
*sem_free
;
396 if (new_pool_size
< semtot
)
398 /* enforce hard limit */
399 if (new_pool_size
> limitseminfo
.semmns
) {
401 printf("semaphore hard limit of %d reached, requested %d\n",
402 limitseminfo
.semmns
, new_pool_size
);
407 new_pool_size
= (new_pool_size
/SEMMNS_INC
+ 1) * SEMMNS_INC
;
408 new_pool_size
= new_pool_size
> limitseminfo
.semmns
? limitseminfo
.semmns
: new_pool_size
;
411 printf("growing sem_pool array from %d to %d\n", seminfo
.semmns
, new_pool_size
);
413 MALLOC(new_sem_pool
, struct sem
*, sizeof(struct sem
) * new_pool_size
,
414 M_SYSVSEM
, M_WAITOK
);
415 if (NULL
== new_sem_pool
) {
417 printf("allocation failed. no changes made.\n");
422 /* We have our new memory, now copy the old contents over */
424 for(i
= 0; i
< seminfo
.semmns
; i
++)
425 new_sem_pool
[i
] = sem_pool
[i
];
427 /* Update our id structures to point to the new semaphores */
428 for(i
= 0; i
< seminfo
.semmni
; i
++) {
429 if (sema
[i
].sem_perm
.mode
& SEM_ALLOC
) /* ID in use */
430 sema
[i
].sem_base
+= (new_sem_pool
- sem_pool
);
434 sem_pool
= new_sem_pool
;
436 /* clean up the old array */
437 if (sem_free
!= NULL
)
438 FREE(sem_free
, M_SYSVSEM
);
440 seminfo
.semmns
= new_pool_size
;
442 printf("expansion complete\n");
448 * Allocate a new sem_undo structure for a process
449 * (returns ptr to structure or NULL if no more room)
451 * Assumes we already hold the subsystem lock.
454 static struct sem_undo
*
455 semu_alloc(struct proc
*p
)
458 register struct sem_undo
*suptr
;
459 register struct sem_undo
**supptr
;
463 * Try twice to allocate something.
464 * (we'll purge any empty structures after the first pass so
465 * two passes are always enough)
468 for (attempt
= 0; attempt
< 2; attempt
++) {
470 * Look for a free structure.
471 * Fill it in and return it if we find one.
474 for (i
= 0; i
< seminfo
.semmnu
; i
++) {
476 if (suptr
->un_proc
== NULL
) {
477 suptr
->un_next
= semu_list
;
480 suptr
->un_ent
= NULL
;
487 * We didn't find a free one, if this is the first attempt
488 * then try to free some structures.
492 /* All the structures are in use - try to free some */
493 int did_something
= 0;
496 while ((suptr
= *supptr
) != NULL
) {
497 if (suptr
->un_cnt
== 0) {
498 suptr
->un_proc
= NULL
;
499 *supptr
= suptr
->un_next
;
502 supptr
= &(suptr
->un_next
);
505 /* If we didn't free anything. Try expanding
506 * the semu[] array. If that doesn't work
507 * then fail. We expand last to get the
508 * most reuse out of existing resources.
511 if (!grow_semu_array(seminfo
.semmnu
+ 1))
515 * The second pass failed even though we freed
516 * something after the first pass!
517 * This is IMPOSSIBLE!
519 panic("semu_alloc - second attempt failed");
526 * Adjust a particular entry for a particular proc
528 * Assumes we already hold the subsystem lock.
531 semundo_adjust(struct proc
*p
, struct sem_undo
**supptr
, int semid
,
532 int semnum
, int adjval
)
534 register struct sem_undo
*suptr
;
535 register struct undo
*sueptr
, **suepptr
, *new_sueptr
;
538 /* Look for and remember the sem_undo if the caller doesn't provide
543 for (suptr
= semu_list
; suptr
!= NULL
;
544 suptr
= suptr
->un_next
) {
545 if (suptr
->un_proc
== p
) {
553 suptr
= semu_alloc(p
);
561 * Look for the requested entry and adjust it (delete if adjval becomes
566 for (i
= 0, suepptr
= &suptr
->un_ent
, sueptr
= suptr
->un_ent
;
568 i
++, suepptr
= &sueptr
->une_next
, sueptr
= sueptr
->une_next
) {
569 if (sueptr
->une_id
!= semid
|| sueptr
->une_num
!= semnum
)
572 sueptr
->une_adjval
= 0;
574 sueptr
->une_adjval
+= adjval
;
575 if (sueptr
->une_adjval
== 0) {
577 *suepptr
= sueptr
->une_next
;
578 FREE(sueptr
, M_SYSVSEM
);
581 if (new_sueptr
!= NULL
) {
583 * We lost the race: free the "undo" entry we allocated
584 * and use the one that won.
586 FREE(new_sueptr
, M_SYSVSEM
);
592 /* Didn't find the right entry - create it */
594 if (new_sueptr
!= NULL
) {
595 FREE(new_sueptr
, M_SYSVSEM
);
601 if (new_sueptr
!= NULL
) {
603 * Use the new "undo" entry we allocated in the previous pass
605 new_sueptr
->une_next
= suptr
->un_ent
;
606 suptr
->un_ent
= new_sueptr
;
608 new_sueptr
->une_adjval
= adjval
;
609 new_sueptr
->une_id
= semid
;
610 new_sueptr
->une_num
= semnum
;
614 if (suptr
->un_cnt
!= limitseminfo
.semume
) {
615 SYSV_SEM_SUBSYS_UNLOCK();
617 * Unlocking opens the door to race conditions. Someone else
618 * could be trying to allocate the same thing at this point,
619 * so we'll have to check if we lost the race.
621 MALLOC(new_sueptr
, struct undo
*, sizeof (struct undo
),
622 M_SYSVSEM
, M_WAITOK
);
623 SYSV_SEM_SUBSYS_LOCK();
624 if (new_sueptr
== NULL
) {
628 * There might be other threads doing the same thing for this
629 * process, so check again if an "undo" entry exists for that
638 /* Assumes we already hold the subsystem lock.
641 semundo_clear(int semid
, int semnum
)
643 struct sem_undo
*suptr
;
645 for (suptr
= semu_list
; suptr
!= NULL
; suptr
= suptr
->un_next
) {
647 struct undo
**suepptr
;
650 sueptr
= suptr
->un_ent
;
651 suepptr
= &suptr
->un_ent
;
652 while (i
< suptr
->un_cnt
) {
653 if (sueptr
->une_id
== semid
) {
654 if (semnum
== -1 || sueptr
->une_num
== semnum
) {
656 *suepptr
= sueptr
->une_next
;
657 FREE(sueptr
, M_SYSVSEM
);
665 suepptr
= &sueptr
->une_next
;
666 sueptr
= sueptr
->une_next
;
672 * Note that the user-mode half of this passes a union coerced to a
673 * user_addr_t. The union contains either an int or a pointer, and
674 * so we have to coerce it back, variant on whether the calling
675 * process is 64 bit or not. The coercion works for the 'val' element
676 * because the alignment is the same in user and kernel space.
679 semctl(struct proc
*p
, struct semctl_args
*uap
, register_t
*retval
)
681 int semid
= uap
->semid
;
682 int semnum
= uap
->semnum
;
684 user_semun_t user_arg
= (user_semun_t
)uap
->arg
;
685 kauth_cred_t cred
= kauth_cred_get();
687 struct user_semid_ds sbuf
;
688 struct user_semid_ds
*semaptr
;
689 struct user_semid_ds uds
;
692 AUDIT_ARG(svipc_cmd
, cmd
);
693 AUDIT_ARG(svipc_id
, semid
);
695 SYSV_SEM_SUBSYS_LOCK();
698 printf("call to semctl(%d, %d, %d, 0x%qx)\n", semid
, semnum
, cmd
, user_arg
);
701 semid
= IPCID_TO_IX(semid
);
703 if (semid
< 0 || semid
>= seminfo
.semmni
) {
705 printf("Invalid semid\n");
711 semaptr
= &sema
[semid
];
712 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ||
713 semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
)) {
723 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_M
)))
726 semaptr
->sem_perm
.cuid
= kauth_cred_getuid(cred
);
727 semaptr
->sem_perm
.uid
= kauth_cred_getuid(cred
);
728 semtot
-= semaptr
->sem_nsems
;
729 for (i
= semaptr
->sem_base
- sem_pool
; i
< semtot
; i
++)
730 sem_pool
[i
] = sem_pool
[i
+ semaptr
->sem_nsems
];
731 for (i
= 0; i
< seminfo
.semmni
; i
++) {
732 if ((sema
[i
].sem_perm
.mode
& SEM_ALLOC
) &&
733 sema
[i
].sem_base
> semaptr
->sem_base
)
734 sema
[i
].sem_base
-= semaptr
->sem_nsems
;
736 semaptr
->sem_perm
.mode
= 0;
737 semundo_clear(semid
, -1);
738 wakeup((caddr_t
)semaptr
);
742 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_M
)))
745 SYSV_SEM_SUBSYS_UNLOCK();
747 if (IS_64BIT_PROCESS(p
)) {
748 eval
= copyin(user_arg
.buf
, &sbuf
, sizeof(struct user_semid_ds
));
750 eval
= copyin(user_arg
.buf
, &sbuf
, sizeof(struct semid_ds
));
751 /* convert in place; ugly, but safe */
752 semid_ds_32to64((struct semid_ds
*)&sbuf
, &sbuf
);
758 SYSV_SEM_SUBSYS_LOCK();
760 semaptr
->sem_perm
.uid
= sbuf
.sem_perm
.uid
;
761 semaptr
->sem_perm
.gid
= sbuf
.sem_perm
.gid
;
762 semaptr
->sem_perm
.mode
= (semaptr
->sem_perm
.mode
& ~0777) |
763 (sbuf
.sem_perm
.mode
& 0777);
764 semaptr
->sem_ctime
= sysv_semtime();
768 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
770 bcopy(semaptr
, &uds
, sizeof(struct user_semid_ds
));
771 SYSV_SEM_SUBSYS_UNLOCK();
772 if (IS_64BIT_PROCESS(p
)) {
773 eval
= copyout(&uds
, user_arg
.buf
, sizeof(struct user_semid_ds
));
775 struct semid_ds semid_ds32
;
776 semid_ds_64to32(&uds
, &semid_ds32
);
777 eval
= copyout(&semid_ds32
, user_arg
.buf
, sizeof(struct semid_ds
));
779 SYSV_SEM_SUBSYS_LOCK();
783 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
785 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
) {
789 rval
= semaptr
->sem_base
[semnum
].semncnt
;
793 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
795 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
) {
799 rval
= semaptr
->sem_base
[semnum
].sempid
;
803 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
805 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
) {
809 rval
= semaptr
->sem_base
[semnum
].semval
;
813 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
815 /* XXXXXXXXXXXXXXXX TBD XXXXXXXXXXXXXXXX */
816 for (i
= 0; i
< semaptr
->sem_nsems
; i
++) {
817 /* XXX could be done in one go... */
818 eval
= copyout((caddr_t
)&semaptr
->sem_base
[i
].semval
,
819 user_arg
.array
+ (i
* sizeof(unsigned short)),
820 sizeof(unsigned short));
827 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
829 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
) {
833 rval
= semaptr
->sem_base
[semnum
].semzcnt
;
837 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_W
)))
840 printf("Invalid credentials for write\n");
844 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
847 printf("Invalid number out of range for set\n");
853 * Cast down a pointer instead of using 'val' member directly
854 * to avoid introducing endieness and a pad field into the
855 * header file. Ugly, but it works.
857 semaptr
->sem_base
[semnum
].semval
= CAST_DOWN(int,user_arg
.buf
);
858 semundo_clear(semid
, semnum
);
859 wakeup((caddr_t
)semaptr
);
863 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_W
)))
865 /*** XXXXXXXXXXXX TBD ********/
866 for (i
= 0; i
< semaptr
->sem_nsems
; i
++) {
867 /* XXX could be done in one go... */
868 eval
= copyin(user_arg
.array
+ (i
* sizeof(unsigned short)),
869 (caddr_t
)&semaptr
->sem_base
[i
].semval
,
870 sizeof(unsigned short));
874 semundo_clear(semid
, -1);
875 wakeup((caddr_t
)semaptr
);
886 SYSV_SEM_SUBSYS_UNLOCK();
891 semget(__unused
struct proc
*p
, struct semget_args
*uap
, register_t
*retval
)
895 int nsems
= uap
->nsems
;
896 int semflg
= uap
->semflg
;
897 kauth_cred_t cred
= kauth_cred_get();
900 if (key
!= IPC_PRIVATE
)
901 printf("semget(0x%x, %d, 0%o)\n", key
, nsems
, semflg
);
903 printf("semget(IPC_PRIVATE, %d, 0%o)\n", nsems
, semflg
);
907 SYSV_SEM_SUBSYS_LOCK();
910 if (key
!= IPC_PRIVATE
) {
911 for (semid
= 0; semid
< seminfo
.semmni
; semid
++) {
912 if ((sema
[semid
].sem_perm
.mode
& SEM_ALLOC
) &&
913 sema
[semid
].sem_perm
.key
== key
)
916 if (semid
< seminfo
.semmni
) {
918 printf("found public key\n");
920 if ((eval
= ipcperm(cred
, &sema
[semid
].sem_perm
,
923 if (nsems
< 0 || sema
[semid
].sem_nsems
< nsems
) {
925 printf("too small\n");
930 if ((semflg
& IPC_CREAT
) && (semflg
& IPC_EXCL
)) {
932 printf("not exclusive\n");
942 printf("need to allocate an id for the request\n");
944 if (key
== IPC_PRIVATE
|| (semflg
& IPC_CREAT
)) {
945 if (nsems
<= 0 || nsems
> limitseminfo
.semmsl
) {
947 printf("nsems out of range (0<%d<=%d)\n", nsems
,
953 if (nsems
> seminfo
.semmns
- semtot
) {
955 printf("not enough semaphores left (need %d, got %d)\n",
956 nsems
, seminfo
.semmns
- semtot
);
958 if (!grow_sem_pool(semtot
+ nsems
)) {
960 printf("failed to grow the sem array\n");
966 for (semid
= 0; semid
< seminfo
.semmni
; semid
++) {
967 if ((sema
[semid
].sem_perm
.mode
& SEM_ALLOC
) == 0)
970 if (semid
== seminfo
.semmni
) {
972 printf("no more id's available\n");
974 if (!grow_sema_array(seminfo
.semmni
+ 1))
977 printf("failed to grow sema array\n");
984 printf("semid %d is available\n", semid
);
986 sema
[semid
].sem_perm
.key
= key
;
987 sema
[semid
].sem_perm
.cuid
= kauth_cred_getuid(cred
);
988 sema
[semid
].sem_perm
.uid
= kauth_cred_getuid(cred
);
989 sema
[semid
].sem_perm
.cgid
= cred
->cr_gid
;
990 sema
[semid
].sem_perm
.gid
= cred
->cr_gid
;
991 sema
[semid
].sem_perm
.mode
= (semflg
& 0777) | SEM_ALLOC
;
992 sema
[semid
].sem_perm
.seq
=
993 (sema
[semid
].sem_perm
.seq
+ 1) & 0x7fff;
994 sema
[semid
].sem_nsems
= nsems
;
995 sema
[semid
].sem_otime
= 0;
996 sema
[semid
].sem_ctime
= sysv_semtime();
997 sema
[semid
].sem_base
= &sem_pool
[semtot
];
999 bzero(sema
[semid
].sem_base
,
1000 sizeof(sema
[semid
].sem_base
[0])*nsems
);
1002 printf("sembase = 0x%x, next = 0x%x\n", sema
[semid
].sem_base
,
1007 printf("didn't find it and wasn't asked to create it\n");
1014 *retval
= IXSEQ_TO_IPCID(semid
, sema
[semid
].sem_perm
);
1015 AUDIT_ARG(svipc_id
, *retval
);
1017 printf("semget is done, returning %d\n", *retval
);
1022 SYSV_SEM_SUBSYS_UNLOCK();
1027 semop(struct proc
*p
, struct semop_args
*uap
, register_t
*retval
)
1029 int semid
= uap
->semid
;
1030 int nsops
= uap
->nsops
;
1031 struct sembuf sops
[MAX_SOPS
];
1032 register struct user_semid_ds
*semaptr
;
1033 register struct sembuf
*sopptr
= NULL
; /* protected by 'semptr' */
1034 register struct sem
*semptr
= NULL
; /* protected by 'if' */
1035 struct sem_undo
*suptr
= NULL
;
1037 int do_wakeup
, do_undos
;
1039 AUDIT_ARG(svipc_id
, uap
->semid
);
1041 SYSV_SEM_SUBSYS_LOCK();
1044 printf("call to semop(%d, 0x%x, %d)\n", semid
, sops
, nsops
);
1047 semid
= IPCID_TO_IX(semid
); /* Convert back to zero origin */
1049 if (semid
< 0 || semid
>= seminfo
.semmni
) {
1054 semaptr
= &sema
[semid
];
1055 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0) {
1059 if (semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
)) {
1064 if ((eval
= ipcperm(kauth_cred_get(), &semaptr
->sem_perm
, IPC_W
))) {
1066 printf("eval = %d from ipaccess\n", eval
);
1071 if (nsops
< 0 || nsops
> MAX_SOPS
) {
1073 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS
, nsops
);
1079 /* OK for LP64, since sizeof(struct sembuf) is currently invariant */
1080 if ((eval
= copyin(uap
->sops
, &sops
, nsops
* sizeof(struct sembuf
))) != 0) {
1082 printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval
,
1083 uap
->sops
, &sops
, nsops
* sizeof(struct sembuf
));
1089 * Loop trying to satisfy the vector of requests.
1090 * If we reach a point where we must wait, any requests already
1091 * performed are rolled back and we go to sleep until some other
1092 * process wakes us up. At this point, we start all over again.
1094 * This ensures that from the perspective of other tasks, a set
1095 * of requests is atomic (never partially satisfied).
1102 for (i
= 0; i
< nsops
; i
++) {
1105 if (sopptr
->sem_num
>= semaptr
->sem_nsems
) {
1110 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1113 printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
1114 semaptr
, semaptr
->sem_base
, semptr
,
1115 sopptr
->sem_num
, semptr
->semval
, sopptr
->sem_op
,
1116 (sopptr
->sem_flg
& IPC_NOWAIT
) ? "nowait" : "wait");
1119 if (sopptr
->sem_op
< 0) {
1120 if (semptr
->semval
+ sopptr
->sem_op
< 0) {
1122 printf("semop: can't do it now\n");
1126 semptr
->semval
+= sopptr
->sem_op
;
1127 if (semptr
->semval
== 0 &&
1128 semptr
->semzcnt
> 0)
1131 if (sopptr
->sem_flg
& SEM_UNDO
)
1133 } else if (sopptr
->sem_op
== 0) {
1134 if (semptr
->semval
> 0) {
1136 printf("semop: not zero now\n");
1141 if (semptr
->semncnt
> 0)
1143 semptr
->semval
+= sopptr
->sem_op
;
1144 if (sopptr
->sem_flg
& SEM_UNDO
)
1150 * Did we get through the entire vector?
1156 * No ... rollback anything that we've already done
1159 printf("semop: rollback 0 through %d\n", i
-1);
1161 for (j
= 0; j
< i
; j
++)
1162 semaptr
->sem_base
[sops
[j
].sem_num
].semval
-=
1166 * If the request that we couldn't satisfy has the
1167 * NOWAIT flag set then return with EAGAIN.
1169 if (sopptr
->sem_flg
& IPC_NOWAIT
) {
1174 if (sopptr
->sem_op
== 0)
1180 printf("semop: good night!\n");
1182 /* Release our lock on the semaphore subsystem so
1183 * another thread can get at the semaphore we are
1184 * waiting for. We will get the lock back after we
1187 eval
= msleep((caddr_t
)semaptr
, &sysv_sem_subsys_mutex
, (PZERO
- 4) | PCATCH
,
1191 printf("semop: good morning (eval=%d)!\n", eval
);
1193 /* we need the lock here due to mods on semptr */
1195 if (sopptr
->sem_op
== 0)
1204 suptr
= NULL
; /* sem_undo may have been reallocated */
1205 semaptr
= &sema
[semid
]; /* sema may have been reallocated */
1209 printf("semop: good morning!\n");
1213 * Make sure that the semaphore still exists
1215 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ||
1216 semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
)) {
1217 /* The man page says to return EIDRM. */
1218 /* Unfortunately, BSD doesn't define that code! */
1219 if (sopptr
->sem_op
== 0)
1232 * The semaphore is still alive. Readjust the count of
1233 * waiting processes. semptr needs to be recomputed
1234 * because the sem[] may have been reallocated while
1235 * we were sleeping, updating our sem_base pointer.
1237 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1238 if (sopptr
->sem_op
== 0)
1246 * Process any SEM_UNDO requests.
1249 for (i
= 0; i
< nsops
; i
++) {
1251 * We only need to deal with SEM_UNDO's for non-zero
1256 if ((sops
[i
].sem_flg
& SEM_UNDO
) == 0)
1258 adjval
= sops
[i
].sem_op
;
1261 eval
= semundo_adjust(p
, &suptr
, semid
,
1262 sops
[i
].sem_num
, -adjval
);
1267 * Oh-Oh! We ran out of either sem_undo's or undo's.
1268 * Rollback the adjustments to this point and then
1269 * rollback the semaphore ups and down so we can return
1270 * with an error with all structures restored. We
1271 * rollback the undo's in the exact reverse order that
1272 * we applied them. This guarantees that we won't run
1273 * out of space as we roll things back out.
1275 for (j
= i
- 1; j
>= 0; j
--) {
1276 if ((sops
[j
].sem_flg
& SEM_UNDO
) == 0)
1278 adjval
= sops
[j
].sem_op
;
1281 if (semundo_adjust(p
, &suptr
, semid
,
1282 sops
[j
].sem_num
, adjval
) != 0)
1283 panic("semop - can't undo undos");
1286 for (j
= 0; j
< nsops
; j
++)
1287 semaptr
->sem_base
[sops
[j
].sem_num
].semval
-=
1291 printf("eval = %d from semundo_adjust\n", eval
);
1294 } /* loop through the sops */
1295 } /* if (do_undos) */
1297 /* We're definitely done - set the sempid's */
1298 for (i
= 0; i
< nsops
; i
++) {
1300 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1301 semptr
->sempid
= p
->p_pid
;
1306 printf("semop: doing wakeup\n");
1308 sem_wakeup((caddr_t
)semaptr
);
1310 wakeup((caddr_t
)semaptr
);
1312 printf("semop: back from wakeup\n");
1314 wakeup((caddr_t
)semaptr
);
1318 printf("semop: done\n");
1323 SYSV_SEM_SUBSYS_UNLOCK();
1328 * Go through the undo structures for this process and apply the adjustments to
1332 semexit(struct proc
*p
)
1334 register struct sem_undo
*suptr
;
1335 register struct sem_undo
**supptr
;
1338 /* If we have not allocated our semaphores yet there can't be
1339 * anything to undo, but we need the lock to prevent
1340 * dynamic memory race conditions.
1342 SYSV_SEM_SUBSYS_LOCK();
1346 SYSV_SEM_SUBSYS_UNLOCK();
1352 * Go through the chain of undo vectors looking for one
1353 * associated with this process.
1356 for (supptr
= &semu_list
; (suptr
= *supptr
) != NULL
;
1357 supptr
= &suptr
->un_next
) {
1358 if (suptr
->un_proc
== p
)
1366 printf("proc @%08x has undo structure with %d entries\n", p
,
1371 * If there are any active undo elements then process them.
1373 if (suptr
->un_cnt
> 0) {
1374 while (suptr
->un_ent
!= NULL
) {
1375 struct undo
*sueptr
;
1379 struct user_semid_ds
*semaptr
;
1381 sueptr
= suptr
->un_ent
;
1382 semid
= sueptr
->une_id
;
1383 semnum
= sueptr
->une_num
;
1384 adjval
= sueptr
->une_adjval
;
1386 semaptr
= &sema
[semid
];
1387 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0)
1388 panic("semexit - semid not allocated");
1389 if (semnum
>= semaptr
->sem_nsems
)
1390 panic("semexit - semnum out of range");
1393 printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",
1398 semaptr
->sem_base
[semnum
].semval
);
1402 if (semaptr
->sem_base
[semnum
].semval
< -adjval
)
1403 semaptr
->sem_base
[semnum
].semval
= 0;
1405 semaptr
->sem_base
[semnum
].semval
+=
1408 semaptr
->sem_base
[semnum
].semval
+= adjval
;
1410 /* Maybe we should build a list of semaptr's to wake
1411 * up, finish all access to data structures, release the
1412 * subsystem lock, and wake all the processes. Something
1413 * to think about. It wouldn't buy us anything unless
1414 * wakeup had the potential to block, or the syscall
1415 * funnel state was changed to allow multiple threads
1416 * in the BSD code at once.
1419 sem_wakeup((caddr_t
)semaptr
);
1421 wakeup((caddr_t
)semaptr
);
1424 printf("semexit: back from wakeup\n");
1427 suptr
->un_ent
= sueptr
->une_next
;
1428 FREE(sueptr
, M_SYSVSEM
);
1434 * Deallocate the undo vector.
1437 printf("removing vector\n");
1439 suptr
->un_proc
= NULL
;
1440 *supptr
= suptr
->un_next
;
1444 * There is a semaphore leak (i.e. memory leak) in this code.
1445 * We should be deleting the IPC_PRIVATE semaphores when they are
1446 * no longer needed, and we dont. We would have to track which processes
1447 * know about which IPC_PRIVATE semaphores, updating the list after
1448 * every fork. We can't just delete them semaphore when the process
1449 * that created it dies, because that process may well have forked
1450 * some children. So we need to wait until all of it's children have
1451 * died, and so on. Maybe we should tag each IPC_PRIVATE sempahore
1452 * with the creating group ID, count the number of processes left in
1453 * that group, and delete the semaphore when the group is gone.
1454 * Until that code gets implemented we will leak IPC_PRIVATE semaphores.
1455 * There is an upper bound on the size of our semaphore array, so
1456 * leaking the semaphores should not work as a DOS attack.
1458 * Please note that the original BSD code this file is based on had the
1459 * same leaky semaphore problem.
1462 SYSV_SEM_SUBSYS_UNLOCK();
1466 /* (struct sysctl_oid *oidp, void *arg1, int arg2, \
1467 struct sysctl_req *req) */
1469 sysctl_seminfo(__unused
struct sysctl_oid
*oidp
, void *arg1
,
1470 __unused
int arg2
, struct sysctl_req
*req
)
1474 error
= SYSCTL_OUT(req
, arg1
, sizeof(int));
1475 if (error
|| req
->newptr
== USER_ADDR_NULL
)
1478 SYSV_SEM_SUBSYS_LOCK();
1480 /* Set the values only if shared memory is not initialised */
1481 if ((sem_pool
== NULL
) &&
1484 (semu_list
== NULL
)) {
1485 if ((error
= SYSCTL_IN(req
, arg1
, sizeof(int)))) {
1491 SYSV_SEM_SUBSYS_UNLOCK();
1496 /* SYSCTL_NODE(_kern, KERN_SYSV, sysv, CTLFLAG_RW, 0, "SYSV"); */
1497 extern struct sysctl_oid_list sysctl__kern_sysv_children
;
1498 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMMNI
, semmni
, CTLTYPE_INT
| CTLFLAG_RW
,
1499 &limitseminfo
.semmni
, 0, &sysctl_seminfo
,"I","semmni");
1501 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMMNS
, semmns
, CTLTYPE_INT
| CTLFLAG_RW
,
1502 &limitseminfo
.semmns
, 0, &sysctl_seminfo
,"I","semmns");
1504 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMMNU
, semmnu
, CTLTYPE_INT
| CTLFLAG_RW
,
1505 &limitseminfo
.semmnu
, 0, &sysctl_seminfo
,"I","semmnu");
1507 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMMSL
, semmsl
, CTLTYPE_INT
| CTLFLAG_RW
,
1508 &limitseminfo
.semmsl
, 0, &sysctl_seminfo
,"I","semmsl");
1510 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMUNE
, semume
, CTLTYPE_INT
| CTLFLAG_RW
,
1511 &limitseminfo
.semume
, 0, &sysctl_seminfo
,"I","semume");
1515 IPCS_sem_sysctl(__unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
1516 __unused
int arg2
, struct sysctl_req
*req
)
1521 struct IPCS_command u32
;
1522 struct user_IPCS_command u64
;
1524 struct semid_ds semid_ds32
; /* post conversion, 32 bit version */
1526 size_t ipcs_sz
= sizeof(struct user_IPCS_command
);
1527 size_t semid_ds_sz
= sizeof(struct user_semid_ds
);
1528 struct proc
*p
= current_proc();
1530 /* Copy in the command structure */
1531 if ((error
= SYSCTL_IN(req
, &ipcs
, ipcs_sz
)) != 0) {
1535 if (!IS_64BIT_PROCESS(p
)) {
1536 ipcs_sz
= sizeof(struct IPCS_command
);
1537 semid_ds_sz
= sizeof(struct semid_ds
);
1540 /* Let us version this interface... */
1541 if (ipcs
.u64
.ipcs_magic
!= IPCS_MAGIC
) {
1545 SYSV_SEM_SUBSYS_LOCK();
1546 switch(ipcs
.u64
.ipcs_op
) {
1547 case IPCS_SEM_CONF
: /* Obtain global configuration data */
1548 if (ipcs
.u64
.ipcs_datalen
!= sizeof(struct seminfo
)) {
1552 if (ipcs
.u64
.ipcs_cursor
!= 0) { /* fwd. compat. */
1556 SYSV_SEM_SUBSYS_UNLOCK();
1557 error
= copyout(&seminfo
, ipcs
.u64
.ipcs_data
, ipcs
.u64
.ipcs_datalen
);
1558 SYSV_SEM_SUBSYS_LOCK();
1561 case IPCS_SEM_ITER
: /* Iterate over existing segments */
1562 cursor
= ipcs
.u64
.ipcs_cursor
;
1563 if (cursor
< 0 || cursor
>= seminfo
.semmni
) {
1567 if (ipcs
.u64
.ipcs_datalen
!= (int)semid_ds_sz
) {
1571 for( ; cursor
< seminfo
.semmni
; cursor
++) {
1572 if (sema
[cursor
].sem_perm
.mode
& SEM_ALLOC
)
1576 if (cursor
== seminfo
.semmni
) {
1581 semid_dsp
= &sema
[cursor
]; /* default: 64 bit */
1584 * If necessary, convert the 64 bit kernel segment
1585 * descriptor to a 32 bit user one.
1587 if (!IS_64BIT_PROCESS(p
)) {
1588 semid_ds_64to32(semid_dsp
, &semid_ds32
);
1589 semid_dsp
= &semid_ds32
;
1591 SYSV_SEM_SUBSYS_UNLOCK();
1592 error
= copyout(semid_dsp
, ipcs
.u64
.ipcs_data
, ipcs
.u64
.ipcs_datalen
);
1595 ipcs
.u64
.ipcs_cursor
= cursor
+ 1;
1596 error
= SYSCTL_OUT(req
, &ipcs
, ipcs_sz
);
1598 SYSV_SEM_SUBSYS_LOCK();
1605 SYSV_SEM_SUBSYS_UNLOCK();
1609 SYSCTL_DECL(_kern_sysv_ipcs
);
1610 SYSCTL_PROC(_kern_sysv_ipcs
, OID_AUTO
, sem
, CTLFLAG_RW
|CTLFLAG_ANYBODY
,
1611 0, 0, IPCS_sem_sysctl
,
1612 "S,IPCS_sem_command",
1613 "ipcs sem command interface");