fix potential deadlock in dlerror buffer handling at thread exit
[musl] / src / process / fork.c
1 #include <unistd.h>
2 #include <errno.h>
3 #include "libc.h"
4 #include "lock.h"
5 #include "pthread_impl.h"
6 #include "fork_impl.h"
7
8 static volatile int *const dummy_lockptr = 0;
9
10 weak_alias(dummy_lockptr, __at_quick_exit_lockptr);
11 weak_alias(dummy_lockptr, __atexit_lockptr);
12 weak_alias(dummy_lockptr, __gettext_lockptr);
13 weak_alias(dummy_lockptr, __locale_lockptr);
14 weak_alias(dummy_lockptr, __random_lockptr);
15 weak_alias(dummy_lockptr, __sem_open_lockptr);
16 weak_alias(dummy_lockptr, __stdio_ofl_lockptr);
17 weak_alias(dummy_lockptr, __syslog_lockptr);
18 weak_alias(dummy_lockptr, __timezone_lockptr);
19 weak_alias(dummy_lockptr, __bump_lockptr);
20
21 weak_alias(dummy_lockptr, __vmlock_lockptr);
22
23 static volatile int *const *const atfork_locks[] = {
24         &__at_quick_exit_lockptr,
25         &__atexit_lockptr,
26         &__gettext_lockptr,
27         &__locale_lockptr,
28         &__random_lockptr,
29         &__sem_open_lockptr,
30         &__stdio_ofl_lockptr,
31         &__syslog_lockptr,
32         &__timezone_lockptr,
33         &__bump_lockptr,
34 };
35
36 static void dummy(int x) { }
37 weak_alias(dummy, __fork_handler);
38 weak_alias(dummy, __malloc_atfork);
39 weak_alias(dummy, __ldso_atfork);
40
41 static void dummy_0(void) { }
42 weak_alias(dummy_0, __tl_lock);
43 weak_alias(dummy_0, __tl_unlock);
44
45 pid_t fork(void)
46 {
47         sigset_t set;
48         __fork_handler(-1);
49         __block_app_sigs(&set);
50         int need_locks = libc.need_locks > 0;
51         if (need_locks) {
52                 __ldso_atfork(-1);
53                 __inhibit_ptc();
54                 for (int i=0; i<sizeof atfork_locks/sizeof *atfork_locks; i++)
55                         if (*atfork_locks[i]) LOCK(*atfork_locks[i]);
56                 __malloc_atfork(-1);
57                 __tl_lock();
58         }
59         pthread_t self=__pthread_self(), next=self->next;
60         pid_t ret = _Fork();
61         int errno_save = errno;
62         if (need_locks) {
63                 if (!ret) {
64                         for (pthread_t td=next; td!=self; td=td->next)
65                                 td->tid = -1;
66                         if (__vmlock_lockptr) {
67                                 __vmlock_lockptr[0] = 0;
68                                 __vmlock_lockptr[1] = 0;
69                         }
70                 }
71                 __tl_unlock();
72                 __malloc_atfork(!ret);
73                 for (int i=0; i<sizeof atfork_locks/sizeof *atfork_locks; i++)
74                         if (*atfork_locks[i])
75                                 if (ret) UNLOCK(*atfork_locks[i]);
76                                 else **atfork_locks[i] = 0;
77                 __release_ptc();
78                 __ldso_atfork(!ret);
79         }
80         __restore_sigs(&set);
81         __fork_handler(!ret);
82         if (ret<0) errno = errno_save;
83         return ret;
84 }