xref: /freebsd/sys/kern/uipc_sem.c (revision e72055b7feba695a760d45f01f0f8268b1cb4a74)
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_compat.h"
38 #include "opt_posix.h"
39 
40 #include <sys/param.h>
41 #include <sys/capsicum.h>
42 #include <sys/condvar.h>
43 #include <sys/fcntl.h>
44 #include <sys/file.h>
45 #include <sys/filedesc.h>
46 #include <sys/fnv_hash.h>
47 #include <sys/kernel.h>
48 #include <sys/ksem.h>
49 #include <sys/lock.h>
50 #include <sys/malloc.h>
51 #include <sys/module.h>
52 #include <sys/mutex.h>
53 #include <sys/priv.h>
54 #include <sys/proc.h>
55 #include <sys/posix4.h>
56 #include <sys/_semaphore.h>
57 #include <sys/stat.h>
58 #include <sys/syscall.h>
59 #include <sys/syscallsubr.h>
60 #include <sys/sysctl.h>
61 #include <sys/sysent.h>
62 #include <sys/sysproto.h>
63 #include <sys/systm.h>
64 #include <sys/sx.h>
65 #include <sys/vnode.h>
66 
67 #include <security/mac/mac_framework.h>
68 
69 FEATURE(p1003_1b_semaphores, "POSIX P1003.1B semaphores support");
70 /*
71  * TODO
72  *
73  * - Resource limits?
74  * - Replace global sem_lock with mtx_pool locks?
75  * - Add a MAC check_create() hook for creating new named semaphores.
76  */
77 
78 #ifndef SEM_MAX
79 #define	SEM_MAX	30
80 #endif
81 
82 #ifdef SEM_DEBUG
83 #define	DP(x)	printf x
84 #else
85 #define	DP(x)
86 #endif
87 
88 struct ksem_mapping {
89 	char		*km_path;
90 	Fnv32_t		km_fnv;
91 	struct ksem	*km_ksem;
92 	LIST_ENTRY(ksem_mapping) km_link;
93 };
94 
95 static MALLOC_DEFINE(M_KSEM, "ksem", "semaphore file descriptor");
96 static LIST_HEAD(, ksem_mapping) *ksem_dictionary;
97 static struct sx ksem_dict_lock;
98 static struct mtx ksem_count_lock;
99 static struct mtx sem_lock;
100 static u_long ksem_hash;
101 static int ksem_dead;
102 
103 #define	KSEM_HASH(fnv)	(&ksem_dictionary[(fnv) & ksem_hash])
104 
105 static int nsems = 0;
106 SYSCTL_DECL(_p1003_1b);
107 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0,
108     "Number of active kernel POSIX semaphores");
109 
110 static int	kern_sem_wait(struct thread *td, semid_t id, int tryflag,
111 		    struct timespec *abstime);
112 static int	ksem_access(struct ksem *ks, struct ucred *ucred);
113 static struct ksem *ksem_alloc(struct ucred *ucred, mode_t mode,
114 		    unsigned int value);
115 static int	ksem_create(struct thread *td, const char *path,
116 		    semid_t *semidp, mode_t mode, unsigned int value,
117 		    int flags, int compat32);
118 static void	ksem_drop(struct ksem *ks);
119 static int	ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp,
120     struct file **fpp);
121 static struct ksem *ksem_hold(struct ksem *ks);
122 static void	ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks);
123 static struct ksem *ksem_lookup(char *path, Fnv32_t fnv);
124 static void	ksem_module_destroy(void);
125 static int	ksem_module_init(void);
126 static int	ksem_remove(char *path, Fnv32_t fnv, struct ucred *ucred);
127 static int	sem_modload(struct module *module, int cmd, void *arg);
128 
129 static fo_stat_t	ksem_stat;
130 static fo_close_t	ksem_closef;
131 static fo_chmod_t	ksem_chmod;
132 static fo_chown_t	ksem_chown;
133 
134 /* File descriptor operations. */
135 static struct fileops ksem_ops = {
136 	.fo_read = invfo_rdwr,
137 	.fo_write = invfo_rdwr,
138 	.fo_truncate = invfo_truncate,
139 	.fo_ioctl = invfo_ioctl,
140 	.fo_poll = invfo_poll,
141 	.fo_kqfilter = invfo_kqfilter,
142 	.fo_stat = ksem_stat,
143 	.fo_close = ksem_closef,
144 	.fo_chmod = ksem_chmod,
145 	.fo_chown = ksem_chown,
146 	.fo_sendfile = invfo_sendfile,
147 	.fo_flags = DFLAG_PASSABLE
148 };
149 
150 FEATURE(posix_sem, "POSIX semaphores");
151 
152 static int
153 ksem_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
154     struct thread *td)
155 {
156 	struct ksem *ks;
157 #ifdef MAC
158 	int error;
159 #endif
160 
161 	ks = fp->f_data;
162 
163 #ifdef MAC
164 	error = mac_posixsem_check_stat(active_cred, fp->f_cred, ks);
165 	if (error)
166 		return (error);
167 #endif
168 
169 	/*
170 	 * Attempt to return sanish values for fstat() on a semaphore
171 	 * file descriptor.
172 	 */
173 	bzero(sb, sizeof(*sb));
174 
175 	mtx_lock(&sem_lock);
176 	sb->st_atim = ks->ks_atime;
177 	sb->st_ctim = ks->ks_ctime;
178 	sb->st_mtim = ks->ks_mtime;
179 	sb->st_birthtim = ks->ks_birthtime;
180 	sb->st_uid = ks->ks_uid;
181 	sb->st_gid = ks->ks_gid;
182 	sb->st_mode = S_IFREG | ks->ks_mode;		/* XXX */
183 	mtx_unlock(&sem_lock);
184 
185 	return (0);
186 }
187 
188 static int
189 ksem_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
190     struct thread *td)
191 {
192 	struct ksem *ks;
193 	int error;
194 
195 	error = 0;
196 	ks = fp->f_data;
197 	mtx_lock(&sem_lock);
198 #ifdef MAC
199 	error = mac_posixsem_check_setmode(active_cred, ks, mode);
200 	if (error != 0)
201 		goto out;
202 #endif
203 	error = vaccess(VREG, ks->ks_mode, ks->ks_uid, ks->ks_gid, VADMIN,
204 	    active_cred, NULL);
205 	if (error != 0)
206 		goto out;
207 	ks->ks_mode = mode & ACCESSPERMS;
208 out:
209 	mtx_unlock(&sem_lock);
210 	return (error);
211 }
212 
213 static int
214 ksem_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
215     struct thread *td)
216 {
217 	struct ksem *ks;
218 	int error;
219 
220 	error = 0;
221 	ks = fp->f_data;
222 	mtx_lock(&sem_lock);
223 #ifdef MAC
224 	error = mac_posixsem_check_setowner(active_cred, ks, uid, gid);
225 	if (error != 0)
226 		goto out;
227 #endif
228 	if (uid == (uid_t)-1)
229 		uid = ks->ks_uid;
230 	if (gid == (gid_t)-1)
231                  gid = ks->ks_gid;
232 	if (((uid != ks->ks_uid && uid != active_cred->cr_uid) ||
233 	    (gid != ks->ks_gid && !groupmember(gid, active_cred))) &&
234 	    (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0)))
235 		goto out;
236 	ks->ks_uid = uid;
237 	ks->ks_gid = gid;
238 out:
239 	mtx_unlock(&sem_lock);
240 	return (error);
241 }
242 
243 static int
244 ksem_closef(struct file *fp, struct thread *td)
245 {
246 	struct ksem *ks;
247 
248 	ks = fp->f_data;
249 	fp->f_data = NULL;
250 	ksem_drop(ks);
251 
252 	return (0);
253 }
254 
255 /*
256  * ksem object management including creation and reference counting
257  * routines.
258  */
259 static struct ksem *
260 ksem_alloc(struct ucred *ucred, mode_t mode, unsigned int value)
261 {
262 	struct ksem *ks;
263 
264 	mtx_lock(&ksem_count_lock);
265 	if (nsems == p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX) || ksem_dead) {
266 		mtx_unlock(&ksem_count_lock);
267 		return (NULL);
268 	}
269 	nsems++;
270 	mtx_unlock(&ksem_count_lock);
271 	ks = malloc(sizeof(*ks), M_KSEM, M_WAITOK | M_ZERO);
272 	ks->ks_uid = ucred->cr_uid;
273 	ks->ks_gid = ucred->cr_gid;
274 	ks->ks_mode = mode;
275 	ks->ks_value = value;
276 	cv_init(&ks->ks_cv, "ksem");
277 	vfs_timestamp(&ks->ks_birthtime);
278 	ks->ks_atime = ks->ks_mtime = ks->ks_ctime = ks->ks_birthtime;
279 	refcount_init(&ks->ks_ref, 1);
280 #ifdef MAC
281 	mac_posixsem_init(ks);
282 	mac_posixsem_create(ucred, ks);
283 #endif
284 
285 	return (ks);
286 }
287 
288 static struct ksem *
289 ksem_hold(struct ksem *ks)
290 {
291 
292 	refcount_acquire(&ks->ks_ref);
293 	return (ks);
294 }
295 
296 static void
297 ksem_drop(struct ksem *ks)
298 {
299 
300 	if (refcount_release(&ks->ks_ref)) {
301 #ifdef MAC
302 		mac_posixsem_destroy(ks);
303 #endif
304 		cv_destroy(&ks->ks_cv);
305 		free(ks, M_KSEM);
306 		mtx_lock(&ksem_count_lock);
307 		nsems--;
308 		mtx_unlock(&ksem_count_lock);
309 	}
310 }
311 
312 /*
313  * Determine if the credentials have sufficient permissions for read
314  * and write access.
315  */
316 static int
317 ksem_access(struct ksem *ks, struct ucred *ucred)
318 {
319 	int error;
320 
321 	error = vaccess(VREG, ks->ks_mode, ks->ks_uid, ks->ks_gid,
322 	    VREAD | VWRITE, ucred, NULL);
323 	if (error)
324 		error = priv_check_cred(ucred, PRIV_SEM_WRITE, 0);
325 	return (error);
326 }
327 
328 /*
329  * Dictionary management.  We maintain an in-kernel dictionary to map
330  * paths to semaphore objects.  We use the FNV hash on the path to
331  * store the mappings in a hash table.
332  */
333 static struct ksem *
334 ksem_lookup(char *path, Fnv32_t fnv)
335 {
336 	struct ksem_mapping *map;
337 
338 	LIST_FOREACH(map, KSEM_HASH(fnv), km_link) {
339 		if (map->km_fnv != fnv)
340 			continue;
341 		if (strcmp(map->km_path, path) == 0)
342 			return (map->km_ksem);
343 	}
344 
345 	return (NULL);
346 }
347 
348 static void
349 ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks)
350 {
351 	struct ksem_mapping *map;
352 
353 	map = malloc(sizeof(struct ksem_mapping), M_KSEM, M_WAITOK);
354 	map->km_path = path;
355 	map->km_fnv = fnv;
356 	map->km_ksem = ksem_hold(ks);
357 	ks->ks_path = path;
358 	LIST_INSERT_HEAD(KSEM_HASH(fnv), map, km_link);
359 }
360 
361 static int
362 ksem_remove(char *path, Fnv32_t fnv, struct ucred *ucred)
363 {
364 	struct ksem_mapping *map;
365 	int error;
366 
367 	LIST_FOREACH(map, KSEM_HASH(fnv), km_link) {
368 		if (map->km_fnv != fnv)
369 			continue;
370 		if (strcmp(map->km_path, path) == 0) {
371 #ifdef MAC
372 			error = mac_posixsem_check_unlink(ucred, map->km_ksem);
373 			if (error)
374 				return (error);
375 #endif
376 			error = ksem_access(map->km_ksem, ucred);
377 			if (error)
378 				return (error);
379 			map->km_ksem->ks_path = NULL;
380 			LIST_REMOVE(map, km_link);
381 			ksem_drop(map->km_ksem);
382 			free(map->km_path, M_KSEM);
383 			free(map, M_KSEM);
384 			return (0);
385 		}
386 	}
387 
388 	return (ENOENT);
389 }
390 
391 static void
392 ksem_info_impl(struct ksem *ks, char *path, size_t size, uint32_t *value)
393 {
394 
395 	if (ks->ks_path == NULL)
396 		return;
397 	sx_slock(&ksem_dict_lock);
398 	if (ks->ks_path != NULL)
399 		strlcpy(path, ks->ks_path, size);
400 	if (value != NULL)
401 		*value = ks->ks_value;
402 	sx_sunlock(&ksem_dict_lock);
403 }
404 
405 static int
406 ksem_create_copyout_semid(struct thread *td, semid_t *semidp, int fd,
407     int compat32)
408 {
409 	semid_t semid;
410 #ifdef COMPAT_FREEBSD32
411 	int32_t semid32;
412 #endif
413 	void *ptr;
414 	size_t ptrs;
415 
416 #ifdef COMPAT_FREEBSD32
417 	if (compat32) {
418 		semid32 = fd;
419 		ptr = &semid32;
420 		ptrs = sizeof(semid32);
421 	} else {
422 #endif
423 		semid = fd;
424 		ptr = &semid;
425 		ptrs = sizeof(semid);
426 		compat32 = 0; /* silence gcc */
427 #ifdef COMPAT_FREEBSD32
428 	}
429 #endif
430 
431 	return (copyout(ptr, semidp, ptrs));
432 }
433 
434 /* Other helper routines. */
435 static int
436 ksem_create(struct thread *td, const char *name, semid_t *semidp, mode_t mode,
437     unsigned int value, int flags, int compat32)
438 {
439 	struct filedesc *fdp;
440 	struct ksem *ks;
441 	struct file *fp;
442 	char *path;
443 	Fnv32_t fnv;
444 	int error, fd;
445 
446 	if (value > SEM_VALUE_MAX)
447 		return (EINVAL);
448 
449 	fdp = td->td_proc->p_fd;
450 	mode = (mode & ~fdp->fd_cmask) & ACCESSPERMS;
451 	error = falloc(td, &fp, &fd, O_CLOEXEC);
452 	if (error) {
453 		if (name == NULL)
454 			error = ENOSPC;
455 		return (error);
456 	}
457 
458 	/*
459 	 * Go ahead and copyout the file descriptor now.  This is a bit
460 	 * premature, but it is a lot easier to handle errors as opposed
461 	 * to later when we've possibly created a new semaphore, etc.
462 	 */
463 	error = ksem_create_copyout_semid(td, semidp, fd, compat32);
464 	if (error) {
465 		fdclose(fdp, fp, fd, td);
466 		fdrop(fp, td);
467 		return (error);
468 	}
469 
470 	if (name == NULL) {
471 		/* Create an anonymous semaphore. */
472 		ks = ksem_alloc(td->td_ucred, mode, value);
473 		if (ks == NULL)
474 			error = ENOSPC;
475 		else
476 			ks->ks_flags |= KS_ANONYMOUS;
477 	} else {
478 		path = malloc(MAXPATHLEN, M_KSEM, M_WAITOK);
479 		error = copyinstr(name, path, MAXPATHLEN, NULL);
480 
481 		/* Require paths to start with a '/' character. */
482 		if (error == 0 && path[0] != '/')
483 			error = EINVAL;
484 		if (error) {
485 			fdclose(fdp, fp, fd, td);
486 			fdrop(fp, td);
487 			free(path, M_KSEM);
488 			return (error);
489 		}
490 
491 		fnv = fnv_32_str(path, FNV1_32_INIT);
492 		sx_xlock(&ksem_dict_lock);
493 		ks = ksem_lookup(path, fnv);
494 		if (ks == NULL) {
495 			/* Object does not exist, create it if requested. */
496 			if (flags & O_CREAT) {
497 				ks = ksem_alloc(td->td_ucred, mode, value);
498 				if (ks == NULL)
499 					error = ENFILE;
500 				else {
501 					ksem_insert(path, fnv, ks);
502 					path = NULL;
503 				}
504 			} else
505 				error = ENOENT;
506 		} else {
507 			/*
508 			 * Object already exists, obtain a new
509 			 * reference if requested and permitted.
510 			 */
511 			if ((flags & (O_CREAT | O_EXCL)) ==
512 			    (O_CREAT | O_EXCL))
513 				error = EEXIST;
514 			else {
515 #ifdef MAC
516 				error = mac_posixsem_check_open(td->td_ucred,
517 				    ks);
518 				if (error == 0)
519 #endif
520 				error = ksem_access(ks, td->td_ucred);
521 			}
522 			if (error == 0)
523 				ksem_hold(ks);
524 #ifdef INVARIANTS
525 			else
526 				ks = NULL;
527 #endif
528 		}
529 		sx_xunlock(&ksem_dict_lock);
530 		if (path)
531 			free(path, M_KSEM);
532 	}
533 
534 	if (error) {
535 		KASSERT(ks == NULL, ("ksem_create error with a ksem"));
536 		fdclose(fdp, fp, fd, td);
537 		fdrop(fp, td);
538 		return (error);
539 	}
540 	KASSERT(ks != NULL, ("ksem_create w/o a ksem"));
541 
542 	finit(fp, FREAD | FWRITE, DTYPE_SEM, ks, &ksem_ops);
543 
544 	fdrop(fp, td);
545 
546 	return (0);
547 }
548 
549 static int
550 ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp,
551     struct file **fpp)
552 {
553 	struct ksem *ks;
554 	struct file *fp;
555 	int error;
556 
557 	error = fget(td, id, rightsp, &fp);
558 	if (error)
559 		return (EINVAL);
560 	if (fp->f_type != DTYPE_SEM) {
561 		fdrop(fp, td);
562 		return (EINVAL);
563 	}
564 	ks = fp->f_data;
565 	if (ks->ks_flags & KS_DEAD) {
566 		fdrop(fp, td);
567 		return (EINVAL);
568 	}
569 	*fpp = fp;
570 	return (0);
571 }
572 
573 /* System calls. */
574 #ifndef _SYS_SYSPROTO_H_
575 struct ksem_init_args {
576 	unsigned int	value;
577 	semid_t		*idp;
578 };
579 #endif
580 int
581 sys_ksem_init(struct thread *td, struct ksem_init_args *uap)
582 {
583 
584 	return (ksem_create(td, NULL, uap->idp, S_IRWXU | S_IRWXG, uap->value,
585 	    0, 0));
586 }
587 
588 #ifndef _SYS_SYSPROTO_H_
589 struct ksem_open_args {
590 	char		*name;
591 	int		oflag;
592 	mode_t		mode;
593 	unsigned int	value;
594 	semid_t		*idp;
595 };
596 #endif
597 int
598 sys_ksem_open(struct thread *td, struct ksem_open_args *uap)
599 {
600 
601 	DP((">>> ksem_open start, pid=%d\n", (int)td->td_proc->p_pid));
602 
603 	if ((uap->oflag & ~(O_CREAT | O_EXCL)) != 0)
604 		return (EINVAL);
605 	return (ksem_create(td, uap->name, uap->idp, uap->mode, uap->value,
606 	    uap->oflag, 0));
607 }
608 
609 #ifndef _SYS_SYSPROTO_H_
610 struct ksem_unlink_args {
611 	char		*name;
612 };
613 #endif
614 int
615 sys_ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
616 {
617 	char *path;
618 	Fnv32_t fnv;
619 	int error;
620 
621 	path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
622 	error = copyinstr(uap->name, path, MAXPATHLEN, NULL);
623 	if (error) {
624 		free(path, M_TEMP);
625 		return (error);
626 	}
627 
628 	fnv = fnv_32_str(path, FNV1_32_INIT);
629 	sx_xlock(&ksem_dict_lock);
630 	error = ksem_remove(path, fnv, td->td_ucred);
631 	sx_xunlock(&ksem_dict_lock);
632 	free(path, M_TEMP);
633 
634 	return (error);
635 }
636 
637 #ifndef _SYS_SYSPROTO_H_
638 struct ksem_close_args {
639 	semid_t		id;
640 };
641 #endif
642 int
643 sys_ksem_close(struct thread *td, struct ksem_close_args *uap)
644 {
645 	struct ksem *ks;
646 	struct file *fp;
647 	int error;
648 
649 	/* No capability rights required to close a semaphore. */
650 	error = ksem_get(td, uap->id, 0, &fp);
651 	if (error)
652 		return (error);
653 	ks = fp->f_data;
654 	if (ks->ks_flags & KS_ANONYMOUS) {
655 		fdrop(fp, td);
656 		return (EINVAL);
657 	}
658 	error = kern_close(td, uap->id);
659 	fdrop(fp, td);
660 	return (error);
661 }
662 
663 #ifndef _SYS_SYSPROTO_H_
664 struct ksem_post_args {
665 	semid_t	id;
666 };
667 #endif
668 int
669 sys_ksem_post(struct thread *td, struct ksem_post_args *uap)
670 {
671 	cap_rights_t rights;
672 	struct file *fp;
673 	struct ksem *ks;
674 	int error;
675 
676 	error = ksem_get(td, uap->id,
677 	    cap_rights_init(&rights, CAP_SEM_POST), &fp);
678 	if (error)
679 		return (error);
680 	ks = fp->f_data;
681 
682 	mtx_lock(&sem_lock);
683 #ifdef MAC
684 	error = mac_posixsem_check_post(td->td_ucred, fp->f_cred, ks);
685 	if (error)
686 		goto err;
687 #endif
688 	if (ks->ks_value == SEM_VALUE_MAX) {
689 		error = EOVERFLOW;
690 		goto err;
691 	}
692 	++ks->ks_value;
693 	if (ks->ks_waiters > 0)
694 		cv_signal(&ks->ks_cv);
695 	error = 0;
696 	vfs_timestamp(&ks->ks_ctime);
697 err:
698 	mtx_unlock(&sem_lock);
699 	fdrop(fp, td);
700 	return (error);
701 }
702 
703 #ifndef _SYS_SYSPROTO_H_
704 struct ksem_wait_args {
705 	semid_t		id;
706 };
707 #endif
708 int
709 sys_ksem_wait(struct thread *td, struct ksem_wait_args *uap)
710 {
711 
712 	return (kern_sem_wait(td, uap->id, 0, NULL));
713 }
714 
715 #ifndef _SYS_SYSPROTO_H_
716 struct ksem_timedwait_args {
717 	semid_t		id;
718 	const struct timespec *abstime;
719 };
720 #endif
721 int
722 sys_ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
723 {
724 	struct timespec abstime;
725 	struct timespec *ts;
726 	int error;
727 
728 	/*
729 	 * We allow a null timespec (wait forever).
730 	 */
731 	if (uap->abstime == NULL)
732 		ts = NULL;
733 	else {
734 		error = copyin(uap->abstime, &abstime, sizeof(abstime));
735 		if (error != 0)
736 			return (error);
737 		if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
738 			return (EINVAL);
739 		ts = &abstime;
740 	}
741 	return (kern_sem_wait(td, uap->id, 0, ts));
742 }
743 
744 #ifndef _SYS_SYSPROTO_H_
745 struct ksem_trywait_args {
746 	semid_t		id;
747 };
748 #endif
749 int
750 sys_ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
751 {
752 
753 	return (kern_sem_wait(td, uap->id, 1, NULL));
754 }
755 
756 static int
757 kern_sem_wait(struct thread *td, semid_t id, int tryflag,
758     struct timespec *abstime)
759 {
760 	struct timespec ts1, ts2;
761 	struct timeval tv;
762 	cap_rights_t rights;
763 	struct file *fp;
764 	struct ksem *ks;
765 	int error;
766 
767 	DP((">>> kern_sem_wait entered! pid=%d\n", (int)td->td_proc->p_pid));
768 	error = ksem_get(td, id, cap_rights_init(&rights, CAP_SEM_WAIT), &fp);
769 	if (error)
770 		return (error);
771 	ks = fp->f_data;
772 	mtx_lock(&sem_lock);
773 	DP((">>> kern_sem_wait critical section entered! pid=%d\n",
774 	    (int)td->td_proc->p_pid));
775 #ifdef MAC
776 	error = mac_posixsem_check_wait(td->td_ucred, fp->f_cred, ks);
777 	if (error) {
778 		DP(("kern_sem_wait mac failed\n"));
779 		goto err;
780 	}
781 #endif
782 	DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
783 	vfs_timestamp(&ks->ks_atime);
784 	while (ks->ks_value == 0) {
785 		ks->ks_waiters++;
786 		if (tryflag != 0)
787 			error = EAGAIN;
788 		else if (abstime == NULL)
789 			error = cv_wait_sig(&ks->ks_cv, &sem_lock);
790 		else {
791 			for (;;) {
792 				ts1 = *abstime;
793 				getnanotime(&ts2);
794 				timespecsub(&ts1, &ts2);
795 				TIMESPEC_TO_TIMEVAL(&tv, &ts1);
796 				if (tv.tv_sec < 0) {
797 					error = ETIMEDOUT;
798 					break;
799 				}
800 				error = cv_timedwait_sig(&ks->ks_cv,
801 				    &sem_lock, tvtohz(&tv));
802 				if (error != EWOULDBLOCK)
803 					break;
804 			}
805 		}
806 		ks->ks_waiters--;
807 		if (error)
808 			goto err;
809 	}
810 	ks->ks_value--;
811 	DP(("kern_sem_wait value post-decrement = %d\n", ks->ks_value));
812 	error = 0;
813 err:
814 	mtx_unlock(&sem_lock);
815 	fdrop(fp, td);
816 	DP(("<<< kern_sem_wait leaving, pid=%d, error = %d\n",
817 	    (int)td->td_proc->p_pid, error));
818 	return (error);
819 }
820 
821 #ifndef _SYS_SYSPROTO_H_
822 struct ksem_getvalue_args {
823 	semid_t		id;
824 	int		*val;
825 };
826 #endif
827 int
828 sys_ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
829 {
830 	cap_rights_t rights;
831 	struct file *fp;
832 	struct ksem *ks;
833 	int error, val;
834 
835 	error = ksem_get(td, uap->id,
836 	    cap_rights_init(&rights, CAP_SEM_GETVALUE), &fp);
837 	if (error)
838 		return (error);
839 	ks = fp->f_data;
840 
841 	mtx_lock(&sem_lock);
842 #ifdef MAC
843 	error = mac_posixsem_check_getvalue(td->td_ucred, fp->f_cred, ks);
844 	if (error) {
845 		mtx_unlock(&sem_lock);
846 		fdrop(fp, td);
847 		return (error);
848 	}
849 #endif
850 	val = ks->ks_value;
851 	vfs_timestamp(&ks->ks_atime);
852 	mtx_unlock(&sem_lock);
853 	fdrop(fp, td);
854 	error = copyout(&val, uap->val, sizeof(val));
855 	return (error);
856 }
857 
858 #ifndef _SYS_SYSPROTO_H_
859 struct ksem_destroy_args {
860 	semid_t		id;
861 };
862 #endif
863 int
864 sys_ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
865 {
866 	struct file *fp;
867 	struct ksem *ks;
868 	int error;
869 
870 	/* No capability rights required to close a semaphore. */
871 	error = ksem_get(td, uap->id, 0, &fp);
872 	if (error)
873 		return (error);
874 	ks = fp->f_data;
875 	if (!(ks->ks_flags & KS_ANONYMOUS)) {
876 		fdrop(fp, td);
877 		return (EINVAL);
878 	}
879 	mtx_lock(&sem_lock);
880 	if (ks->ks_waiters != 0) {
881 		mtx_unlock(&sem_lock);
882 		error = EBUSY;
883 		goto err;
884 	}
885 	ks->ks_flags |= KS_DEAD;
886 	mtx_unlock(&sem_lock);
887 
888 	error = kern_close(td, uap->id);
889 err:
890 	fdrop(fp, td);
891 	return (error);
892 }
893 
894 static struct syscall_helper_data ksem_syscalls[] = {
895 	SYSCALL_INIT_HELPER(ksem_init),
896 	SYSCALL_INIT_HELPER(ksem_open),
897 	SYSCALL_INIT_HELPER(ksem_unlink),
898 	SYSCALL_INIT_HELPER(ksem_close),
899 	SYSCALL_INIT_HELPER(ksem_post),
900 	SYSCALL_INIT_HELPER(ksem_wait),
901 	SYSCALL_INIT_HELPER(ksem_timedwait),
902 	SYSCALL_INIT_HELPER(ksem_trywait),
903 	SYSCALL_INIT_HELPER(ksem_getvalue),
904 	SYSCALL_INIT_HELPER(ksem_destroy),
905 	SYSCALL_INIT_LAST
906 };
907 
908 #ifdef COMPAT_FREEBSD32
909 #include <compat/freebsd32/freebsd32.h>
910 #include <compat/freebsd32/freebsd32_proto.h>
911 #include <compat/freebsd32/freebsd32_signal.h>
912 #include <compat/freebsd32/freebsd32_syscall.h>
913 #include <compat/freebsd32/freebsd32_util.h>
914 
915 int
916 freebsd32_ksem_init(struct thread *td, struct freebsd32_ksem_init_args *uap)
917 {
918 
919 	return (ksem_create(td, NULL, uap->idp, S_IRWXU | S_IRWXG, uap->value,
920 	    0, 1));
921 }
922 
923 int
924 freebsd32_ksem_open(struct thread *td, struct freebsd32_ksem_open_args *uap)
925 {
926 
927 	if ((uap->oflag & ~(O_CREAT | O_EXCL)) != 0)
928 		return (EINVAL);
929 	return (ksem_create(td, uap->name, uap->idp, uap->mode, uap->value,
930 	    uap->oflag, 1));
931 }
932 
933 int
934 freebsd32_ksem_timedwait(struct thread *td,
935     struct freebsd32_ksem_timedwait_args *uap)
936 {
937 	struct timespec32 abstime32;
938 	struct timespec *ts, abstime;
939 	int error;
940 
941 	/*
942 	 * We allow a null timespec (wait forever).
943 	 */
944 	if (uap->abstime == NULL)
945 		ts = NULL;
946 	else {
947 		error = copyin(uap->abstime, &abstime32, sizeof(abstime32));
948 		if (error != 0)
949 			return (error);
950 		CP(abstime32, abstime, tv_sec);
951 		CP(abstime32, abstime, tv_nsec);
952 		if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
953 			return (EINVAL);
954 		ts = &abstime;
955 	}
956 	return (kern_sem_wait(td, uap->id, 0, ts));
957 }
958 
959 static struct syscall_helper_data ksem32_syscalls[] = {
960 	SYSCALL32_INIT_HELPER(freebsd32_ksem_init),
961 	SYSCALL32_INIT_HELPER(freebsd32_ksem_open),
962 	SYSCALL32_INIT_HELPER_COMPAT(ksem_unlink),
963 	SYSCALL32_INIT_HELPER_COMPAT(ksem_close),
964 	SYSCALL32_INIT_HELPER_COMPAT(ksem_post),
965 	SYSCALL32_INIT_HELPER_COMPAT(ksem_wait),
966 	SYSCALL32_INIT_HELPER(freebsd32_ksem_timedwait),
967 	SYSCALL32_INIT_HELPER_COMPAT(ksem_trywait),
968 	SYSCALL32_INIT_HELPER_COMPAT(ksem_getvalue),
969 	SYSCALL32_INIT_HELPER_COMPAT(ksem_destroy),
970 	SYSCALL_INIT_LAST
971 };
972 #endif
973 
974 static int
975 ksem_module_init(void)
976 {
977 	int error;
978 
979 	mtx_init(&sem_lock, "sem", NULL, MTX_DEF);
980 	mtx_init(&ksem_count_lock, "ksem count", NULL, MTX_DEF);
981 	sx_init(&ksem_dict_lock, "ksem dictionary");
982 	ksem_dictionary = hashinit(1024, M_KSEM, &ksem_hash);
983 	p31b_setcfg(CTL_P1003_1B_SEMAPHORES, 200112L);
984 	p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
985 	p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
986 	ksem_info = ksem_info_impl;
987 
988 	error = syscall_helper_register(ksem_syscalls);
989 	if (error)
990 		return (error);
991 #ifdef COMPAT_FREEBSD32
992 	error = syscall32_helper_register(ksem32_syscalls);
993 	if (error)
994 		return (error);
995 #endif
996 	return (0);
997 }
998 
999 static void
1000 ksem_module_destroy(void)
1001 {
1002 
1003 #ifdef COMPAT_FREEBSD32
1004 	syscall32_helper_unregister(ksem32_syscalls);
1005 #endif
1006 	syscall_helper_unregister(ksem_syscalls);
1007 
1008 	ksem_info = NULL;
1009 	p31b_setcfg(CTL_P1003_1B_SEMAPHORES, 0);
1010 	hashdestroy(ksem_dictionary, M_KSEM, ksem_hash);
1011 	sx_destroy(&ksem_dict_lock);
1012 	mtx_destroy(&ksem_count_lock);
1013 	mtx_destroy(&sem_lock);
1014 	p31b_unsetcfg(CTL_P1003_1B_SEM_VALUE_MAX);
1015 	p31b_unsetcfg(CTL_P1003_1B_SEM_NSEMS_MAX);
1016 }
1017 
1018 static int
1019 sem_modload(struct module *module, int cmd, void *arg)
1020 {
1021         int error = 0;
1022 
1023         switch (cmd) {
1024         case MOD_LOAD:
1025 		error = ksem_module_init();
1026 		if (error)
1027 			ksem_module_destroy();
1028                 break;
1029 
1030         case MOD_UNLOAD:
1031 		mtx_lock(&ksem_count_lock);
1032 		if (nsems != 0) {
1033 			error = EOPNOTSUPP;
1034 			mtx_unlock(&ksem_count_lock);
1035 			break;
1036 		}
1037 		ksem_dead = 1;
1038 		mtx_unlock(&ksem_count_lock);
1039 		ksem_module_destroy();
1040                 break;
1041 
1042         case MOD_SHUTDOWN:
1043                 break;
1044         default:
1045                 error = EINVAL;
1046                 break;
1047         }
1048         return (error);
1049 }
1050 
1051 static moduledata_t sem_mod = {
1052         "sem",
1053         &sem_modload,
1054         NULL
1055 };
1056 
1057 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1058 MODULE_VERSION(sem, 1);
1059