xref: /freebsd/sys/kern/uipc_sem.c (revision 262e143bd46171a6415a5b28af260a5efa2a3db8)
1 /*-
2  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
3  * Copyright (c) 2003-2005 SPARTA, Inc.
4  * Copyright (c) 2005 Robert N. M. Watson
5  * All rights reserved.
6  *
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.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
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.
20  *
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
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include "opt_mac.h"
38 #include "opt_posix.h"
39 
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>
45 #include <sys/proc.h>
46 #include <sys/lock.h>
47 #include <sys/mutex.h>
48 #include <sys/module.h>
49 #include <sys/condvar.h>
50 #include <sys/sem.h>
51 #include <sys/uio.h>
52 #include <sys/syscall.h>
53 #include <sys/stat.h>
54 #include <sys/sysent.h>
55 #include <sys/sysctl.h>
56 #include <sys/time.h>
57 #include <sys/mac.h>
58 #include <sys/malloc.h>
59 #include <sys/fcntl.h>
60 
61 #include <posix4/ksem.h>
62 #include <posix4/posix4.h>
63 #include <posix4/semaphore.h>
64 #include <posix4/_semaphore.h>
65 
66 static int sem_count_proc(struct proc *p);
67 static struct ksem *sem_lookup_byname(const char *name);
68 static int sem_create(struct thread *td, const char *name,
69     struct ksem **ksret, mode_t mode, unsigned int value);
70 static void sem_free(struct ksem *ksnew);
71 static int sem_perm(struct thread *td, struct ksem *ks);
72 static void sem_enter(struct proc *p, struct ksem *ks);
73 static int sem_leave(struct proc *p, struct ksem *ks);
74 static void sem_exithook(void *arg, struct proc *p);
75 static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
76     int flags);
77 static int sem_hasopen(struct thread *td, struct ksem *ks);
78 
79 static int kern_sem_close(struct thread *td, semid_t id);
80 static int kern_sem_post(struct thread *td, semid_t id);
81 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
82     struct timespec *abstime);
83 static int kern_sem_init(struct thread *td, int dir, unsigned int value,
84     semid_t *idp);
85 static int kern_sem_open(struct thread *td, int dir, const char *name,
86     int oflag, mode_t mode, unsigned int value, semid_t *idp);
87 static int kern_sem_unlink(struct thread *td, const char *name);
88 
89 #ifndef SEM_MAX
90 #define SEM_MAX	30
91 #endif
92 
93 #define SEM_MAX_NAMELEN	14
94 
95 #define SEM_TO_ID(x)	((intptr_t)(x))
96 #define ID_TO_SEM(x)	id_to_sem(x)
97 
98 /*
99  * available semaphores go here, this includes sem_init and any semaphores
100  * created via sem_open that have not yet been unlinked.
101  */
102 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
103 /*
104  * semaphores still in use but have been sem_unlink()'d go here.
105  */
106 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
107 
108 static struct mtx sem_lock;
109 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
110 
111 static int nsems = 0;
112 SYSCTL_DECL(_p1003_1b);
113 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
114 
115 static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
116 
117 #ifdef SEM_DEBUG
118 #define DP(x)	printf x
119 #else
120 #define DP(x)
121 #endif
122 
123 static __inline
124 void
125 sem_ref(struct ksem *ks)
126 {
127 
128 	mtx_assert(&sem_lock, MA_OWNED);
129 	ks->ks_ref++;
130 	DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
131 }
132 
133 static __inline
134 void
135 sem_rel(struct ksem *ks)
136 {
137 
138 	mtx_assert(&sem_lock, MA_OWNED);
139 	DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
140 	if (--ks->ks_ref == 0)
141 		sem_free(ks);
142 }
143 
144 static __inline struct ksem *id_to_sem(semid_t id);
145 
146 static __inline
147 struct ksem *
148 id_to_sem(id)
149 	semid_t id;
150 {
151 	struct ksem *ks;
152 
153 	mtx_assert(&sem_lock, MA_OWNED);
154 	DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
155 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
156 		DP(("id_to_sem: ks = %p\n", ks));
157 		if (ks == (struct ksem *)id)
158 			return (ks);
159 	}
160 	return (NULL);
161 }
162 
163 static struct ksem *
164 sem_lookup_byname(name)
165 	const char *name;
166 {
167 	struct ksem *ks;
168 
169 	mtx_assert(&sem_lock, MA_OWNED);
170 	LIST_FOREACH(ks, &ksem_head, ks_entry)
171 		if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
172 			return (ks);
173 	return (NULL);
174 }
175 
176 static int
177 sem_create(td, name, ksret, mode, value)
178 	struct thread *td;
179 	const char *name;
180 	struct ksem **ksret;
181 	mode_t mode;
182 	unsigned int value;
183 {
184 	struct ksem *ret;
185 	struct proc *p;
186 	struct ucred *uc;
187 	size_t len;
188 	int error;
189 
190 	DP(("sem_create\n"));
191 	p = td->td_proc;
192 	uc = td->td_ucred;
193 	if (value > SEM_VALUE_MAX)
194 		return (EINVAL);
195 	ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
196 	if (name != NULL) {
197 		len = strlen(name);
198 		if (len > SEM_MAX_NAMELEN) {
199 			free(ret, M_SEM);
200 			return (ENAMETOOLONG);
201 		}
202 		/* name must start with a '/' but not contain one. */
203 		if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
204 			free(ret, M_SEM);
205 			return (EINVAL);
206 		}
207 		ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
208 		strcpy(ret->ks_name, name);
209 	} else {
210 		ret->ks_name = NULL;
211 	}
212 	ret->ks_mode = mode;
213 	ret->ks_value = value;
214 	ret->ks_ref = 1;
215 	ret->ks_waiters = 0;
216 	ret->ks_uid = uc->cr_uid;
217 	ret->ks_gid = uc->cr_gid;
218 	ret->ks_onlist = 0;
219 	cv_init(&ret->ks_cv, "sem");
220 	LIST_INIT(&ret->ks_users);
221 #ifdef MAC
222 	mac_init_posix_sem(ret);
223 	mac_create_posix_sem(uc, ret);
224 #endif
225 	if (name != NULL)
226 		sem_enter(td->td_proc, ret);
227 	*ksret = ret;
228 	mtx_lock(&sem_lock);
229 	if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
230 		sem_leave(td->td_proc, ret);
231 		sem_free(ret);
232 		error = ENFILE;
233 	} else {
234 		nsems++;
235 		error = 0;
236 	}
237 	mtx_unlock(&sem_lock);
238 	return (error);
239 }
240 
241 #ifndef _SYS_SYSPROTO_H_
242 struct ksem_init_args {
243 	unsigned int value;
244 	semid_t *idp;
245 };
246 int ksem_init(struct thread *td, struct ksem_init_args *uap);
247 #endif
248 int
249 ksem_init(td, uap)
250 	struct thread *td;
251 	struct ksem_init_args *uap;
252 {
253 	int error;
254 
255 	error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp);
256 	return (error);
257 }
258 
259 static int
260 kern_sem_init(td, dir, value, idp)
261 	struct thread *td;
262 	int dir;
263 	unsigned int value;
264 	semid_t *idp;
265 {
266 	struct ksem *ks;
267 	semid_t id;
268 	int error;
269 
270 	error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
271 	if (error)
272 		return (error);
273 	id = SEM_TO_ID(ks);
274 	if (dir == UIO_USERSPACE) {
275 		error = copyout(&id, idp, sizeof(id));
276 		if (error) {
277 			mtx_lock(&sem_lock);
278 			sem_rel(ks);
279 			mtx_unlock(&sem_lock);
280 			return (error);
281 		}
282 	} else {
283 		*idp = id;
284 	}
285 	mtx_lock(&sem_lock);
286 	LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
287 	ks->ks_onlist = 1;
288 	mtx_unlock(&sem_lock);
289 	return (error);
290 }
291 
292 #ifndef _SYS_SYSPROTO_H_
293 struct ksem_open_args {
294 	char *name;
295 	int oflag;
296 	mode_t mode;
297 	unsigned int value;
298 	semid_t *idp;
299 };
300 int ksem_open(struct thread *td, struct ksem_open_args *uap);
301 #endif
302 int
303 ksem_open(td, uap)
304 	struct thread *td;
305 	struct ksem_open_args *uap;
306 {
307 	char name[SEM_MAX_NAMELEN + 1];
308 	size_t done;
309 	int error;
310 
311 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
312 	if (error)
313 		return (error);
314 	DP((">>> sem_open start\n"));
315 	error = kern_sem_open(td, UIO_USERSPACE,
316 	    name, uap->oflag, uap->mode, uap->value, uap->idp);
317 	DP(("<<< sem_open end\n"));
318 	return (error);
319 }
320 
321 static int
322 kern_sem_open(td, dir, name, oflag, mode, value, idp)
323 	struct thread *td;
324 	int dir;
325 	const char *name;
326 	int oflag;
327 	mode_t mode;
328 	unsigned int value;
329 	semid_t *idp;
330 {
331 	struct ksem *ksnew, *ks;
332 	int error;
333 	semid_t id;
334 
335 	ksnew = NULL;
336 	mtx_lock(&sem_lock);
337 	ks = sem_lookup_byname(name);
338 	/*
339 	 * If we found it but O_EXCL is set, error.
340 	 */
341 	if (ks != NULL && (oflag & O_EXCL) != 0) {
342 		mtx_unlock(&sem_lock);
343 		return (EEXIST);
344 	}
345 	/*
346 	 * If we didn't find it...
347 	 */
348 	if (ks == NULL) {
349 		/*
350 		 * didn't ask for creation? error.
351 		 */
352 		if ((oflag & O_CREAT) == 0) {
353 			mtx_unlock(&sem_lock);
354 			return (ENOENT);
355 		}
356 		/*
357 		 * We may block during creation, so drop the lock.
358 		 */
359 		mtx_unlock(&sem_lock);
360 		error = sem_create(td, name, &ksnew, mode, value);
361 		if (error != 0)
362 			return (error);
363 		id = SEM_TO_ID(ksnew);
364 		if (dir == UIO_USERSPACE) {
365 			DP(("about to copyout! %d to %p\n", id, idp));
366 			error = copyout(&id, idp, sizeof(id));
367 			if (error) {
368 				mtx_lock(&sem_lock);
369 				sem_leave(td->td_proc, ksnew);
370 				sem_rel(ksnew);
371 				mtx_unlock(&sem_lock);
372 				return (error);
373 			}
374 		} else {
375 			DP(("about to set! %d to %p\n", id, idp));
376 			*idp = id;
377 		}
378 		/*
379 		 * We need to make sure we haven't lost a race while
380 		 * allocating during creation.
381 		 */
382 		mtx_lock(&sem_lock);
383 		ks = sem_lookup_byname(name);
384 		if (ks != NULL) {
385 			/* we lost... */
386 			sem_leave(td->td_proc, ksnew);
387 			sem_rel(ksnew);
388 			/* we lost and we can't loose... */
389 			if ((oflag & O_EXCL) != 0) {
390 				mtx_unlock(&sem_lock);
391 				return (EEXIST);
392 			}
393 		} else {
394 			DP(("sem_create: about to add to list...\n"));
395 			LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
396 			DP(("sem_create: setting list bit...\n"));
397 			ksnew->ks_onlist = 1;
398 			DP(("sem_create: done, about to unlock...\n"));
399 		}
400 	} else {
401 #ifdef MAC
402 		error = mac_check_posix_sem_open(td->td_ucred, ks);
403 		if (error)
404 			goto err_open;
405 #endif
406 		/*
407 		 * if we aren't the creator, then enforce permissions.
408 		 */
409 		error = sem_perm(td, ks);
410 		if (error)
411 			goto err_open;
412 		sem_ref(ks);
413 		mtx_unlock(&sem_lock);
414 		id = SEM_TO_ID(ks);
415 		if (dir == UIO_USERSPACE) {
416 			error = copyout(&id, idp, sizeof(id));
417 			if (error) {
418 				mtx_lock(&sem_lock);
419 				sem_rel(ks);
420 				mtx_unlock(&sem_lock);
421 				return (error);
422 			}
423 		} else {
424 			*idp = id;
425 		}
426 		sem_enter(td->td_proc, ks);
427 		mtx_lock(&sem_lock);
428 		sem_rel(ks);
429 	}
430 err_open:
431 	mtx_unlock(&sem_lock);
432 	return (error);
433 }
434 
435 static int
436 sem_perm(td, ks)
437 	struct thread *td;
438 	struct ksem *ks;
439 {
440 	struct ucred *uc;
441 
442 	uc = td->td_ucred;
443 	DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
444 	    uc->cr_uid, uc->cr_gid,
445 	     ks->ks_uid, ks->ks_gid, ks->ks_mode));
446 	if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
447 	    (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
448 	    (ks->ks_mode & S_IWOTH) != 0 || suser(td) == 0)
449 		return (0);
450 	return (EPERM);
451 }
452 
453 static void
454 sem_free(struct ksem *ks)
455 {
456 
457 	nsems--;
458 	if (ks->ks_onlist)
459 		LIST_REMOVE(ks, ks_entry);
460 	if (ks->ks_name != NULL)
461 		free(ks->ks_name, M_SEM);
462 	cv_destroy(&ks->ks_cv);
463 	free(ks, M_SEM);
464 }
465 
466 static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks);
467 
468 static __inline struct kuser *
469 sem_getuser(p, ks)
470 	struct proc *p;
471 	struct ksem *ks;
472 {
473 	struct kuser *k;
474 
475 	LIST_FOREACH(k, &ks->ks_users, ku_next)
476 		if (k->ku_pid == p->p_pid)
477 			return (k);
478 	return (NULL);
479 }
480 
481 static int
482 sem_hasopen(td, ks)
483 	struct thread *td;
484 	struct ksem *ks;
485 {
486 
487 	return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
488 	    || sem_getuser(td->td_proc, ks) != NULL);
489 }
490 
491 static int
492 sem_leave(p, ks)
493 	struct proc *p;
494 	struct ksem *ks;
495 {
496 	struct kuser *k;
497 
498 	DP(("sem_leave: ks = %p\n", ks));
499 	k = sem_getuser(p, ks);
500 	DP(("sem_leave: ks = %p, k = %p\n", ks, k));
501 	if (k != NULL) {
502 		LIST_REMOVE(k, ku_next);
503 		sem_rel(ks);
504 		DP(("sem_leave: about to free k\n"));
505 		free(k, M_SEM);
506 		DP(("sem_leave: returning\n"));
507 		return (0);
508 	}
509 	return (EINVAL);
510 }
511 
512 static void
513 sem_enter(p, ks)
514 	struct proc *p;
515 	struct ksem *ks;
516 {
517 	struct kuser *ku, *k;
518 
519 	ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
520 	ku->ku_pid = p->p_pid;
521 	mtx_lock(&sem_lock);
522 	k = sem_getuser(p, ks);
523 	if (k != NULL) {
524 		mtx_unlock(&sem_lock);
525 		free(ku, M_TEMP);
526 		return;
527 	}
528 	LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
529 	sem_ref(ks);
530 	mtx_unlock(&sem_lock);
531 }
532 
533 #ifndef _SYS_SYSPROTO_H_
534 struct ksem_unlink_args {
535 	char *name;
536 };
537 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
538 #endif
539 
540 int
541 ksem_unlink(td, uap)
542 	struct thread *td;
543 	struct ksem_unlink_args *uap;
544 {
545 	char name[SEM_MAX_NAMELEN + 1];
546 	size_t done;
547 	int error;
548 
549 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
550 	return (error ? error :
551 	    kern_sem_unlink(td, name));
552 }
553 
554 static int
555 kern_sem_unlink(td, name)
556 	struct thread *td;
557 	const char *name;
558 {
559 	struct ksem *ks;
560 	int error;
561 
562 	mtx_lock(&sem_lock);
563 	ks = sem_lookup_byname(name);
564 	if (ks != NULL) {
565 #ifdef MAC
566 		error = mac_check_posix_sem_unlink(td->td_ucred, ks);
567 		if (error) {
568 			mtx_unlock(&sem_lock);
569 			return (error);
570 		}
571 #endif
572 		error = sem_perm(td, ks);
573 	} else
574 		error = ENOENT;
575 	DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
576 	if (error == 0) {
577 		LIST_REMOVE(ks, ks_entry);
578 		LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
579 		sem_rel(ks);
580 	}
581 	mtx_unlock(&sem_lock);
582 	return (error);
583 }
584 
585 #ifndef _SYS_SYSPROTO_H_
586 struct ksem_close_args {
587 	semid_t id;
588 };
589 int ksem_close(struct thread *td, struct ksem_close_args *uap);
590 #endif
591 
592 int
593 ksem_close(struct thread *td, struct ksem_close_args *uap)
594 {
595 
596 	return (kern_sem_close(td, uap->id));
597 }
598 
599 static int
600 kern_sem_close(td, id)
601 	struct thread *td;
602 	semid_t id;
603 {
604 	struct ksem *ks;
605 	int error;
606 
607 	error = EINVAL;
608 	mtx_lock(&sem_lock);
609 	ks = ID_TO_SEM(id);
610 	/* this is not a valid operation for unnamed sems */
611 	if (ks != NULL && ks->ks_name != NULL)
612 		error = sem_leave(td->td_proc, ks);
613 	mtx_unlock(&sem_lock);
614 	return (error);
615 }
616 
617 #ifndef _SYS_SYSPROTO_H_
618 struct ksem_post_args {
619 	semid_t id;
620 };
621 int ksem_post(struct thread *td, struct ksem_post_args *uap);
622 #endif
623 int
624 ksem_post(td, uap)
625 	struct thread *td;
626 	struct ksem_post_args *uap;
627 {
628 
629 	return (kern_sem_post(td, uap->id));
630 }
631 
632 static int
633 kern_sem_post(td, id)
634 	struct thread *td;
635 	semid_t id;
636 {
637 	struct ksem *ks;
638 	int error;
639 
640 	mtx_lock(&sem_lock);
641 	ks = ID_TO_SEM(id);
642 	if (ks == NULL || !sem_hasopen(td, ks)) {
643 		error = EINVAL;
644 		goto err;
645 	}
646 #ifdef MAC
647 	error = mac_check_posix_sem_post(td->td_ucred, ks);
648 	if (error)
649 		goto err;
650 #endif
651 	if (ks->ks_value == SEM_VALUE_MAX) {
652 		error = EOVERFLOW;
653 		goto err;
654 	}
655 	++ks->ks_value;
656 	if (ks->ks_waiters > 0)
657 		cv_signal(&ks->ks_cv);
658 	error = 0;
659 err:
660 	mtx_unlock(&sem_lock);
661 	return (error);
662 }
663 
664 #ifndef _SYS_SYSPROTO_H_
665 struct ksem_wait_args {
666 	semid_t id;
667 };
668 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
669 #endif
670 
671 int
672 ksem_wait(td, uap)
673 	struct thread *td;
674 	struct ksem_wait_args *uap;
675 {
676 
677 	return (kern_sem_wait(td, uap->id, 0, NULL));
678 }
679 
680 #ifndef _SYS_SYSPROTO_H_
681 struct ksem_timedwait_args {
682 	semid_t id;
683 	const struct timespec *abstime;
684 };
685 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
686 #endif
687 int
688 ksem_timedwait(td, uap)
689 	struct thread *td;
690 	struct ksem_timedwait_args *uap;
691 {
692 	struct timespec abstime;
693 	struct timespec *ts;
694 	int error;
695 
696 	/* We allow a null timespec (wait forever). */
697 	if (uap->abstime == NULL)
698 		ts = NULL;
699 	else {
700 		error = copyin(uap->abstime, &abstime, sizeof(abstime));
701 		if (error != 0)
702 			return (error);
703 		if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
704 			return (EINVAL);
705 		ts = &abstime;
706 	}
707 	return (kern_sem_wait(td, uap->id, 0, ts));
708 }
709 
710 #ifndef _SYS_SYSPROTO_H_
711 struct ksem_trywait_args {
712 	semid_t id;
713 };
714 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
715 #endif
716 int
717 ksem_trywait(td, uap)
718 	struct thread *td;
719 	struct ksem_trywait_args *uap;
720 {
721 
722 	return (kern_sem_wait(td, uap->id, 1, NULL));
723 }
724 
725 static int
726 kern_sem_wait(td, id, tryflag, abstime)
727 	struct thread *td;
728 	semid_t id;
729 	int tryflag;
730 	struct timespec *abstime;
731 {
732 	struct timespec ts1, ts2;
733 	struct timeval tv;
734 	struct ksem *ks;
735 	int error;
736 
737 	DP((">>> kern_sem_wait entered!\n"));
738 	mtx_lock(&sem_lock);
739 	ks = ID_TO_SEM(id);
740 	if (ks == NULL) {
741 		DP(("kern_sem_wait ks == NULL\n"));
742 		error = EINVAL;
743 		goto err;
744 	}
745 	sem_ref(ks);
746 	if (!sem_hasopen(td, ks)) {
747 		DP(("kern_sem_wait hasopen failed\n"));
748 		error = EINVAL;
749 		goto err;
750 	}
751 #ifdef MAC
752 	error = mac_check_posix_sem_wait(td->td_ucred, ks);
753 	if (error) {
754 		DP(("kern_sem_wait mac failed\n"));
755 		goto err;
756 	}
757 #endif
758 	DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
759 	if (ks->ks_value == 0) {
760 		ks->ks_waiters++;
761 		if (tryflag != 0)
762 			error = EAGAIN;
763 		else if (abstime == NULL)
764 			error = cv_wait_sig(&ks->ks_cv, &sem_lock);
765 		else {
766 			for (;;) {
767 				ts1 = *abstime;
768 				getnanotime(&ts2);
769 				timespecsub(&ts1, &ts2);
770 				TIMESPEC_TO_TIMEVAL(&tv, &ts1);
771 				if (tv.tv_sec < 0) {
772 					error = ETIMEDOUT;
773 					break;
774 				}
775 				error = cv_timedwait_sig(&ks->ks_cv,
776 				    &sem_lock, tvtohz(&tv));
777 				if (error != EWOULDBLOCK)
778 					break;
779 			}
780 		}
781 		ks->ks_waiters--;
782 		if (error)
783 			goto err;
784 	}
785 	ks->ks_value--;
786 	error = 0;
787 err:
788 	if (ks != NULL)
789 		sem_rel(ks);
790 	mtx_unlock(&sem_lock);
791 	DP(("<<< kern_sem_wait leaving, error = %d\n", error));
792 	return (error);
793 }
794 
795 #ifndef _SYS_SYSPROTO_H_
796 struct ksem_getvalue_args {
797 	semid_t id;
798 	int *val;
799 };
800 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
801 #endif
802 int
803 ksem_getvalue(td, uap)
804 	struct thread *td;
805 	struct ksem_getvalue_args *uap;
806 {
807 	struct ksem *ks;
808 	int error, val;
809 
810 	mtx_lock(&sem_lock);
811 	ks = ID_TO_SEM(uap->id);
812 	if (ks == NULL || !sem_hasopen(td, ks)) {
813 		mtx_unlock(&sem_lock);
814 		return (EINVAL);
815 	}
816 #ifdef MAC
817 	error = mac_check_posix_sem_getvalue(td->td_ucred, ks);
818 	if (error) {
819 		mtx_unlock(&sem_lock);
820 		return (error);
821 	}
822 #endif
823 	val = ks->ks_value;
824 	mtx_unlock(&sem_lock);
825 	error = copyout(&val, uap->val, sizeof(val));
826 	return (error);
827 }
828 
829 #ifndef _SYS_SYSPROTO_H_
830 struct ksem_destroy_args {
831 	semid_t id;
832 };
833 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
834 #endif
835 int
836 ksem_destroy(td, uap)
837 	struct thread *td;
838 	struct ksem_destroy_args *uap;
839 {
840 	struct ksem *ks;
841 	int error;
842 
843 	mtx_lock(&sem_lock);
844 	ks = ID_TO_SEM(uap->id);
845 	if (ks == NULL || !sem_hasopen(td, ks) ||
846 	    ks->ks_name != NULL) {
847 		error = EINVAL;
848 		goto err;
849 	}
850 #ifdef MAC
851 	error = mac_check_posix_sem_destroy(td->td_ucred, ks);
852 	if (error)
853 		goto err;
854 #endif
855 	if (ks->ks_waiters != 0) {
856 		error = EBUSY;
857 		goto err;
858 	}
859 	sem_rel(ks);
860 	error = 0;
861 err:
862 	mtx_unlock(&sem_lock);
863 	return (error);
864 }
865 
866 /*
867  * Count the number of kusers associated with a proc, so as to guess at how
868  * many to allocate when forking.
869  */
870 static int
871 sem_count_proc(p)
872 	struct proc *p;
873 {
874 	struct ksem *ks;
875 	struct kuser *ku;
876 	int count;
877 
878 	mtx_assert(&sem_lock, MA_OWNED);
879 
880 	count = 0;
881 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
882 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
883 			if (ku->ku_pid == p->p_pid)
884 				count++;
885 		}
886 	}
887 	LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
888 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
889 			if (ku->ku_pid == p->p_pid)
890 				count++;
891 		}
892 	}
893 	return (count);
894 }
895 
896 /*
897  * When a process forks, the child process must gain a reference to each open
898  * semaphore in the parent process, whether it is unlinked or not.  This
899  * requires allocating a kuser structure for each semaphore reference in the
900  * new process.  Because the set of semaphores in the parent can change while
901  * the fork is in progress, we have to handle races -- first we attempt to
902  * allocate enough storage to acquire references to each of the semaphores,
903  * then we enter the semaphores and release the temporary references.
904  */
905 static void
906 sem_forkhook(arg, p1, p2, flags)
907 	void *arg;
908 	struct proc *p1;
909 	struct proc *p2;
910 	int flags;
911 {
912 	struct ksem *ks, **sem_array;
913 	int count, i, new_count;
914 	struct kuser *ku;
915 
916 	mtx_lock(&sem_lock);
917 	count = sem_count_proc(p1);
918 	if (count == 0) {
919 		mtx_unlock(&sem_lock);
920 		return;
921 	}
922 race_lost:
923 	mtx_assert(&sem_lock, MA_OWNED);
924 	mtx_unlock(&sem_lock);
925 	sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
926 	mtx_lock(&sem_lock);
927 	new_count = sem_count_proc(p1);
928 	if (count < new_count) {
929 		/* Lost race, repeat and allocate more storage. */
930 		free(sem_array, M_TEMP);
931 		count = new_count;
932 		goto race_lost;
933 	}
934 	/*
935 	 * Given an array capable of storing an adequate number of semaphore
936 	 * references, now walk the list of semaphores and acquire a new
937 	 * reference for any semaphore opened by p1.
938 	 */
939 	count = new_count;
940 	i = 0;
941 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
942 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
943 			if (ku->ku_pid == p1->p_pid) {
944 				sem_ref(ks);
945 				sem_array[i] = ks;
946 				i++;
947 				break;
948 			}
949 		}
950 	}
951 	LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
952 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
953 			if (ku->ku_pid == p1->p_pid) {
954 				sem_ref(ks);
955 				sem_array[i] = ks;
956 				i++;
957 				break;
958 			}
959 		}
960 	}
961 	mtx_unlock(&sem_lock);
962 	KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
963 	/*
964 	 * Now cause p2 to enter each of the referenced semaphores, then
965 	 * release our temporary reference.  This is pretty inefficient.
966 	 * Finally, free our temporary array.
967 	 */
968 	for (i = 0; i < count; i++) {
969 		sem_enter(p2, sem_array[i]);
970 		mtx_lock(&sem_lock);
971 		sem_rel(sem_array[i]);
972 		mtx_unlock(&sem_lock);
973 	}
974 	free(sem_array, M_TEMP);
975 }
976 
977 static void
978 sem_exithook(arg, p)
979 	void *arg;
980 	struct proc *p;
981 {
982 	struct ksem *ks, *ksnext;
983 
984 	mtx_lock(&sem_lock);
985 	ks = LIST_FIRST(&ksem_head);
986 	while (ks != NULL) {
987 		ksnext = LIST_NEXT(ks, ks_entry);
988 		sem_leave(p, ks);
989 		ks = ksnext;
990 	}
991 	ks = LIST_FIRST(&ksem_deadhead);
992 	while (ks != NULL) {
993 		ksnext = LIST_NEXT(ks, ks_entry);
994 		sem_leave(p, ks);
995 		ks = ksnext;
996 	}
997 	mtx_unlock(&sem_lock);
998 }
999 
1000 static int
1001 sem_modload(struct module *module, int cmd, void *arg)
1002 {
1003         int error = 0;
1004 
1005         switch (cmd) {
1006         case MOD_LOAD:
1007 		mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
1008 		p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
1009 		p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
1010 		sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook,
1011 		    NULL, EVENTHANDLER_PRI_ANY);
1012 		sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exithook,
1013 		    NULL, EVENTHANDLER_PRI_ANY);
1014 		sem_fork_tag = EVENTHANDLER_REGISTER(process_fork, sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
1015                 break;
1016         case MOD_UNLOAD:
1017 		if (nsems != 0) {
1018 			error = EOPNOTSUPP;
1019 			break;
1020 		}
1021 		EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
1022 		EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
1023 		EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
1024 		mtx_destroy(&sem_lock);
1025                 break;
1026         case MOD_SHUTDOWN:
1027                 break;
1028         default:
1029                 error = EINVAL;
1030                 break;
1031         }
1032         return (error);
1033 }
1034 
1035 static moduledata_t sem_mod = {
1036         "sem",
1037         &sem_modload,
1038         NULL
1039 };
1040 
1041 SYSCALL_MODULE_HELPER(ksem_init);
1042 SYSCALL_MODULE_HELPER(ksem_open);
1043 SYSCALL_MODULE_HELPER(ksem_unlink);
1044 SYSCALL_MODULE_HELPER(ksem_close);
1045 SYSCALL_MODULE_HELPER(ksem_post);
1046 SYSCALL_MODULE_HELPER(ksem_wait);
1047 SYSCALL_MODULE_HELPER(ksem_timedwait);
1048 SYSCALL_MODULE_HELPER(ksem_trywait);
1049 SYSCALL_MODULE_HELPER(ksem_getvalue);
1050 SYSCALL_MODULE_HELPER(ksem_destroy);
1051 
1052 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1053 MODULE_VERSION(sem, 1);
1054