uipc_sem.c revision 1.30 1 /* $NetBSD: uipc_sem.c,v 1.30 2011/04/11 22:31:43 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.30 2011/04/11 22:31:43 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 return ENOENT;
452 }
453
454 /* Check for the limit locked. */
455 if (nsems >= ksem_max) {
456 mutex_exit(&ksem_lock);
457 error = ENFILE;
458 goto err;
459 }
460
461 /*
462 * Finally, insert semaphore into the hash.
463 * Note: it already has the initial reference.
464 */
465 ks = ksnew;
466 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
467 nsems++;
468 mutex_exit(&ksem_lock);
469
470 ksnew = NULL;
471 }
472 KASSERT(ks != NULL);
473 fp->f_data = ks;
474 fd_affix(p, fp, fd);
475 err:
476 if (error) {
477 fd_abort(p, fp, fd);
478 }
479 if (ksnew) {
480 ksem_free(ksnew);
481 }
482 return error;
483 }
484
485 int
486 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap,
487 register_t *retval)
488 {
489 /* {
490 intptr_t id;
491 } */
492 struct sys_close_args cuap;
493 SCARG(&cuap, fd) = SCARG(uap, id);
494 return sys_close(l, (const void *)&cuap, retval);
495 }
496
497 static int
498 ksem_close_fop(file_t *fp)
499 {
500 ksem_t *ks = fp->f_data;
501 bool destroy = false;
502
503 mutex_enter(&ks->ks_lock);
504 KASSERT(ks->ks_ref > 0);
505 if (--ks->ks_ref == 0) {
506 /*
507 * Destroy if the last reference and semaphore is unnamed,
508 * or unlinked (for named semaphore).
509 */
510 destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL);
511 }
512 mutex_exit(&ks->ks_lock);
513
514 if (destroy) {
515 ksem_free(ks);
516 }
517 return 0;
518 }
519
520 int
521 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap,
522 register_t *retval)
523 {
524 /* {
525 const char *name;
526 } */
527 char name[SEM_MAX_NAMELEN + 1];
528 ksem_t *ks;
529 u_int refcnt;
530 int error;
531
532 error = copyinstr(SCARG(uap, name), name, sizeof(name), NULL);
533 if (error)
534 return error;
535
536 mutex_enter(&ksem_lock);
537 ks = ksem_lookup(name);
538 if (ks == NULL) {
539 mutex_exit(&ksem_lock);
540 return ENOENT;
541 }
542 KASSERT(mutex_owned(&ks->ks_lock));
543
544 /* Verify permissions. */
545 error = ksem_perm(l, ks);
546 if (error) {
547 mutex_exit(&ks->ks_lock);
548 mutex_exit(&ksem_lock);
549 return error;
550 }
551
552 /* Remove and destroy if no referenes. */
553 LIST_REMOVE(ks, ks_entry);
554 nsems--;
555
556 refcnt = ks->ks_ref;
557 if (refcnt) {
558 /* Mark as unlinked, if there are references. */
559 ks->ks_flags |= KS_UNLINKED;
560 }
561 mutex_exit(&ks->ks_lock);
562 mutex_exit(&ksem_lock);
563
564 if (refcnt == 0) {
565 ksem_free(ks);
566 }
567 return 0;
568 }
569
570 int
571 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap,
572 register_t *retval)
573 {
574 /* {
575 intptr_t id;
576 } */
577 int fd = (int)SCARG(uap, id), error;
578 ksem_t *ks;
579
580 error = ksem_get(fd, &ks);
581 if (error) {
582 return error;
583 }
584 KASSERT(mutex_owned(&ks->ks_lock));
585 if (ks->ks_value == SEM_VALUE_MAX) {
586 error = EOVERFLOW;
587 goto out;
588 }
589 ks->ks_value++;
590 if (ks->ks_waiters) {
591 cv_broadcast(&ks->ks_cv);
592 }
593 out:
594 mutex_exit(&ks->ks_lock);
595 fd_putfile(fd);
596 return error;
597 }
598
599 static int
600 ksem_wait(lwp_t *l, intptr_t id, bool try)
601 {
602 int fd = (int)id, error;
603 ksem_t *ks;
604
605 error = ksem_get(fd, &ks);
606 if (error) {
607 return error;
608 }
609 KASSERT(mutex_owned(&ks->ks_lock));
610 while (ks->ks_value == 0) {
611 ks->ks_waiters++;
612 error = try ? EAGAIN : cv_wait_sig(&ks->ks_cv, &ks->ks_lock);
613 ks->ks_waiters--;
614 if (error)
615 goto out;
616 }
617 ks->ks_value--;
618 out:
619 mutex_exit(&ks->ks_lock);
620 fd_putfile(fd);
621 return error;
622 }
623
624 int
625 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap,
626 register_t *retval)
627 {
628 /* {
629 intptr_t id;
630 } */
631
632 return ksem_wait(l, SCARG(uap, id), false);
633 }
634
635 int
636 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap,
637 register_t *retval)
638 {
639 /* {
640 intptr_t id;
641 } */
642
643 return ksem_wait(l, SCARG(uap, id), true);
644 }
645
646 int
647 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap,
648 register_t *retval)
649 {
650 /* {
651 intptr_t id;
652 unsigned int *value;
653 } */
654 int fd = (int)SCARG(uap, id), error;
655 ksem_t *ks;
656 unsigned int val;
657
658 error = ksem_get(fd, &ks);
659 if (error) {
660 return error;
661 }
662 KASSERT(mutex_owned(&ks->ks_lock));
663 val = ks->ks_value;
664 mutex_exit(&ks->ks_lock);
665 fd_putfile(fd);
666
667 return copyout(&val, SCARG(uap, value), sizeof(val));
668 }
669
670 int
671 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap,
672 register_t *retval)
673 {
674 /* {
675 intptr_t id;
676 } */
677 int fd = (int)SCARG(uap, id), error;
678 struct sys_close_args cuap;
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 fd_putfile(fd);
700 if (error) {
701 return error;
702 }
703 SCARG(&cuap, fd) = fd;
704 return sys_close(l, (const void *)&cuap, retval);
705 }
706