1 /*
2 * Copyright (C) 2005 David Xu <davidxu@freebsd.org>.
3 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice(s), this list of conditions and the following disclaimer as
11 * the first lines of this file unmodified other than the possible
12 * addition of one or more copyright notices.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice(s), this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "namespace.h"
32 #include <machine/tls.h>
33 #include <sys/mman.h>
34 #include <sys/queue.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <pthread.h>
41 #include <semaphore.h>
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include <unistd.h>
47 #ifdef _PTHREADS_DEBUGGING
48 #include <stdio.h>
49 #endif
50 #include "un-namespace.h"
51
52 #include "thr_private.h"
53
54 #define cpu_ccfence() __asm __volatile("" : : : "memory")
55
56 #define container_of(ptr, type, member) \
57 ({ \
58 __typeof(((type *)0)->member) *_p = (ptr); \
59 (type *)((char *)_p - offsetof(type, member)); \
60 })
61
62 /*
63 * Semaphore definitions.
64 */
65 struct sem {
66 volatile umtx_t count;
67 u_int32_t magic;
68 int semid;
69 int unused; /* pad */
70 } __cachealign;
71
72 #define SEM_MAGIC ((u_int32_t) 0x09fa4012)
73
74 static char const *sem_prefix = "/var/run/sem";
75
76
77 /*
78 * POSIX requires that two successive calls to sem_open return
79 * the same address if no call to unlink nor close have been
80 * done in the middle. For that, we keep a list of open semaphore
81 * and search for an existing one before remapping a semaphore.
82 * We have to keep the fd open to check for races.
83 *
84 * Example :
85 * sem_open("/test", O_CREAT | O_EXCL...) -> fork() ->
86 * parent :
87 * sem_unlink("/test") -> sem_open("/test", O_CREAT | O_EXCl ...)
88 * child :
89 * sem_open("/test", 0).
90 * We need to check that the cached mapping is the one of the most up
91 * to date file linked at this name, or child process will reopen the
92 * *old* version of the semaphore, which is wrong.
93 *
94 * fstat and nlink check is used to test for this race.
95 */
96
97 struct sem_info {
98 int open_count;
99 ino_t inode;
100 dev_t dev;
101 int fd;
102 sem_t sem;
103 LIST_ENTRY(sem_info) next;
104 };
105
106 static pthread_mutex_t sem_lock;
107 static LIST_HEAD(,sem_info) sem_list = LIST_HEAD_INITIALIZER(sem_list);
108
109 #ifdef _PTHREADS_DEBUGGING
110
111 static
112 void
sem_log(const char * ctl,...)113 sem_log(const char *ctl, ...)
114 {
115 char buf[256];
116 va_list va;
117 size_t len;
118
119 va_start(va, ctl);
120 len = vsnprintf(buf, sizeof(buf), ctl, va);
121 va_end(va);
122 _thr_log(buf, len);
123 }
124
125 #else
126
127 static __inline
128 void
sem_log(const char * ctl __unused,...)129 sem_log(const char *ctl __unused, ...)
130 {
131 }
132
133 #endif
134
135 #define SEMID_LWP 0
136 #define SEMID_FORK 1
137 #define SEMID_NAMED 2
138
139 static void
sem_prefork(void)140 sem_prefork(void)
141 {
142 _pthread_mutex_lock(&sem_lock);
143 }
144
145 static void
sem_postfork(void)146 sem_postfork(void)
147 {
148 _pthread_mutex_unlock(&sem_lock);
149 }
150
151 static void
sem_child_postfork(void)152 sem_child_postfork(void)
153 {
154 _pthread_mutex_unlock(&sem_lock);
155 }
156
157 void
_thr_sem_init(void)158 _thr_sem_init(void)
159 {
160 pthread_mutexattr_t ma;
161
162 _pthread_mutexattr_init(&ma);
163 _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE);
164 _pthread_mutex_init(&sem_lock, &ma);
165 _pthread_mutexattr_destroy(&ma);
166 _thr_atfork_kern(sem_prefork, sem_postfork, sem_child_postfork);
167 }
168
169 static inline int
sem_check_validity(sem_t * sem)170 sem_check_validity(sem_t *sem)
171 {
172
173 if ((sem != NULL) && (*sem != NULL) && ((*sem)->magic == SEM_MAGIC)) {
174 return (0);
175 } else {
176 errno = EINVAL;
177 return (-1);
178 }
179 }
180
181 static sem_t
sem_alloc(unsigned int value,int pshared)182 sem_alloc(unsigned int value, int pshared)
183 {
184 sem_t sem;
185 int semid;
186
187 if (value > SEM_VALUE_MAX) {
188 errno = EINVAL;
189 return (NULL);
190 }
191 if (pshared) {
192 static __thread sem_t sem_base;
193 static __thread int sem_count;
194
195 if (sem_base == NULL) {
196 sem_base = mmap(NULL, getpagesize(),
197 PROT_READ | PROT_WRITE,
198 MAP_ANON | MAP_SHARED,
199 -1, 0);
200 sem_count = getpagesize() / sizeof(*sem);
201 }
202 sem = sem_base++;
203 if (--sem_count == 0)
204 sem_base = NULL;
205 semid = SEMID_FORK;
206 } else {
207 sem = __malloc(sizeof(struct sem));
208 semid = SEMID_LWP;
209 }
210 if (sem == NULL) {
211 errno = ENOSPC;
212 return (NULL);
213 }
214 sem->magic = SEM_MAGIC;
215 sem->count = (u_int32_t)value;
216 sem->semid = semid;
217
218 sem_log("sem_alloc %p (%d)\n", sem, value);
219
220 return (sem);
221 }
222
223 int
_sem_init(sem_t * sem,int pshared,unsigned int value)224 _sem_init(sem_t *sem, int pshared, unsigned int value)
225 {
226 if (sem == NULL) {
227 errno = EINVAL;
228 return (-1);
229 }
230
231 *sem = sem_alloc(value, pshared);
232 if (*sem == NULL)
233 return (-1);
234 return (0);
235 }
236
237 int
_sem_destroy(sem_t * sem)238 _sem_destroy(sem_t *sem)
239 {
240 if (sem_check_validity(sem) != 0) {
241 errno = EINVAL;
242 return (-1);
243 }
244
245 (*sem)->magic = 0;
246
247 switch ((*sem)->semid) {
248 case SEMID_LWP:
249 __free(*sem);
250 break;
251 case SEMID_FORK:
252 /* memory is left intact */
253 break;
254 default:
255 errno = EINVAL;
256 return (-1);
257 }
258 return (0);
259 }
260
261 int
_sem_getvalue(sem_t * __restrict sem,int * __restrict sval)262 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
263 {
264 if (sem_check_validity(sem) != 0) {
265 errno = EINVAL;
266 return (-1);
267 }
268 *sval = (*sem)->count;
269
270 return (0);
271 }
272
273 int
_sem_trywait(sem_t * sem)274 _sem_trywait(sem_t *sem)
275 {
276 int val;
277
278 if (sem_check_validity(sem) != 0) {
279 errno = EINVAL;
280 return (-1);
281 }
282
283 sem_log("sem_trywait %p %d\n", *sem, (*sem)->count);
284 while ((val = (*sem)->count) > 0) {
285 cpu_ccfence();
286 if (atomic_cmpset_int(&(*sem)->count, val, val - 1)) {
287 sem_log("sem_trywait %p %d (success)\n", *sem, val - 1);
288 return (0);
289 }
290 }
291 errno = EAGAIN;
292 sem_log("sem_trywait %p %d (failure)\n", *sem, val);
293 return (-1);
294 }
295
296 int
_sem_wait(sem_t * sem)297 _sem_wait(sem_t *sem)
298 {
299 pthread_t curthread;
300 int val, oldcancel, retval;
301
302 if (sem_check_validity(sem) != 0) {
303 errno = EINVAL;
304 return (-1);
305 }
306
307 curthread = tls_get_curthread();
308 _pthread_testcancel();
309
310 sem_log("sem_wait %p %d (begin)\n", *sem, (*sem)->count);
311
312 do {
313 cpu_ccfence();
314 while ((val = (*sem)->count) > 0) {
315 cpu_ccfence();
316 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) {
317 sem_log("sem_wait %p %d (success)\n",
318 *sem, val - 1);
319 return (0);
320 }
321 }
322 oldcancel = _thr_cancel_enter(curthread);
323 sem_log("sem_wait %p %d (wait)\n", *sem, val);
324 retval = _thr_umtx_wait_intr(&(*sem)->count, 0);
325 sem_log("sem_wait %p %d (wait return %d)\n",
326 *sem, (*sem)->count, retval);
327 _thr_cancel_leave(curthread, oldcancel);
328 /* ignore retval */
329 } while (retval != EINTR);
330
331 sem_log("sem_wait %p %d (error %d)\n", *sem, retval);
332 errno = retval;
333
334 return (-1);
335 }
336
337 int
_sem_timedwait(sem_t * __restrict sem,const struct timespec * __restrict abstime)338 _sem_timedwait(sem_t * __restrict sem, const struct timespec * __restrict abstime)
339 {
340 struct timespec ts, ts2;
341 pthread_t curthread;
342 int val, oldcancel, retval;
343
344 if (sem_check_validity(sem) != 0)
345 return (-1);
346
347 curthread = tls_get_curthread();
348 _pthread_testcancel();
349 sem_log("sem_timedwait %p %d (begin)\n", *sem, (*sem)->count);
350
351 /*
352 * The timeout argument is only supposed to
353 * be checked if the thread would have blocked.
354 */
355 do {
356 while ((val = (*sem)->count) > 0) {
357 cpu_ccfence();
358 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) {
359 sem_log("sem_wait %p %d (success)\n",
360 *sem, val - 1);
361 return (0);
362 }
363 }
364 if (abstime == NULL ||
365 abstime->tv_nsec >= 1000000000 ||
366 abstime->tv_nsec < 0) {
367 sem_log("sem_wait %p %d (bad abstime)\n", *sem, val);
368 errno = EINVAL;
369 return (-1);
370 }
371 clock_gettime(CLOCK_REALTIME, &ts);
372 timespecsub(abstime, &ts, &ts2);
373 oldcancel = _thr_cancel_enter(curthread);
374 sem_log("sem_wait %p %d (wait)\n", *sem, val);
375 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
376 CLOCK_REALTIME);
377 sem_log("sem_wait %p %d (wait return %d)\n",
378 *sem, (*sem)->count, retval);
379 _thr_cancel_leave(curthread, oldcancel);
380 } while (retval != ETIMEDOUT && retval != EINTR);
381
382 sem_log("sem_wait %p %d (error %d)\n", *sem, retval);
383 errno = retval;
384
385 return (-1);
386 }
387
388 int
_sem_post(sem_t * sem)389 _sem_post(sem_t *sem)
390 {
391 int val;
392
393 if (sem_check_validity(sem) != 0)
394 return (-1);
395
396 /*
397 * sem_post() is required to be safe to call from within
398 * signal handlers, these code should work as that.
399 */
400 val = atomic_fetchadd_int(&(*sem)->count, 1) + 1;
401 sem_log("sem_post %p %d\n", *sem, val);
402 _thr_umtx_wake(&(*sem)->count, 0);
403
404 return (0);
405 }
406
407 static int
get_path(const char * name,char * path,size_t len,char const ** prefix)408 get_path(const char *name, char *path, size_t len, char const **prefix)
409 {
410 size_t path_len;
411
412 *prefix = NULL;
413
414 if (name[0] == '/') {
415 *prefix = getenv("LIBTHREAD_SEM_PREFIX");
416
417 if (*prefix == NULL)
418 *prefix = sem_prefix;
419
420 path_len = strlcpy(path, *prefix, len);
421
422 if (path_len > len) {
423 return (ENAMETOOLONG);
424 }
425 }
426
427 path_len = strlcat(path, name, len);
428
429 if (path_len > len)
430 return (ENAMETOOLONG);
431
432 return (0);
433 }
434
435
436 static sem_t *
sem_get_mapping(ino_t inode,dev_t dev)437 sem_get_mapping(ino_t inode, dev_t dev)
438 {
439 struct sem_info *ni;
440 struct stat sbuf;
441
442 LIST_FOREACH(ni, &sem_list, next) {
443 if (ni->inode == inode && ni->dev == dev) {
444 /* Check for races */
445 if(_fstat(ni->fd, &sbuf) == 0) {
446 if (sbuf.st_nlink > 0) {
447 ni->open_count++;
448 return (&ni->sem);
449 } else {
450 ni->inode = 0;
451 LIST_REMOVE(ni, next);
452 }
453 }
454 return (SEM_FAILED);
455
456 }
457 }
458
459 return (SEM_FAILED);
460 }
461
462
463 static sem_t *
sem_add_mapping(ino_t inode,dev_t dev,sem_t sem,int fd)464 sem_add_mapping(ino_t inode, dev_t dev, sem_t sem, int fd)
465 {
466 struct sem_info *ni;
467
468 ni = __malloc(sizeof(struct sem_info));
469 if (ni == NULL) {
470 errno = ENOSPC;
471 return (SEM_FAILED);
472 }
473
474 bzero(ni, sizeof(*ni));
475 ni->open_count = 1;
476 ni->sem = sem;
477 ni->fd = fd;
478 ni->inode = inode;
479 ni->dev = dev;
480
481 LIST_INSERT_HEAD(&sem_list, ni, next);
482
483 return (&ni->sem);
484 }
485
486 static int
sem_close_mapping(sem_t * sem)487 sem_close_mapping(sem_t *sem)
488 {
489 struct sem_info *ni;
490
491 if ((*sem)->semid != SEMID_NAMED)
492 return (EINVAL);
493
494 ni = container_of(sem, struct sem_info, sem);
495
496 if ( --ni->open_count > 0) {
497 return (0);
498 } else {
499 if (ni->inode != 0) {
500 LIST_REMOVE(ni, next);
501 }
502 munmap(ni->sem, getpagesize());
503 __sys_close(ni->fd);
504 __free(ni);
505 return (0);
506 }
507 }
508
509 sem_t *
_sem_open(const char * name,int oflag,...)510 _sem_open(const char *name, int oflag, ...)
511 {
512 char path[PATH_MAX];
513 char tmppath[PATH_MAX];
514 char const *prefix = NULL;
515 size_t path_len;
516 int error, fd, create;
517 sem_t *sem;
518 sem_t semtmp;
519 va_list ap;
520 mode_t mode;
521 struct stat sbuf;
522 unsigned int value = 0;
523
524 create = 0;
525 error = 0;
526 fd = -1;
527 sem = SEM_FAILED;
528
529 /*
530 * Bail out if invalid flags specified.
531 */
532 if (oflag & ~(O_CREAT|O_EXCL)) {
533 errno = EINVAL;
534 return (SEM_FAILED);
535 }
536
537 oflag |= O_RDWR;
538 oflag |= O_CLOEXEC;
539
540 if (name == NULL) {
541 errno = EINVAL;
542 return (SEM_FAILED);
543 }
544
545 _pthread_mutex_lock(&sem_lock);
546
547 error = get_path(name, path, PATH_MAX, &prefix);
548 if (error) {
549 errno = error;
550 goto error;
551 }
552
553 retry:
554 fd = __sys_open(path, O_RDWR | O_CLOEXEC);
555
556 if (fd > 0) {
557
558 if ((oflag & O_EXCL) == O_EXCL) {
559 __sys_close(fd);
560 errno = EEXIST;
561 goto error;
562 }
563
564 if (_fstat(fd, &sbuf) != 0) {
565 /* Bad things happened, like another thread closing our descriptor */
566 __sys_close(fd);
567 errno = EINVAL;
568 goto error;
569 }
570
571 sem = sem_get_mapping(sbuf.st_ino, sbuf.st_dev);
572
573 if (sem != SEM_FAILED) {
574 __sys_close(fd);
575 goto done;
576 }
577
578 if ((sbuf.st_mode & S_IFREG) == 0) {
579 /* We only want regular files here */
580 __sys_close(fd);
581 errno = EINVAL;
582 goto error;
583 }
584 } else if ((oflag & O_CREAT) && errno == ENOENT) {
585
586 va_start(ap, oflag);
587
588 mode = (mode_t) va_arg(ap, int);
589 value = (unsigned int) va_arg(ap, int);
590
591 va_end(ap);
592
593 if (value > SEM_VALUE_MAX) {
594 errno = EINVAL;
595 goto error;
596 }
597
598 strlcpy(tmppath, prefix, sizeof(tmppath));
599 path_len = strlcat(tmppath, "/sem.XXXXXX", sizeof(tmppath));
600
601 if (path_len > sizeof(tmppath)) {
602 errno = ENAMETOOLONG;
603 goto error;
604 }
605
606
607 fd = mkstemp(tmppath);
608
609 if ( fd == -1 ) {
610 errno = EINVAL;
611 goto error;
612 }
613
614 error = fchmod(fd, mode);
615 if ( error == -1 ) {
616 __sys_close(fd);
617 errno = EINVAL;
618 goto error;
619 }
620
621 error = __sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
622 if ( error == -1 ) {
623 __sys_close(fd);
624 errno = EINVAL;
625 goto error;
626 }
627
628 create = 1;
629 }
630
631 if (fd == -1) {
632 switch (errno) {
633 case ENOTDIR:
634 case EISDIR:
635 case EMLINK:
636 case ELOOP:
637 errno = EINVAL;
638 break;
639 case EDQUOT:
640 case EIO:
641 errno = ENOSPC;
642 break;
643 case EROFS:
644 errno = EACCES;
645 }
646 goto error;
647 }
648
649 semtmp = (sem_t) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
650 MAP_NOSYNC | MAP_SHARED, fd, 0);
651
652 if (semtmp == MAP_FAILED) {
653 if (errno != EACCES && errno != EMFILE)
654 errno = ENOMEM;
655
656 if (create)
657 _unlink(tmppath);
658
659 __sys_close(fd);
660 goto error;
661 }
662
663 if (create) {
664 ftruncate(fd, sizeof(struct sem));
665 semtmp->magic = SEM_MAGIC;
666 semtmp->count = (u_int32_t)value;
667 semtmp->semid = SEMID_NAMED;
668
669 if (link(tmppath, path) != 0) {
670 munmap(semtmp, getpagesize());
671 __sys_close(fd);
672 _unlink(tmppath);
673
674 if (errno == EEXIST && (oflag & O_EXCL) == 0) {
675 goto retry;
676 }
677
678 goto error;
679 }
680 _unlink(tmppath);
681
682 if (_fstat(fd, &sbuf) != 0) {
683 /* Bad things happened, like another thread closing our descriptor */
684 munmap(semtmp, getpagesize());
685 __sys_close(fd);
686 errno = EINVAL;
687 goto error;
688 }
689
690 }
691 sem = sem_add_mapping(sbuf.st_ino, sbuf.st_dev, semtmp, fd);
692
693 done:
694 _pthread_mutex_unlock(&sem_lock);
695 return (sem);
696
697 error:
698 _pthread_mutex_unlock(&sem_lock);
699 return (SEM_FAILED);
700
701 }
702
703 int
_sem_close(sem_t * sem)704 _sem_close(sem_t *sem)
705 {
706 _pthread_mutex_lock(&sem_lock);
707
708 if (sem_check_validity(sem)) {
709 _pthread_mutex_unlock(&sem_lock);
710 errno = EINVAL;
711 return (-1);
712 }
713
714 if (sem_close_mapping(sem)) {
715 _pthread_mutex_unlock(&sem_lock);
716 errno = EINVAL;
717 return (-1);
718 }
719 _pthread_mutex_unlock(&sem_lock);
720
721 return (0);
722 }
723
724 int
_sem_unlink(const char * name)725 _sem_unlink(const char *name)
726 {
727 char path[PATH_MAX];
728 const char *prefix;
729 int error;
730
731 error = get_path(name, path, PATH_MAX, &prefix);
732 if (error) {
733 errno = error;
734 return (-1);
735 }
736
737 error = _unlink(path);
738
739 if(error) {
740 if (errno != ENAMETOOLONG && errno != ENOENT)
741 errno = EACCES;
742
743 return (-1);
744 }
745
746 return (0);
747 }
748
749 __strong_reference(_sem_destroy, sem_destroy);
750 __strong_reference(_sem_getvalue, sem_getvalue);
751 __strong_reference(_sem_init, sem_init);
752 __strong_reference(_sem_trywait, sem_trywait);
753 __strong_reference(_sem_wait, sem_wait);
754 __strong_reference(_sem_timedwait, sem_timedwait);
755 __strong_reference(_sem_post, sem_post);
756 __strong_reference(_sem_open, sem_open);
757 __strong_reference(_sem_close, sem_close);
758 __strong_reference(_sem_unlink, sem_unlink);
759