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