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