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