2 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
3 * Copyright (c) 2003-2005 SPARTA, Inc.
4 * Copyright (c) 2005 Robert N. M. Watson
7 * This software was developed for the FreeBSD Project in part by Network
8 * Associates Laboratories, the Security Research Division of Network
9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
10 * as part of the DARPA CHATS research program.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
38 #include "opt_posix.h"
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/sysproto.h>
43 #include <sys/eventhandler.h>
44 #include <sys/kernel.h>
48 #include <sys/posix4.h>
50 #include <sys/mutex.h>
51 #include <sys/module.h>
52 #include <sys/condvar.h>
55 #include <sys/semaphore.h>
56 #include <sys/syscall.h>
58 #include <sys/sysent.h>
59 #include <sys/sysctl.h>
61 #include <sys/malloc.h>
62 #include <sys/fcntl.h>
63 #include <sys/_semaphore.h>
65 #include <security/mac/mac_framework.h>
67 static int sem_count_proc(struct proc *p);
68 static struct ksem *sem_lookup_byname(const char *name);
69 static int sem_create(struct thread *td, const char *name,
70 struct ksem **ksret, mode_t mode, unsigned int value);
71 static void sem_free(struct ksem *ksnew);
72 static int sem_perm(struct thread *td, struct ksem *ks);
73 static void sem_enter(struct proc *p, struct ksem *ks);
74 static int sem_leave(struct proc *p, struct ksem *ks);
75 static void sem_exechook(void *arg, struct proc *p,
76 struct image_params *imgp);
77 static void sem_exithook(void *arg, struct proc *p);
78 static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
80 static int sem_hasopen(struct thread *td, struct ksem *ks);
82 static int kern_sem_close(struct thread *td, semid_t id);
83 static int kern_sem_post(struct thread *td, semid_t id);
84 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
85 struct timespec *abstime);
86 static int kern_sem_init(struct thread *td, int dir, unsigned int value,
88 static int kern_sem_open(struct thread *td, int dir, const char *name,
89 int oflag, mode_t mode, unsigned int value, semid_t *idp);
90 static int kern_sem_unlink(struct thread *td, const char *name);
96 #define SEM_MAX_NAMELEN 14
98 #define SEM_TO_ID(x) ((intptr_t)(x))
99 #define ID_TO_SEM(x) id_to_sem(x)
102 * Available semaphores go here, this includes sem_init and any semaphores
103 * created via sem_open that have not yet been unlinked.
105 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
108 * Semaphores still in use but have been sem_unlink()'d go here.
110 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
112 static struct mtx sem_lock;
113 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
115 static int nsems = 0;
116 SYSCTL_DECL(_p1003_1b);
117 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
119 static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
122 #define DP(x) printf x
128 sem_ref(struct ksem *ks)
131 mtx_assert(&sem_lock, MA_OWNED);
133 DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
137 sem_rel(struct ksem *ks)
140 mtx_assert(&sem_lock, MA_OWNED);
141 DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
142 if (--ks->ks_ref == 0)
148 id_to_sem(semid_t id)
152 mtx_assert(&sem_lock, MA_OWNED);
153 DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
154 LIST_FOREACH(ks, &ksem_head, ks_entry) {
155 DP(("id_to_sem: ks = %p\n", ks));
156 if (ks == (struct ksem *)id)
163 sem_lookup_byname(const char *name)
167 mtx_assert(&sem_lock, MA_OWNED);
168 LIST_FOREACH(ks, &ksem_head, ks_entry)
169 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
175 sem_create(struct thread *td, const char *name, struct ksem **ksret,
176 mode_t mode, unsigned int value)
184 DP(("sem_create\n"));
187 if (value > SEM_VALUE_MAX)
189 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
192 if (len > SEM_MAX_NAMELEN) {
194 return (ENAMETOOLONG);
197 /* Name must start with a '/' but not contain one. */
198 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
202 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
203 strcpy(ret->ks_name, name);
208 ret->ks_value = value;
211 ret->ks_uid = uc->cr_uid;
212 ret->ks_gid = uc->cr_gid;
214 cv_init(&ret->ks_cv, "sem");
215 LIST_INIT(&ret->ks_users);
217 mac_posixsem_init(ret);
218 mac_posixsem_create(uc, ret);
221 sem_enter(td->td_proc, ret);
225 if (nsems > p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
226 sem_leave(td->td_proc, ret);
231 mtx_unlock(&sem_lock);
235 #ifndef _SYS_SYSPROTO_H_
236 struct ksem_init_args {
240 int ksem_init(struct thread *td, struct ksem_init_args *uap);
243 ksem_init(struct thread *td, struct ksem_init_args *uap)
246 return (kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp));
250 kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
256 error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
260 if (dir == UIO_USERSPACE) {
261 error = copyout(&id, idp, sizeof(id));
265 mtx_unlock(&sem_lock);
272 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
274 mtx_unlock(&sem_lock);
278 #ifndef _SYS_SYSPROTO_H_
279 struct ksem_open_args {
286 int ksem_open(struct thread *td, struct ksem_open_args *uap);
289 ksem_open(struct thread *td, struct ksem_open_args *uap)
291 char name[SEM_MAX_NAMELEN + 1];
295 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
298 DP((">>> sem_open start\n"));
299 error = kern_sem_open(td, UIO_USERSPACE,
300 name, uap->oflag, uap->mode, uap->value, uap->idp);
301 DP(("<<< sem_open end\n"));
306 kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
307 mode_t mode, unsigned int value, semid_t *idp)
309 struct ksem *ksnew, *ks;
315 ks = sem_lookup_byname(name);
318 * If we found it but O_EXCL is set, error.
320 if (ks != NULL && (oflag & O_EXCL) != 0) {
321 mtx_unlock(&sem_lock);
326 * If we didn't find it...
330 * didn't ask for creation? error.
332 if ((oflag & O_CREAT) == 0) {
333 mtx_unlock(&sem_lock);
338 * We may block during creation, so drop the lock.
340 mtx_unlock(&sem_lock);
341 error = sem_create(td, name, &ksnew, mode, value);
344 id = SEM_TO_ID(ksnew);
345 if (dir == UIO_USERSPACE) {
346 DP(("about to copyout! %d to %p\n", id, idp));
347 error = copyout(&id, idp, sizeof(id));
350 sem_leave(td->td_proc, ksnew);
352 mtx_unlock(&sem_lock);
356 DP(("about to set! %d to %p\n", id, idp));
361 * We need to make sure we haven't lost a race while
362 * allocating during creation.
365 ks = sem_lookup_byname(name);
368 sem_leave(td->td_proc, ksnew);
370 /* we lost and we can't loose... */
371 if ((oflag & O_EXCL) != 0) {
372 mtx_unlock(&sem_lock);
376 DP(("sem_create: about to add to list...\n"));
377 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
378 DP(("sem_create: setting list bit...\n"));
379 ksnew->ks_onlist = 1;
380 DP(("sem_create: done, about to unlock...\n"));
384 error = mac_posixsem_check_open(td->td_ucred, ks);
389 * if we aren't the creator, then enforce permissions.
391 error = sem_perm(td, ks);
395 mtx_unlock(&sem_lock);
397 if (dir == UIO_USERSPACE) {
398 error = copyout(&id, idp, sizeof(id));
402 mtx_unlock(&sem_lock);
408 sem_enter(td->td_proc, ks);
413 mtx_unlock(&sem_lock);
418 sem_perm(struct thread *td, struct ksem *ks)
423 * XXXRW: This permission routine appears to be incorrect. If the
424 * user matches, we shouldn't go on to the group if the user
425 * permissions don't allow the action? Not changed for now. To fix,
426 * change from a series of if (); if (); to if () else if () else...
429 DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
430 uc->cr_uid, uc->cr_gid,
431 ks->ks_uid, ks->ks_gid, ks->ks_mode));
432 if ((uc->cr_uid == ks->ks_uid) && (ks->ks_mode & S_IWUSR) != 0)
434 if ((uc->cr_gid == ks->ks_gid) && (ks->ks_mode & S_IWGRP) != 0)
436 if ((ks->ks_mode & S_IWOTH) != 0)
438 return (priv_check(td, PRIV_SEM_WRITE));
442 sem_free(struct ksem *ks)
446 mac_posixsem_destroy(ks);
450 LIST_REMOVE(ks, ks_entry);
451 if (ks->ks_name != NULL)
452 free(ks->ks_name, M_SEM);
453 cv_destroy(&ks->ks_cv);
457 static __inline struct kuser *
458 sem_getuser(struct proc *p, struct ksem *ks)
462 LIST_FOREACH(k, &ks->ks_users, ku_next)
463 if (k->ku_pid == p->p_pid)
469 sem_hasopen(struct thread *td, struct ksem *ks)
472 return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
473 || sem_getuser(td->td_proc, ks) != NULL);
477 sem_leave(struct proc *p, struct ksem *ks)
481 DP(("sem_leave: ks = %p\n", ks));
482 k = sem_getuser(p, ks);
483 DP(("sem_leave: ks = %p, k = %p\n", ks, k));
485 LIST_REMOVE(k, ku_next);
487 DP(("sem_leave: about to free k\n"));
489 DP(("sem_leave: returning\n"));
496 sem_enter(struct proc *p, struct ksem *ks)
498 struct kuser *ku, *k;
500 ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
501 ku->ku_pid = p->p_pid;
503 k = sem_getuser(p, ks);
505 mtx_unlock(&sem_lock);
509 LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
511 mtx_unlock(&sem_lock);
514 #ifndef _SYS_SYSPROTO_H_
515 struct ksem_unlink_args {
518 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
521 ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
523 char name[SEM_MAX_NAMELEN + 1];
527 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
528 return (error ? error :
529 kern_sem_unlink(td, name));
533 kern_sem_unlink(struct thread *td, const char *name)
539 ks = sem_lookup_byname(name);
542 error = mac_posixsem_check_unlink(td->td_ucred, ks);
544 mtx_unlock(&sem_lock);
548 error = sem_perm(td, ks);
551 DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
553 LIST_REMOVE(ks, ks_entry);
554 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
557 mtx_unlock(&sem_lock);
561 #ifndef _SYS_SYSPROTO_H_
562 struct ksem_close_args {
565 int ksem_close(struct thread *td, struct ksem_close_args *uap);
568 ksem_close(struct thread *td, struct ksem_close_args *uap)
571 return (kern_sem_close(td, uap->id));
575 kern_sem_close(struct thread *td, semid_t id)
585 * This is not a valid operation for unnamed sems.
587 if (ks != NULL && ks->ks_name != NULL)
588 error = sem_leave(td->td_proc, ks);
589 mtx_unlock(&sem_lock);
593 #ifndef _SYS_SYSPROTO_H_
594 struct ksem_post_args {
597 int ksem_post(struct thread *td, struct ksem_post_args *uap);
600 ksem_post(struct thread *td, struct ksem_post_args *uap)
603 return (kern_sem_post(td, uap->id));
607 kern_sem_post(struct thread *td, semid_t id)
614 if (ks == NULL || !sem_hasopen(td, ks)) {
619 error = mac_posixsem_check_post(td->td_ucred, ks);
623 if (ks->ks_value == SEM_VALUE_MAX) {
628 if (ks->ks_waiters > 0)
629 cv_signal(&ks->ks_cv);
632 mtx_unlock(&sem_lock);
636 #ifndef _SYS_SYSPROTO_H_
637 struct ksem_wait_args {
640 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
643 ksem_wait(struct thread *td, struct ksem_wait_args *uap)
646 return (kern_sem_wait(td, uap->id, 0, NULL));
649 #ifndef _SYS_SYSPROTO_H_
650 struct ksem_timedwait_args {
652 const struct timespec *abstime;
654 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
657 ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
659 struct timespec abstime;
664 * We allow a null timespec (wait forever).
666 if (uap->abstime == NULL)
669 error = copyin(uap->abstime, &abstime, sizeof(abstime));
672 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
676 return (kern_sem_wait(td, uap->id, 0, ts));
679 #ifndef _SYS_SYSPROTO_H_
680 struct ksem_trywait_args {
683 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
686 ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
689 return (kern_sem_wait(td, uap->id, 1, NULL));
693 kern_sem_wait(struct thread *td, semid_t id, int tryflag,
694 struct timespec *abstime)
696 struct timespec ts1, ts2;
701 DP((">>> kern_sem_wait entered!\n"));
705 DP(("kern_sem_wait ks == NULL\n"));
710 if (!sem_hasopen(td, ks)) {
711 DP(("kern_sem_wait hasopen failed\n"));
716 error = mac_posixsem_check_wait(td->td_ucred, ks);
718 DP(("kern_sem_wait mac failed\n"));
722 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
723 if (ks->ks_value == 0) {
727 else if (abstime == NULL)
728 error = cv_wait_sig(&ks->ks_cv, &sem_lock);
733 timespecsub(&ts1, &ts2);
734 TIMESPEC_TO_TIMEVAL(&tv, &ts1);
739 error = cv_timedwait_sig(&ks->ks_cv,
740 &sem_lock, tvtohz(&tv));
741 if (error != EWOULDBLOCK)
754 mtx_unlock(&sem_lock);
755 DP(("<<< kern_sem_wait leaving, error = %d\n", error));
759 #ifndef _SYS_SYSPROTO_H_
760 struct ksem_getvalue_args {
764 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
767 ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
773 ks = ID_TO_SEM(uap->id);
774 if (ks == NULL || !sem_hasopen(td, ks)) {
775 mtx_unlock(&sem_lock);
779 error = mac_posixsem_check_getvalue(td->td_ucred, ks);
781 mtx_unlock(&sem_lock);
786 mtx_unlock(&sem_lock);
787 error = copyout(&val, uap->val, sizeof(val));
791 #ifndef _SYS_SYSPROTO_H_
792 struct ksem_destroy_args {
795 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
798 ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
804 ks = ID_TO_SEM(uap->id);
805 if (ks == NULL || !sem_hasopen(td, ks) ||
806 ks->ks_name != NULL) {
811 error = mac_posixsem_check_destroy(td->td_ucred, ks);
815 if (ks->ks_waiters != 0) {
822 mtx_unlock(&sem_lock);
827 * Count the number of kusers associated with a proc, so as to guess at how
828 * many to allocate when forking.
831 sem_count_proc(struct proc *p)
837 mtx_assert(&sem_lock, MA_OWNED);
840 LIST_FOREACH(ks, &ksem_head, ks_entry) {
841 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
842 if (ku->ku_pid == p->p_pid)
846 LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
847 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
848 if (ku->ku_pid == p->p_pid)
856 * When a process forks, the child process must gain a reference to each open
857 * semaphore in the parent process, whether it is unlinked or not. This
858 * requires allocating a kuser structure for each semaphore reference in the
859 * new process. Because the set of semaphores in the parent can change while
860 * the fork is in progress, we have to handle races -- first we attempt to
861 * allocate enough storage to acquire references to each of the semaphores,
862 * then we enter the semaphores and release the temporary references.
865 sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
867 struct ksem *ks, **sem_array;
868 int count, i, new_count;
872 count = sem_count_proc(p1);
874 mtx_unlock(&sem_lock);
878 mtx_assert(&sem_lock, MA_OWNED);
879 mtx_unlock(&sem_lock);
880 sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
882 new_count = sem_count_proc(p1);
883 if (count < new_count) {
884 /* Lost race, repeat and allocate more storage. */
885 free(sem_array, M_TEMP);
891 * Given an array capable of storing an adequate number of semaphore
892 * references, now walk the list of semaphores and acquire a new
893 * reference for any semaphore opened by p1.
897 LIST_FOREACH(ks, &ksem_head, ks_entry) {
898 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
899 if (ku->ku_pid == p1->p_pid) {
907 LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
908 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
909 if (ku->ku_pid == p1->p_pid) {
917 mtx_unlock(&sem_lock);
918 KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
921 * Now cause p2 to enter each of the referenced semaphores, then
922 * release our temporary reference. This is pretty inefficient.
923 * Finally, free our temporary array.
925 for (i = 0; i < count; i++) {
926 sem_enter(p2, sem_array[i]);
928 sem_rel(sem_array[i]);
929 mtx_unlock(&sem_lock);
931 free(sem_array, M_TEMP);
935 sem_exechook(void *arg, struct proc *p, struct image_params *imgp __unused)
937 sem_exithook(arg, p);
941 sem_exithook(void *arg, struct proc *p)
943 struct ksem *ks, *ksnext;
946 ks = LIST_FIRST(&ksem_head);
948 ksnext = LIST_NEXT(ks, ks_entry);
952 ks = LIST_FIRST(&ksem_deadhead);
954 ksnext = LIST_NEXT(ks, ks_entry);
958 mtx_unlock(&sem_lock);
962 sem_modload(struct module *module, int cmd, void *arg)
968 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
969 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
970 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
971 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit,
972 sem_exithook, NULL, EVENTHANDLER_PRI_ANY);
973 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec,
974 sem_exechook, NULL, EVENTHANDLER_PRI_ANY);
975 sem_fork_tag = EVENTHANDLER_REGISTER(process_fork,
976 sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
984 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
985 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
986 EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
987 mtx_destroy(&sem_lock);
999 static moduledata_t sem_mod = {
1005 SYSCALL_MODULE_HELPER(ksem_init);
1006 SYSCALL_MODULE_HELPER(ksem_open);
1007 SYSCALL_MODULE_HELPER(ksem_unlink);
1008 SYSCALL_MODULE_HELPER(ksem_close);
1009 SYSCALL_MODULE_HELPER(ksem_post);
1010 SYSCALL_MODULE_HELPER(ksem_wait);
1011 SYSCALL_MODULE_HELPER(ksem_timedwait);
1012 SYSCALL_MODULE_HELPER(ksem_trywait);
1013 SYSCALL_MODULE_HELPER(ksem_getvalue);
1014 SYSCALL_MODULE_HELPER(ksem_destroy);
1016 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1017 MODULE_VERSION(sem, 1);