uipc_sem.c revision 1.32 1 /* $NetBSD: uipc_sem.c,v 1.32 2011/04/14 00:32:23 rmind Exp $ */
2
3 /*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Mindaugas Rasiukevicius.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Copyright (c) 2002 Alfred Perlstein <alfred (at) FreeBSD.org>
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58 /*
59 * Implementation of POSIX semaphore.
60 */
61
62 #include <sys/cdefs.h>
63 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.32 2011/04/14 00:32:23 rmind Exp $");
64
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/kernel.h>
68 #include <sys/proc.h>
69 #include <sys/ksem.h>
70 #include <sys/syscall.h>
71 #include <sys/stat.h>
72 #include <sys/kmem.h>
73 #include <sys/fcntl.h>
74 #include <sys/file.h>
75 #include <sys/filedesc.h>
76 #include <sys/kauth.h>
77 #include <sys/module.h>
78 #include <sys/mount.h>
79 #include <sys/syscall.h>
80 #include <sys/syscallargs.h>
81 #include <sys/syscallvar.h>
82
83 MODULE(MODULE_CLASS_MISC, ksem, NULL);
84
85 #define SEM_MAX_NAMELEN 14
86 #define SEM_VALUE_MAX (~0U)
87
88 #define KS_UNLINKED 0x01
89
90 typedef struct ksem {
91 LIST_ENTRY(ksem) ks_entry; /* global list entry */
92 kmutex_t ks_lock; /* lock on this ksem */
93 kcondvar_t ks_cv; /* condition variable */
94 u_int ks_ref; /* number of references */
95 u_int ks_value; /* current value */
96 u_int ks_waiters; /* number of waiters */
97 char * ks_name; /* name, if named */
98 size_t ks_namelen; /* length of name */
99 int ks_flags; /* for KS_UNLINKED */
100 mode_t ks_mode; /* protection bits */
101 uid_t ks_uid; /* creator uid */
102 gid_t ks_gid; /* creator gid */
103 } ksem_t;
104
105 static kmutex_t ksem_lock __cacheline_aligned;
106 static LIST_HEAD(,ksem) ksem_head __cacheline_aligned;
107 static u_int nsems __cacheline_aligned;
108
109 static int ksem_sysinit(void);
110 static int ksem_sysfini(bool);
111 static int ksem_modcmd(modcmd_t, void *);
112 static int ksem_close_fop(file_t *);
113
114 static const struct fileops semops = {
115 .fo_read = fbadop_read,
116 .fo_write = fbadop_write,
117 .fo_ioctl = fbadop_ioctl,
118 .fo_fcntl = fnullop_fcntl,
119 .fo_poll = fnullop_poll,
120 .fo_stat = fbadop_stat,
121 .fo_close = ksem_close_fop,
122 .fo_kqfilter = fnullop_kqfilter,
123 .fo_restart = fnullop_restart,
124 };
125
126 static const struct syscall_package ksem_syscalls[] = {
127 { SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init },
128 { SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open },
129 { SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink },
130 { SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close },
131 { SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post },
132 { SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait },
133 { SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait },
134 { SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue },
135 { SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy },
136 { 0, 0, NULL },
137 };
138
139 static int
140 ksem_sysinit(void)
141 {
142 int error;
143
144 nsems = 0;
145 mutex_init(&ksem_lock, MUTEX_DEFAULT, IPL_NONE);
146 LIST_INIT(&ksem_head);
147
148 error = syscall_establish(NULL, ksem_syscalls);
149 if (error) {
150 (void)ksem_sysfini(false);
151 }
152 return error;
153 }
154
155 static int
156 ksem_sysfini(bool interface)
157 {
158 int error;
159
160 if (interface) {
161 error = syscall_disestablish(NULL, ksem_syscalls);
162 if (error != 0) {
163 return error;
164 }
165 if (nsems != 0) {
166 error = syscall_establish(NULL, ksem_syscalls);
167 KASSERT(error == 0);
168 return EBUSY;
169 }
170 }
171 mutex_destroy(&ksem_lock);
172 return 0;
173 }
174
175 static int
176 ksem_modcmd(modcmd_t cmd, void *arg)
177 {
178
179 switch (cmd) {
180 case MODULE_CMD_INIT:
181 return ksem_sysinit();
182
183 case MODULE_CMD_FINI:
184 return ksem_sysfini(true);
185
186 default:
187 return ENOTTY;
188 }
189 }
190
191 static ksem_t *
192 ksem_lookup(const char *name)
193 {
194 ksem_t *ks;
195
196 KASSERT(mutex_owned(&ksem_lock));
197
198 LIST_FOREACH(ks, &ksem_head, ks_entry) {
199 if (strcmp(ks->ks_name, name) == 0) {
200 mutex_enter(&ks->ks_lock);
201 return ks;
202 }
203 }
204 return NULL;
205 }
206
207 static int
208 ksem_perm(lwp_t *l, ksem_t *ks)
209 {
210 kauth_cred_t uc = l->l_cred;
211 mode_t mode = ks->ks_mode;
212
213 KASSERT(mutex_owned(&ks->ks_lock));
214 if ((kauth_cred_geteuid(uc) == ks->ks_uid && (mode & S_IWUSR) != 0) ||
215 (kauth_cred_getegid(uc) == ks->ks_gid && (mode & S_IWGRP) != 0) ||
216 (mode & S_IWOTH) != 0 ||
217 kauth_authorize_generic(uc, KAUTH_GENERIC_ISSUSER, NULL) == 0)
218 return 0;
219
220 return EACCES;
221 }
222
223 /*
224 * ksem_get: get the semaphore from the descriptor.
225 *
226 * => locks the semaphore, if found.
227 * => holds a reference on the file descriptor.
228 */
229 static int
230 ksem_get(int fd, ksem_t **ksret)
231 {
232 ksem_t *ks;
233 file_t *fp;
234
235 fp = fd_getfile(fd);
236 if (__predict_false(fp == NULL)) {
237 return EBADF;
238 }
239 if (__predict_false(fp->f_type != DTYPE_SEM)) {
240 fd_putfile(fd);
241 return EBADF;
242 }
243 ks = fp->f_data;
244 mutex_enter(&ks->ks_lock);
245
246 *ksret = ks;
247 return 0;
248 }
249
250 /*
251 * ksem_create: allocate and setup a new semaphore structure.
252 */
253 static int
254 ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val)
255 {
256 ksem_t *ks;
257 kauth_cred_t uc;
258 char *kname;
259 size_t len;
260
261 /* Pre-check for the limit. */
262 if (nsems >= ksem_max) {
263 return ENFILE;
264 }
265
266 if (val > SEM_VALUE_MAX) {
267 return EINVAL;
268 }
269
270 if (name != NULL) {
271 len = strlen(name);
272 if (len > SEM_MAX_NAMELEN) {
273 return ENAMETOOLONG;
274 }
275 /* Name must start with a '/' but not contain one. */
276 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
277 return EINVAL;
278 }
279 kname = kmem_alloc(++len, KM_SLEEP);
280 strlcpy(kname, name, len);
281 } else {
282 kname = NULL;
283 len = 0;
284 }
285
286 ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP);
287 mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE);
288 cv_init(&ks->ks_cv, "psem");
289 ks->ks_name = kname;
290 ks->ks_namelen = len;
291 ks->ks_mode = mode;
292 ks->ks_value = val;
293 ks->ks_ref = 1;
294
295 uc = l->l_cred;
296 ks->ks_uid = kauth_cred_geteuid(uc);
297 ks->ks_gid = kauth_cred_getegid(uc);
298
299 *ksret = ks;
300 return 0;
301 }
302
303 static void
304 ksem_free(ksem_t *ks)
305 {
306
307 if (ks->ks_name) {
308 KASSERT(ks->ks_namelen > 0);
309 kmem_free(ks->ks_name, ks->ks_namelen);
310 }
311 mutex_destroy(&ks->ks_lock);
312 cv_destroy(&ks->ks_cv);
313 kmem_free(ks, sizeof(ksem_t));
314 }
315
316 int
317 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap,
318 register_t *retval)
319 {
320 /* {
321 unsigned int value;
322 intptr_t *idp;
323 } */
324
325 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
326 }
327
328 int
329 do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout)
330 {
331 proc_t *p = l->l_proc;
332 ksem_t *ks;
333 file_t *fp;
334 intptr_t id;
335 int fd, error;
336
337 error = fd_allocfile(&fp, &fd);
338 if (error) {
339 return error;
340 }
341 fp->f_type = DTYPE_SEM;
342 fp->f_flag = FREAD | FWRITE;
343 fp->f_ops = &semops;
344
345 id = (intptr_t)fd;
346 error = (*docopyout)(&id, idp, sizeof(*idp));
347 if (error) {
348 fd_abort(p, fp, fd);
349 return error;
350 }
351
352 /* Note the mode does not matter for anonymous semaphores. */
353 error = ksem_create(l, NULL, &ks, 0, val);
354 if (error) {
355 fd_abort(p, fp, fd);
356 return error;
357 }
358 fp->f_data = ks;
359 fd_affix(p, fp, fd);
360 return error;
361 }
362
363 int
364 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap,
365 register_t *retval)
366 {
367 /* {
368 const char *name;
369 int oflag;
370 mode_t mode;
371 unsigned int value;
372 intptr_t *idp;
373 } */
374
375 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag),
376 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout);
377 }
378
379 int
380 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
381 unsigned int value, intptr_t *idp, copyout_t docopyout)
382 {
383 char name[SEM_MAX_NAMELEN + 1];
384 proc_t *p = l->l_proc;
385 ksem_t *ksnew = NULL, *ks;
386 file_t *fp;
387 intptr_t id;
388 int fd, error;
389
390 error = copyinstr(semname, name, sizeof(name), NULL);
391 if (error) {
392 return error;
393 }
394 error = fd_allocfile(&fp, &fd);
395 if (error) {
396 return error;
397 }
398 fp->f_type = DTYPE_SEM;
399 fp->f_flag = FREAD | FWRITE;
400 fp->f_ops = &semops;
401
402 /*
403 * The ID (file descriptor number) can be stored early.
404 * Note that zero is a special value for libpthread.
405 */
406 id = (intptr_t)fd;
407 error = (*docopyout)(&id, idp, sizeof(*idp));
408 if (error) {
409 goto err;
410 }
411
412 if (oflag & O_CREAT) {
413 /* Create a new semaphore. */
414 error = ksem_create(l, name, &ksnew, mode, value);
415 if (error) {
416 goto err;
417 }
418 KASSERT(ksnew != NULL);
419 }
420
421 /* Lookup for a semaphore with such name. */
422 mutex_enter(&ksem_lock);
423 ks = ksem_lookup(name);
424 if (ks) {
425 KASSERT(mutex_owned(&ks->ks_lock));
426 mutex_exit(&ksem_lock);
427
428 /* Check for exclusive create. */
429 if (oflag & O_EXCL) {
430 mutex_exit(&ks->ks_lock);
431 error = EEXIST;
432 goto err;
433 }
434 /*
435 * Verify permissions. If we can access it,
436 * add the reference of this thread.
437 */
438 error = ksem_perm(l, ks);
439 if (error == 0) {
440 ks->ks_ref++;
441 }
442 mutex_exit(&ks->ks_lock);
443 if (error) {
444 goto err;
445 }
446 } else {
447 /* Fail if not found and not creating. */
448 if ((oflag & O_CREAT) == 0) {
449 mutex_exit(&ksem_lock);
450 KASSERT(ksnew == NULL);
451 error = ENOENT;
452 goto err;
453 }
454
455 /* Check for the limit locked. */
456 if (nsems >= ksem_max) {
457 mutex_exit(&ksem_lock);
458 error = ENFILE;
459 goto err;
460 }
461
462 /*
463 * Finally, insert semaphore into the list.
464 * Note: it already has the initial reference.
465 */
466 ks = ksnew;
467 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
468 nsems++;
469 mutex_exit(&ksem_lock);
470
471 ksnew = NULL;
472 }
473 KASSERT(ks != NULL);
474 fp->f_data = ks;
475 fd_affix(p, fp, fd);
476 err:
477 if (error) {
478 fd_abort(p, fp, fd);
479 }
480 if (ksnew) {
481 ksem_free(ksnew);
482 }
483 return error;
484 }
485
486 int
487 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap,
488 register_t *retval)
489 {
490 /* {
491 intptr_t id;
492 } */
493 struct sys_close_args cuap;
494 SCARG(&cuap, fd) = SCARG(uap, id);
495 return sys_close(l, (const void *)&cuap, retval);
496 }
497
498 static int
499 ksem_close_fop(file_t *fp)
500 {
501 ksem_t *ks = fp->f_data;
502 bool destroy = false;
503
504 mutex_enter(&ks->ks_lock);
505 KASSERT(ks->ks_ref > 0);
506 if (--ks->ks_ref == 0) {
507 /*
508 * Destroy if the last reference and semaphore is unnamed,
509 * or unlinked (for named semaphore).
510 */
511 destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL);
512 }
513 mutex_exit(&ks->ks_lock);
514
515 if (destroy) {
516 ksem_free(ks);
517 }
518 return 0;
519 }
520
521 int
522 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap,
523 register_t *retval)
524 {
525 /* {
526 const char *name;
527 } */
528 char name[SEM_MAX_NAMELEN + 1];
529 ksem_t *ks;
530 u_int refcnt;
531 int error;
532
533 error = copyinstr(SCARG(uap, name), name, sizeof(name), NULL);
534 if (error)
535 return error;
536
537 mutex_enter(&ksem_lock);
538 ks = ksem_lookup(name);
539 if (ks == NULL) {
540 mutex_exit(&ksem_lock);
541 return ENOENT;
542 }
543 KASSERT(mutex_owned(&ks->ks_lock));
544
545 /* Verify permissions. */
546 error = ksem_perm(l, ks);
547 if (error) {
548 mutex_exit(&ks->ks_lock);
549 mutex_exit(&ksem_lock);
550 return error;
551 }
552
553 /* Remove from the global list. */
554 LIST_REMOVE(ks, ks_entry);
555 nsems--;
556 mutex_exit(&ksem_lock);
557
558 refcnt = ks->ks_ref;
559 if (refcnt) {
560 /* Mark as unlinked, if there are references. */
561 ks->ks_flags |= KS_UNLINKED;
562 }
563 mutex_exit(&ks->ks_lock);
564
565 if (refcnt == 0) {
566 ksem_free(ks);
567 }
568 return 0;
569 }
570
571 int
572 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap,
573 register_t *retval)
574 {
575 /* {
576 intptr_t id;
577 } */
578 int fd = (int)SCARG(uap, id), error;
579 ksem_t *ks;
580
581 error = ksem_get(fd, &ks);
582 if (error) {
583 return error;
584 }
585 KASSERT(mutex_owned(&ks->ks_lock));
586 if (ks->ks_value == SEM_VALUE_MAX) {
587 error = EOVERFLOW;
588 goto out;
589 }
590 ks->ks_value++;
591 if (ks->ks_waiters) {
592 cv_broadcast(&ks->ks_cv);
593 }
594 out:
595 mutex_exit(&ks->ks_lock);
596 fd_putfile(fd);
597 return error;
598 }
599
600 static int
601 ksem_wait(lwp_t *l, intptr_t id, bool try)
602 {
603 int fd = (int)id, error;
604 ksem_t *ks;
605
606 error = ksem_get(fd, &ks);
607 if (error) {
608 return error;
609 }
610 KASSERT(mutex_owned(&ks->ks_lock));
611 while (ks->ks_value == 0) {
612 ks->ks_waiters++;
613 error = try ? EAGAIN : cv_wait_sig(&ks->ks_cv, &ks->ks_lock);
614 ks->ks_waiters--;
615 if (error)
616 goto out;
617 }
618 ks->ks_value--;
619 out:
620 mutex_exit(&ks->ks_lock);
621 fd_putfile(fd);
622 return error;
623 }
624
625 int
626 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap,
627 register_t *retval)
628 {
629 /* {
630 intptr_t id;
631 } */
632
633 return ksem_wait(l, SCARG(uap, id), false);
634 }
635
636 int
637 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap,
638 register_t *retval)
639 {
640 /* {
641 intptr_t id;
642 } */
643
644 return ksem_wait(l, SCARG(uap, id), true);
645 }
646
647 int
648 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap,
649 register_t *retval)
650 {
651 /* {
652 intptr_t id;
653 unsigned int *value;
654 } */
655 int fd = (int)SCARG(uap, id), error;
656 ksem_t *ks;
657 unsigned int val;
658
659 error = ksem_get(fd, &ks);
660 if (error) {
661 return error;
662 }
663 KASSERT(mutex_owned(&ks->ks_lock));
664 val = ks->ks_value;
665 mutex_exit(&ks->ks_lock);
666 fd_putfile(fd);
667
668 return copyout(&val, SCARG(uap, value), sizeof(val));
669 }
670
671 int
672 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap,
673 register_t *retval)
674 {
675 /* {
676 intptr_t id;
677 } */
678 int fd = (int)SCARG(uap, id), error;
679 ksem_t *ks;
680
681 error = ksem_get(fd, &ks);
682 if (error) {
683 return error;
684 }
685 KASSERT(mutex_owned(&ks->ks_lock));
686
687 /* Operation is only for unnamed semaphores. */
688 if (ks->ks_name != NULL) {
689 error = EINVAL;
690 goto out;
691 }
692 /* Cannot destroy if there are waiters. */
693 if (ks->ks_waiters) {
694 error = EBUSY;
695 goto out;
696 }
697 out:
698 mutex_exit(&ks->ks_lock);
699 if (error) {
700 fd_putfile(fd);
701 return error;
702 }
703 return fd_close(fd);
704 }
705