2 * Implementation of SVID semaphores
4 * Author: Daniel Boulet
6 * This software is provided ``AS IS'' without any warranties of any kind.
9 * Copyright (c) 2003-2005 McAfee, Inc.
10 * All rights reserved.
12 * This software was developed for the FreeBSD Project in part by McAfee
13 * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
14 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution.
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
42 #include "opt_sysvipc.h"
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/sysproto.h>
48 #include <sys/eventhandler.h>
49 #include <sys/kernel.h>
52 #include <sys/module.h>
53 #include <sys/mutex.h>
55 #include <sys/syscall.h>
56 #include <sys/syscallsubr.h>
57 #include <sys/sysent.h>
58 #include <sys/sysctl.h>
60 #include <sys/malloc.h>
63 #include <security/mac/mac_framework.h>
65 static MALLOC_DEFINE(M_SEM
, "sem", "SVID compatible semaphores");
68 #define DPRINTF(a) printf a
73 static void seminit(void);
74 static int sysvsem_modload(struct module
*, int, void *);
75 static int semunload(void);
76 static void semexit_myhook(void *arg
, struct proc
*p
);
77 static int sysctl_sema(SYSCTL_HANDLER_ARGS
);
78 static int semvalid(int semid
, struct semid_kernel
*semakptr
);
80 #ifndef _SYS_SYSPROTO_H_
82 int __semctl(struct thread
*td
, struct __semctl_args
*uap
);
84 int semget(struct thread
*td
, struct semget_args
*uap
);
86 int semop(struct thread
*td
, struct semop_args
*uap
);
89 static struct sem_undo
*semu_alloc(struct thread
*td
);
90 static int semundo_adjust(struct thread
*td
, struct sem_undo
**supptr
,
91 int semid
, int semnum
, int adjval
);
92 static void semundo_clear(int semid
, int semnum
);
94 /* XXX casting to (sy_call_t *) is bogus, as usual. */
95 static sy_call_t
*semcalls
[] = {
96 (sy_call_t
*)__semctl
, (sy_call_t
*)semget
,
100 static struct mtx sem_mtx
; /* semaphore global lock */
101 static int semtot
= 0;
102 static struct semid_kernel
*sema
; /* semaphore id pool */
103 static struct mtx
*sema_mtx
; /* semaphore id pool mutexes*/
104 static struct sem
*sem
; /* semaphore pool */
105 SLIST_HEAD(, sem_undo
) semu_list
; /* list of active undo structures */
106 static int *semu
; /* undo structure pool */
107 static eventhandler_tag semexit_tag
;
109 #define SEMUNDO_MTX sem_mtx
110 #define SEMUNDO_LOCK() mtx_lock(&SEMUNDO_MTX);
111 #define SEMUNDO_UNLOCK() mtx_unlock(&SEMUNDO_MTX);
112 #define SEMUNDO_LOCKASSERT(how) mtx_assert(&SEMUNDO_MTX, (how));
115 u_short semval
; /* semaphore value */
116 pid_t sempid
; /* pid of last operation */
117 u_short semncnt
; /* # awaiting semval > cval */
118 u_short semzcnt
; /* # awaiting semval = 0 */
122 * Undo structure (one per process)
125 SLIST_ENTRY(sem_undo
) un_next
; /* ptr to next active undo structure */
126 struct proc
*un_proc
; /* owner of this structure */
127 short un_cnt
; /* # of active entries */
129 short un_adjval
; /* adjust on exit values */
130 short un_num
; /* semaphore # */
131 int un_id
; /* semid */
132 } un_ent
[1]; /* undo entries */
136 * Configuration parameters
139 #define SEMMNI 10 /* # of semaphore identifiers */
142 #define SEMMNS 60 /* # of semaphores in system */
145 #define SEMUME 10 /* max # of undo entries per process */
148 #define SEMMNU 30 /* # of undo structures in system */
151 /* shouldn't need tuning */
153 #define SEMMAP 30 /* # of entries in semaphore map */
156 #define SEMMSL SEMMNS /* max # of semaphores per id */
159 #define SEMOPM 100 /* max # of operations per semop call */
162 #define SEMVMX 32767 /* semaphore maximum value */
163 #define SEMAEM 16384 /* adjust on exit max value */
166 * Due to the way semaphore memory is allocated, we have to ensure that
167 * SEMUSZ is properly aligned.
170 #define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1))
172 /* actual size of an undo structure */
173 #define SEMUSZ SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME]))
176 * Macro to find a particular sem_undo vector
179 ((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz))
182 * semaphore info struct
184 struct seminfo seminfo
= {
185 SEMMAP
, /* # of entries in semaphore map */
186 SEMMNI
, /* # of semaphore identifiers */
187 SEMMNS
, /* # of semaphores in system */
188 SEMMNU
, /* # of undo structures in system */
189 SEMMSL
, /* max # of semaphores per id */
190 SEMOPM
, /* max # of operations per semop call */
191 SEMUME
, /* max # of undo entries per process */
192 SEMUSZ
, /* size in bytes of undo structure */
193 SEMVMX
, /* semaphore maximum value */
194 SEMAEM
/* adjust on exit max value */
197 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semmap
, CTLFLAG_RW
, &seminfo
.semmap
, 0,
198 "Number of entries in the semaphore map");
199 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semmni
, CTLFLAG_RDTUN
, &seminfo
.semmni
, 0,
200 "Number of semaphore identifiers");
201 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semmns
, CTLFLAG_RDTUN
, &seminfo
.semmns
, 0,
202 "Maximum number of semaphores in the system");
203 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semmnu
, CTLFLAG_RDTUN
, &seminfo
.semmnu
, 0,
204 "Maximum number of undo structures in the system");
205 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semmsl
, CTLFLAG_RW
, &seminfo
.semmsl
, 0,
206 "Max semaphores per id");
207 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semopm
, CTLFLAG_RDTUN
, &seminfo
.semopm
, 0,
208 "Max operations per semop call");
209 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semume
, CTLFLAG_RDTUN
, &seminfo
.semume
, 0,
210 "Max undo entries per process");
211 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semusz
, CTLFLAG_RDTUN
, &seminfo
.semusz
, 0,
212 "Size in bytes of undo structure");
213 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semvmx
, CTLFLAG_RW
, &seminfo
.semvmx
, 0,
214 "Semaphore maximum value");
215 SYSCTL_INT(_kern_ipc
, OID_AUTO
, semaem
, CTLFLAG_RW
, &seminfo
.semaem
, 0,
216 "Adjust on exit max value");
217 SYSCTL_PROC(_kern_ipc
, OID_AUTO
, sema
, CTLFLAG_RD
,
218 NULL
, 0, sysctl_sema
, "", "");
225 TUNABLE_INT_FETCH("kern.ipc.semmap", &seminfo
.semmap
);
226 TUNABLE_INT_FETCH("kern.ipc.semmni", &seminfo
.semmni
);
227 TUNABLE_INT_FETCH("kern.ipc.semmns", &seminfo
.semmns
);
228 TUNABLE_INT_FETCH("kern.ipc.semmnu", &seminfo
.semmnu
);
229 TUNABLE_INT_FETCH("kern.ipc.semmsl", &seminfo
.semmsl
);
230 TUNABLE_INT_FETCH("kern.ipc.semopm", &seminfo
.semopm
);
231 TUNABLE_INT_FETCH("kern.ipc.semume", &seminfo
.semume
);
232 TUNABLE_INT_FETCH("kern.ipc.semusz", &seminfo
.semusz
);
233 TUNABLE_INT_FETCH("kern.ipc.semvmx", &seminfo
.semvmx
);
234 TUNABLE_INT_FETCH("kern.ipc.semaem", &seminfo
.semaem
);
236 sem
= malloc(sizeof(struct sem
) * seminfo
.semmns
, M_SEM
, M_WAITOK
);
237 sema
= malloc(sizeof(struct semid_kernel
) * seminfo
.semmni
, M_SEM
,
239 sema_mtx
= malloc(sizeof(struct mtx
) * seminfo
.semmni
, M_SEM
,
241 semu
= malloc(seminfo
.semmnu
* seminfo
.semusz
, M_SEM
, M_WAITOK
);
243 for (i
= 0; i
< seminfo
.semmni
; i
++) {
244 sema
[i
].u
.sem_base
= 0;
245 sema
[i
].u
.sem_perm
.mode
= 0;
246 sema
[i
].u
.sem_perm
.seq
= 0;
248 mac_sysvsem_init(&sema
[i
]);
251 for (i
= 0; i
< seminfo
.semmni
; i
++)
252 mtx_init(&sema_mtx
[i
], "semid", NULL
, MTX_DEF
);
253 for (i
= 0; i
< seminfo
.semmnu
; i
++) {
254 struct sem_undo
*suptr
= SEMU(i
);
255 suptr
->un_proc
= NULL
;
257 SLIST_INIT(&semu_list
);
258 mtx_init(&sem_mtx
, "sem", NULL
, MTX_DEF
);
259 semexit_tag
= EVENTHANDLER_REGISTER(process_exit
, semexit_myhook
, NULL
,
260 EVENTHANDLER_PRI_ANY
);
271 EVENTHANDLER_DEREGISTER(process_exit
, semexit_tag
);
273 for (i
= 0; i
< seminfo
.semmni
; i
++)
274 mac_sysvsem_destroy(&sema
[i
]);
279 for (i
= 0; i
< seminfo
.semmni
; i
++)
280 mtx_destroy(&sema_mtx
[i
]);
281 mtx_destroy(&sem_mtx
);
286 sysvsem_modload(struct module
*module
, int cmd
, void *arg
)
306 static moduledata_t sysvsem_mod
= {
312 SYSCALL_MODULE_HELPER(semsys
);
313 SYSCALL_MODULE_HELPER(__semctl
);
314 SYSCALL_MODULE_HELPER(semget
);
315 SYSCALL_MODULE_HELPER(semop
);
317 DECLARE_MODULE(sysvsem
, sysvsem_mod
,
318 SI_SUB_SYSV_SEM
, SI_ORDER_FIRST
);
319 MODULE_VERSION(sysvsem
, 1);
322 * Entry point for all SEM calls.
327 /* XXX actually varargs. */
328 struct semsys_args
/* {
338 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
340 if (uap
->which
< 0 ||
341 uap
->which
>= sizeof(semcalls
)/sizeof(semcalls
[0]))
343 error
= (*semcalls
[uap
->which
])(td
, &uap
->a2
);
348 * Allocate a new sem_undo structure for a process
349 * (returns ptr to structure or NULL if no more room)
352 static struct sem_undo
*
357 struct sem_undo
*suptr
;
358 struct sem_undo
**supptr
;
361 SEMUNDO_LOCKASSERT(MA_OWNED
);
363 * Try twice to allocate something.
364 * (we'll purge an empty structure after the first pass so
365 * two passes are always enough)
368 for (attempt
= 0; attempt
< 2; attempt
++) {
370 * Look for a free structure.
371 * Fill it in and return it if we find one.
374 for (i
= 0; i
< seminfo
.semmnu
; i
++) {
376 if (suptr
->un_proc
== NULL
) {
377 SLIST_INSERT_HEAD(&semu_list
, suptr
, un_next
);
379 suptr
->un_proc
= td
->td_proc
;
385 * We didn't find a free one, if this is the first attempt
386 * then try to free a structure.
390 /* All the structures are in use - try to free one */
391 int did_something
= 0;
393 SLIST_FOREACH_PREVPTR(suptr
, supptr
, &semu_list
,
395 if (suptr
->un_cnt
== 0) {
396 suptr
->un_proc
= NULL
;
398 *supptr
= SLIST_NEXT(suptr
, un_next
);
403 /* If we didn't free anything then just give-up */
408 * The second pass failed even though we freed
409 * something after the first pass!
410 * This is IMPOSSIBLE!
412 panic("semu_alloc - second attempt failed");
419 * Adjust a particular entry for a particular proc
423 semundo_adjust(td
, supptr
, semid
, semnum
, adjval
)
425 struct sem_undo
**supptr
;
429 struct proc
*p
= td
->td_proc
;
430 struct sem_undo
*suptr
;
434 SEMUNDO_LOCKASSERT(MA_OWNED
);
435 /* Look for and remember the sem_undo if the caller doesn't provide
440 SLIST_FOREACH(suptr
, &semu_list
, un_next
) {
441 if (suptr
->un_proc
== p
) {
449 suptr
= semu_alloc(td
);
457 * Look for the requested entry and adjust it (delete if adjval becomes
460 sunptr
= &suptr
->un_ent
[0];
461 for (i
= 0; i
< suptr
->un_cnt
; i
++, sunptr
++) {
462 if (sunptr
->un_id
!= semid
|| sunptr
->un_num
!= semnum
)
465 adjval
+= sunptr
->un_adjval
;
466 if (adjval
> seminfo
.semaem
|| adjval
< -seminfo
.semaem
)
469 sunptr
->un_adjval
= adjval
;
470 if (sunptr
->un_adjval
== 0) {
472 if (i
< suptr
->un_cnt
)
474 suptr
->un_ent
[suptr
->un_cnt
];
479 /* Didn't find the right entry - create it */
482 if (adjval
> seminfo
.semaem
|| adjval
< -seminfo
.semaem
)
484 if (suptr
->un_cnt
!= seminfo
.semume
) {
485 sunptr
= &suptr
->un_ent
[suptr
->un_cnt
];
487 sunptr
->un_adjval
= adjval
;
488 sunptr
->un_id
= semid
; sunptr
->un_num
= semnum
;
495 semundo_clear(semid
, semnum
)
498 struct sem_undo
*suptr
;
500 SEMUNDO_LOCKASSERT(MA_OWNED
);
501 SLIST_FOREACH(suptr
, &semu_list
, un_next
) {
502 struct undo
*sunptr
= &suptr
->un_ent
[0];
505 while (i
< suptr
->un_cnt
) {
506 if (sunptr
->un_id
== semid
) {
507 if (semnum
== -1 || sunptr
->un_num
== semnum
) {
509 if (i
< suptr
->un_cnt
) {
511 suptr
->un_ent
[suptr
->un_cnt
];
524 semvalid(semid
, semakptr
)
526 struct semid_kernel
*semakptr
;
529 return ((semakptr
->u
.sem_perm
.mode
& SEM_ALLOC
) == 0 ||
530 semakptr
->u
.sem_perm
.seq
!= IPCID_TO_SEQ(semid
) ? EINVAL
: 0);
534 * Note that the user-mode half of this passes a union, not a pointer.
536 #ifndef _SYS_SYSPROTO_H_
537 struct __semctl_args
{
547 struct __semctl_args
*uap
;
549 struct semid_ds dsbuf
;
550 union semun arg
, semun
;
561 error
= copyin(uap
->arg
, &arg
, sizeof(arg
));
573 error
= copyin(arg
.buf
, &dsbuf
, sizeof(dsbuf
));
580 semun
.array
= arg
.array
;
587 error
= kern_semctl(td
, uap
->semid
, uap
->semnum
, uap
->cmd
, &semun
,
595 error
= copyout(&dsbuf
, arg
.buf
, sizeof(dsbuf
));
600 td
->td_retval
[0] = rval
;
605 kern_semctl(struct thread
*td
, int semid
, int semnum
, int cmd
,
606 union semun
*arg
, register_t
*rval
)
609 struct ucred
*cred
= td
->td_ucred
;
611 struct semid_ds
*sbuf
;
612 struct semid_kernel
*semakptr
;
613 struct mtx
*sema_mtxp
;
614 u_short usval
, count
;
617 DPRINTF(("call to semctl(%d, %d, %d, 0x%p)\n",
618 semid
, semnum
, cmd
, arg
));
619 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
627 * For this command we assume semid is an array index
628 * rather than an IPC id.
630 if (semid
< 0 || semid
>= seminfo
.semmni
)
632 semakptr
= &sema
[semid
];
633 sema_mtxp
= &sema_mtx
[semid
];
635 if ((semakptr
->u
.sem_perm
.mode
& SEM_ALLOC
) == 0) {
639 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_R
)))
642 error
= mac_sysvsem_check_semctl(cred
, semakptr
, cmd
);
646 bcopy(&semakptr
->u
, arg
->buf
, sizeof(struct semid_ds
));
647 *rval
= IXSEQ_TO_IPCID(semid
, semakptr
->u
.sem_perm
);
648 mtx_unlock(sema_mtxp
);
652 semidx
= IPCID_TO_IX(semid
);
653 if (semidx
< 0 || semidx
>= seminfo
.semmni
)
656 semakptr
= &sema
[semidx
];
657 sema_mtxp
= &sema_mtx
[semidx
];
660 error
= mac_sysvsem_check_semctl(cred
, semakptr
, cmd
);
670 if ((error
= semvalid(semid
, semakptr
)) != 0)
672 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_M
)))
674 semakptr
->u
.sem_perm
.cuid
= cred
->cr_uid
;
675 semakptr
->u
.sem_perm
.uid
= cred
->cr_uid
;
676 semtot
-= semakptr
->u
.sem_nsems
;
677 for (i
= semakptr
->u
.sem_base
- sem
; i
< semtot
; i
++)
678 sem
[i
] = sem
[i
+ semakptr
->u
.sem_nsems
];
679 for (i
= 0; i
< seminfo
.semmni
; i
++) {
680 if ((sema
[i
].u
.sem_perm
.mode
& SEM_ALLOC
) &&
681 sema
[i
].u
.sem_base
> semakptr
->u
.sem_base
)
682 sema
[i
].u
.sem_base
-= semakptr
->u
.sem_nsems
;
684 semakptr
->u
.sem_perm
.mode
= 0;
686 mac_sysvsem_cleanup(semakptr
);
689 semundo_clear(semidx
, -1);
695 if ((error
= semvalid(semid
, semakptr
)) != 0)
697 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_M
)))
700 semakptr
->u
.sem_perm
.uid
= sbuf
->sem_perm
.uid
;
701 semakptr
->u
.sem_perm
.gid
= sbuf
->sem_perm
.gid
;
702 semakptr
->u
.sem_perm
.mode
= (semakptr
->u
.sem_perm
.mode
&
703 ~0777) | (sbuf
->sem_perm
.mode
& 0777);
704 semakptr
->u
.sem_ctime
= time_second
;
708 if ((error
= semvalid(semid
, semakptr
)) != 0)
710 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_R
)))
712 bcopy(&semakptr
->u
, arg
->buf
, sizeof(struct semid_ds
));
716 if ((error
= semvalid(semid
, semakptr
)) != 0)
718 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_R
)))
720 if (semnum
< 0 || semnum
>= semakptr
->u
.sem_nsems
) {
724 *rval
= semakptr
->u
.sem_base
[semnum
].semncnt
;
728 if ((error
= semvalid(semid
, semakptr
)) != 0)
730 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_R
)))
732 if (semnum
< 0 || semnum
>= semakptr
->u
.sem_nsems
) {
736 *rval
= semakptr
->u
.sem_base
[semnum
].sempid
;
740 if ((error
= semvalid(semid
, semakptr
)) != 0)
742 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_R
)))
744 if (semnum
< 0 || semnum
>= semakptr
->u
.sem_nsems
) {
748 *rval
= semakptr
->u
.sem_base
[semnum
].semval
;
753 * Unfortunately, callers of this function don't know
754 * in advance how many semaphores are in this set.
755 * While we could just allocate the maximum size array
756 * and pass the actual size back to the caller, that
757 * won't work for SETALL since we can't copyin() more
758 * data than the user specified as we may return a
761 * Note that the number of semaphores in a set is
762 * fixed for the life of that set. The only way that
763 * the 'count' could change while are blocked in
764 * malloc() is if this semaphore set were destroyed
765 * and a new one created with the same index.
766 * However, semvalid() will catch that due to the
767 * sequence number unless exactly 0x8000 (or a
768 * multiple thereof) semaphore sets for the same index
769 * are created and destroyed while we are in malloc!
772 count
= semakptr
->u
.sem_nsems
;
773 mtx_unlock(sema_mtxp
);
774 array
= malloc(sizeof(*array
) * count
, M_TEMP
, M_WAITOK
);
776 if ((error
= semvalid(semid
, semakptr
)) != 0)
778 KASSERT(count
== semakptr
->u
.sem_nsems
, ("nsems changed"));
779 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_R
)))
781 for (i
= 0; i
< semakptr
->u
.sem_nsems
; i
++)
782 array
[i
] = semakptr
->u
.sem_base
[i
].semval
;
783 mtx_unlock(sema_mtxp
);
784 error
= copyout(array
, arg
->array
, count
* sizeof(*array
));
789 if ((error
= semvalid(semid
, semakptr
)) != 0)
791 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_R
)))
793 if (semnum
< 0 || semnum
>= semakptr
->u
.sem_nsems
) {
797 *rval
= semakptr
->u
.sem_base
[semnum
].semzcnt
;
801 if ((error
= semvalid(semid
, semakptr
)) != 0)
803 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_W
)))
805 if (semnum
< 0 || semnum
>= semakptr
->u
.sem_nsems
) {
809 if (arg
->val
< 0 || arg
->val
> seminfo
.semvmx
) {
813 semakptr
->u
.sem_base
[semnum
].semval
= arg
->val
;
815 semundo_clear(semidx
, semnum
);
822 * See comment on GETALL for why 'count' shouldn't change
823 * and why we require a userland buffer.
825 count
= semakptr
->u
.sem_nsems
;
826 mtx_unlock(sema_mtxp
);
827 array
= malloc(sizeof(*array
) * count
, M_TEMP
, M_WAITOK
);
828 error
= copyin(arg
->array
, array
, count
* sizeof(*array
));
832 if ((error
= semvalid(semid
, semakptr
)) != 0)
834 KASSERT(count
== semakptr
->u
.sem_nsems
, ("nsems changed"));
835 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, IPC_W
)))
837 for (i
= 0; i
< semakptr
->u
.sem_nsems
; i
++) {
839 if (usval
> seminfo
.semvmx
) {
843 semakptr
->u
.sem_base
[i
].semval
= usval
;
846 semundo_clear(semidx
, -1);
857 mtx_unlock(sema_mtxp
);
863 #ifndef _SYS_SYSPROTO_H_
873 struct semget_args
*uap
;
875 int semid
, error
= 0;
877 int nsems
= uap
->nsems
;
878 int semflg
= uap
->semflg
;
879 struct ucred
*cred
= td
->td_ucred
;
881 DPRINTF(("semget(0x%x, %d, 0%o)\n", key
, nsems
, semflg
));
882 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
886 if (key
!= IPC_PRIVATE
) {
887 for (semid
= 0; semid
< seminfo
.semmni
; semid
++) {
888 if ((sema
[semid
].u
.sem_perm
.mode
& SEM_ALLOC
) &&
889 sema
[semid
].u
.sem_perm
.key
== key
)
892 if (semid
< seminfo
.semmni
) {
893 DPRINTF(("found public key\n"));
894 if ((error
= ipcperm(td
, &sema
[semid
].u
.sem_perm
,
898 if (nsems
> 0 && sema
[semid
].u
.sem_nsems
< nsems
) {
899 DPRINTF(("too small\n"));
903 if ((semflg
& IPC_CREAT
) && (semflg
& IPC_EXCL
)) {
904 DPRINTF(("not exclusive\n"));
909 error
= mac_sysvsem_check_semget(cred
, &sema
[semid
]);
917 DPRINTF(("need to allocate the semid_kernel\n"));
918 if (key
== IPC_PRIVATE
|| (semflg
& IPC_CREAT
)) {
919 if (nsems
<= 0 || nsems
> seminfo
.semmsl
) {
920 DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems
,
925 if (nsems
> seminfo
.semmns
- semtot
) {
927 "not enough semaphores left (need %d, got %d)\n",
928 nsems
, seminfo
.semmns
- semtot
));
932 for (semid
= 0; semid
< seminfo
.semmni
; semid
++) {
933 if ((sema
[semid
].u
.sem_perm
.mode
& SEM_ALLOC
) == 0)
936 if (semid
== seminfo
.semmni
) {
937 DPRINTF(("no more semid_kernel's available\n"));
941 DPRINTF(("semid %d is available\n", semid
));
942 sema
[semid
].u
.sem_perm
.key
= key
;
943 sema
[semid
].u
.sem_perm
.cuid
= cred
->cr_uid
;
944 sema
[semid
].u
.sem_perm
.uid
= cred
->cr_uid
;
945 sema
[semid
].u
.sem_perm
.cgid
= cred
->cr_gid
;
946 sema
[semid
].u
.sem_perm
.gid
= cred
->cr_gid
;
947 sema
[semid
].u
.sem_perm
.mode
= (semflg
& 0777) | SEM_ALLOC
;
948 sema
[semid
].u
.sem_perm
.seq
=
949 (sema
[semid
].u
.sem_perm
.seq
+ 1) & 0x7fff;
950 sema
[semid
].u
.sem_nsems
= nsems
;
951 sema
[semid
].u
.sem_otime
= 0;
952 sema
[semid
].u
.sem_ctime
= time_second
;
953 sema
[semid
].u
.sem_base
= &sem
[semtot
];
955 bzero(sema
[semid
].u
.sem_base
,
956 sizeof(sema
[semid
].u
.sem_base
[0])*nsems
);
958 mac_sysvsem_create(cred
, &sema
[semid
]);
960 DPRINTF(("sembase = %p, next = %p\n",
961 sema
[semid
].u
.sem_base
, &sem
[semtot
]));
963 DPRINTF(("didn't find it and wasn't asked to create it\n"));
969 td
->td_retval
[0] = IXSEQ_TO_IPCID(semid
, sema
[semid
].u
.sem_perm
);
975 #ifndef _SYS_SYSPROTO_H_
985 struct semop_args
*uap
;
988 struct sembuf small_sops
[SMALL_SOPS
];
989 int semid
= uap
->semid
;
990 size_t nsops
= uap
->nsops
;
992 struct semid_kernel
*semakptr
;
993 struct sembuf
*sopptr
= 0;
994 struct sem
*semptr
= 0;
995 struct sem_undo
*suptr
;
996 struct mtx
*sema_mtxp
;
999 int do_wakeup
, do_undos
;
1004 DPRINTF(("call to semop(%d, %p, %u)\n", semid
, sops
, nsops
));
1006 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
1009 semid
= IPCID_TO_IX(semid
); /* Convert back to zero origin */
1011 if (semid
< 0 || semid
>= seminfo
.semmni
)
1014 /* Allocate memory for sem_ops */
1015 if (nsops
<= SMALL_SOPS
)
1017 else if (nsops
<= seminfo
.semopm
)
1018 sops
= malloc(nsops
* sizeof(*sops
), M_TEMP
, M_WAITOK
);
1020 DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo
.semopm
,
1024 if ((error
= copyin(uap
->sops
, sops
, nsops
* sizeof(sops
[0]))) != 0) {
1025 DPRINTF(("error = %d from copyin(%p, %p, %d)\n", error
,
1026 uap
->sops
, sops
, nsops
* sizeof(sops
[0])));
1027 if (sops
!= small_sops
)
1032 semakptr
= &sema
[semid
];
1033 sema_mtxp
= &sema_mtx
[semid
];
1034 mtx_lock(sema_mtxp
);
1035 if ((semakptr
->u
.sem_perm
.mode
& SEM_ALLOC
) == 0) {
1039 if (semakptr
->u
.sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
)) {
1044 * Initial pass thru sops to see what permissions are needed.
1045 * Also perform any checks that don't need repeating on each
1046 * attempt to satisfy the request vector.
1048 j
= 0; /* permission needed */
1050 for (i
= 0; i
< nsops
; i
++) {
1052 if (sopptr
->sem_num
>= semakptr
->u
.sem_nsems
) {
1056 if (sopptr
->sem_flg
& SEM_UNDO
&& sopptr
->sem_op
!= 0)
1058 j
|= (sopptr
->sem_op
== 0) ? SEM_R
: SEM_A
;
1061 if ((error
= ipcperm(td
, &semakptr
->u
.sem_perm
, j
))) {
1062 DPRINTF(("error = %d from ipaccess\n", error
));
1066 error
= mac_sysvsem_check_semop(td
->td_ucred
, semakptr
, j
);
1072 * Loop trying to satisfy the vector of requests.
1073 * If we reach a point where we must wait, any requests already
1074 * performed are rolled back and we go to sleep until some other
1075 * process wakes us up. At this point, we start all over again.
1077 * This ensures that from the perspective of other tasks, a set
1078 * of requests is atomic (never partially satisfied).
1082 error
= 0; /* error return if necessary */
1084 for (i
= 0; i
< nsops
; i
++) {
1086 semptr
= &semakptr
->u
.sem_base
[sopptr
->sem_num
];
1089 "semop: semakptr=%p, sem_base=%p, "
1090 "semptr=%p, sem[%d]=%d : op=%d, flag=%s\n",
1091 semakptr
, semakptr
->u
.sem_base
, semptr
,
1092 sopptr
->sem_num
, semptr
->semval
, sopptr
->sem_op
,
1093 (sopptr
->sem_flg
& IPC_NOWAIT
) ?
1094 "nowait" : "wait"));
1096 if (sopptr
->sem_op
< 0) {
1097 if (semptr
->semval
+ sopptr
->sem_op
< 0) {
1098 DPRINTF(("semop: can't do it now\n"));
1101 semptr
->semval
+= sopptr
->sem_op
;
1102 if (semptr
->semval
== 0 &&
1103 semptr
->semzcnt
> 0)
1106 } else if (sopptr
->sem_op
== 0) {
1107 if (semptr
->semval
!= 0) {
1108 DPRINTF(("semop: not zero now\n"));
1111 } else if (semptr
->semval
+ sopptr
->sem_op
>
1116 if (semptr
->semncnt
> 0)
1118 semptr
->semval
+= sopptr
->sem_op
;
1123 * Did we get through the entire vector?
1129 * No ... rollback anything that we've already done
1131 DPRINTF(("semop: rollback 0 through %d\n", i
-1));
1132 for (j
= 0; j
< i
; j
++)
1133 semakptr
->u
.sem_base
[sops
[j
].sem_num
].semval
-=
1136 /* If we detected an error, return it */
1141 * If the request that we couldn't satisfy has the
1142 * NOWAIT flag set then return with EAGAIN.
1144 if (sopptr
->sem_flg
& IPC_NOWAIT
) {
1149 if (sopptr
->sem_op
== 0)
1154 DPRINTF(("semop: good night!\n"));
1155 error
= msleep(semakptr
, sema_mtxp
, (PZERO
- 4) | PCATCH
,
1157 DPRINTF(("semop: good morning (error=%d)!\n", error
));
1158 /* return code is checked below, after sem[nz]cnt-- */
1161 * Make sure that the semaphore still exists
1163 if ((semakptr
->u
.sem_perm
.mode
& SEM_ALLOC
) == 0 ||
1164 semakptr
->u
.sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
)) {
1170 * Renew the semaphore's pointer after wakeup since
1171 * during msleep sem_base may have been modified and semptr
1172 * is not valid any more
1174 semptr
= &semakptr
->u
.sem_base
[sopptr
->sem_num
];
1177 * The semaphore is still alive. Readjust the count of
1178 * waiting processes.
1180 if (sopptr
->sem_op
== 0)
1186 * Is it really morning, or was our sleep interrupted?
1187 * (Delayed check of msleep() return code because we
1188 * need to decrement sem[nz]cnt either way.)
1194 DPRINTF(("semop: good morning!\n"));
1199 * Process any SEM_UNDO requests.
1204 for (i
= 0; i
< nsops
; i
++) {
1206 * We only need to deal with SEM_UNDO's for non-zero
1211 if ((sops
[i
].sem_flg
& SEM_UNDO
) == 0)
1213 adjval
= sops
[i
].sem_op
;
1216 error
= semundo_adjust(td
, &suptr
, semid
,
1217 sops
[i
].sem_num
, -adjval
);
1222 * Oh-Oh! We ran out of either sem_undo's or undo's.
1223 * Rollback the adjustments to this point and then
1224 * rollback the semaphore ups and down so we can return
1225 * with an error with all structures restored. We
1226 * rollback the undo's in the exact reverse order that
1227 * we applied them. This guarantees that we won't run
1228 * out of space as we roll things back out.
1230 for (j
= 0; j
< i
; j
++) {
1232 if ((sops
[k
].sem_flg
& SEM_UNDO
) == 0)
1234 adjval
= sops
[k
].sem_op
;
1237 if (semundo_adjust(td
, &suptr
, semid
,
1238 sops
[k
].sem_num
, adjval
) != 0)
1239 panic("semop - can't undo undos");
1242 for (j
= 0; j
< nsops
; j
++)
1243 semakptr
->u
.sem_base
[sops
[j
].sem_num
].semval
-=
1246 DPRINTF(("error = %d from semundo_adjust\n", error
));
1249 } /* loop through the sops */
1251 } /* if (do_undos) */
1253 /* We're definitely done - set the sempid's and time */
1254 for (i
= 0; i
< nsops
; i
++) {
1256 semptr
= &semakptr
->u
.sem_base
[sopptr
->sem_num
];
1257 semptr
->sempid
= td
->td_proc
->p_pid
;
1259 semakptr
->u
.sem_otime
= time_second
;
1262 * Do a wakeup if any semaphore was up'd whilst something was
1266 DPRINTF(("semop: doing wakeup\n"));
1268 DPRINTF(("semop: back from wakeup\n"));
1270 DPRINTF(("semop: done\n"));
1271 td
->td_retval
[0] = 0;
1273 mtx_unlock(sema_mtxp
);
1274 if (sops
!= small_sops
)
1280 * Go through the undo structures for this process and apply the adjustments to
1284 semexit_myhook(arg
, p
)
1288 struct sem_undo
*suptr
;
1289 struct sem_undo
**supptr
;
1292 * Go through the chain of undo vectors looking for one
1293 * associated with this process.
1296 SLIST_FOREACH_PREVPTR(suptr
, supptr
, &semu_list
, un_next
) {
1297 if (suptr
->un_proc
== p
) {
1298 *supptr
= SLIST_NEXT(suptr
, un_next
);
1307 DPRINTF(("proc @%p has undo structure with %d entries\n", p
,
1311 * If there are any active undo elements then process them.
1313 if (suptr
->un_cnt
> 0) {
1316 for (ix
= 0; ix
< suptr
->un_cnt
; ix
++) {
1317 int semid
= suptr
->un_ent
[ix
].un_id
;
1318 int semnum
= suptr
->un_ent
[ix
].un_num
;
1319 int adjval
= suptr
->un_ent
[ix
].un_adjval
;
1320 struct semid_kernel
*semakptr
;
1321 struct mtx
*sema_mtxp
;
1323 semakptr
= &sema
[semid
];
1324 sema_mtxp
= &sema_mtx
[semid
];
1325 mtx_lock(sema_mtxp
);
1327 if ((semakptr
->u
.sem_perm
.mode
& SEM_ALLOC
) == 0)
1328 panic("semexit - semid not allocated");
1329 if (semnum
>= semakptr
->u
.sem_nsems
)
1330 panic("semexit - semnum out of range");
1333 "semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n",
1334 suptr
->un_proc
, suptr
->un_ent
[ix
].un_id
,
1335 suptr
->un_ent
[ix
].un_num
,
1336 suptr
->un_ent
[ix
].un_adjval
,
1337 semakptr
->u
.sem_base
[semnum
].semval
));
1340 if (semakptr
->u
.sem_base
[semnum
].semval
<
1342 semakptr
->u
.sem_base
[semnum
].semval
= 0;
1344 semakptr
->u
.sem_base
[semnum
].semval
+=
1347 semakptr
->u
.sem_base
[semnum
].semval
+= adjval
;
1350 DPRINTF(("semexit: back from wakeup\n"));
1351 mtx_unlock(sema_mtxp
);
1357 * Deallocate the undo vector.
1359 DPRINTF(("removing vector\n"));
1361 suptr
->un_proc
= NULL
;
1366 sysctl_sema(SYSCTL_HANDLER_ARGS
)
1369 return (SYSCTL_OUT(req
, sema
,
1370 sizeof(struct semid_kernel
) * seminfo
.semmni
));