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