171b3fa15SDavid Xu /*
271b3fa15SDavid Xu * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
371b3fa15SDavid Xu * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>
471b3fa15SDavid Xu * All rights reserved.
571b3fa15SDavid Xu *
671b3fa15SDavid Xu * Redistribution and use in source and binary forms, with or without
771b3fa15SDavid Xu * modification, are permitted provided that the following conditions
871b3fa15SDavid Xu * are met:
971b3fa15SDavid Xu * 1. Redistributions of source code must retain the above copyright
1071b3fa15SDavid Xu * notice, this list of conditions and the following disclaimer.
1171b3fa15SDavid Xu * 2. Neither the name of the author nor the names of any co-contributors
1271b3fa15SDavid Xu * may be used to endorse or promote products derived from this software
1371b3fa15SDavid Xu * without specific prior written permission.
1471b3fa15SDavid Xu *
1571b3fa15SDavid Xu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1671b3fa15SDavid Xu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1771b3fa15SDavid Xu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1871b3fa15SDavid Xu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1971b3fa15SDavid Xu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2071b3fa15SDavid Xu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2171b3fa15SDavid Xu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2271b3fa15SDavid Xu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2371b3fa15SDavid Xu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2471b3fa15SDavid Xu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2571b3fa15SDavid Xu * SUCH DAMAGE.
2671b3fa15SDavid Xu *
27fcf53d9bSJohn Marino * $FreeBSD: head/lib/libthr/thread/thr_fork.c 213096 2010-08-23 $
2871b3fa15SDavid Xu */
2971b3fa15SDavid Xu
3071b3fa15SDavid Xu /*
3171b3fa15SDavid Xu * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
3271b3fa15SDavid Xu * All rights reserved.
3371b3fa15SDavid Xu *
3471b3fa15SDavid Xu * Redistribution and use in source and binary forms, with or without
3571b3fa15SDavid Xu * modification, are permitted provided that the following conditions
3671b3fa15SDavid Xu * are met:
3771b3fa15SDavid Xu * 1. Redistributions of source code must retain the above copyright
3871b3fa15SDavid Xu * notice, this list of conditions and the following disclaimer.
3971b3fa15SDavid Xu * 2. Redistributions in binary form must reproduce the above copyright
4071b3fa15SDavid Xu * notice, this list of conditions and the following disclaimer in the
4171b3fa15SDavid Xu * documentation and/or other materials provided with the distribution.
42fcf53d9bSJohn Marino * 3. Neither the name of the author nor the names of any co-contributors
4371b3fa15SDavid Xu * may be used to endorse or promote products derived from this software
4471b3fa15SDavid Xu * without specific prior written permission.
4571b3fa15SDavid Xu *
4671b3fa15SDavid Xu * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
4771b3fa15SDavid Xu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4871b3fa15SDavid Xu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4971b3fa15SDavid Xu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
5071b3fa15SDavid Xu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5171b3fa15SDavid Xu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5271b3fa15SDavid Xu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5371b3fa15SDavid Xu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5471b3fa15SDavid Xu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5571b3fa15SDavid Xu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5671b3fa15SDavid Xu * SUCH DAMAGE.
5771b3fa15SDavid Xu *
5871b3fa15SDavid Xu */
599e2ee207SJoerg Sonnenberger
6082657471SMarkus Pfeiffer #include <sys/syscall.h>
61f049c502Szrj #include "namespace.h"
629e2ee207SJoerg Sonnenberger #include <machine/tls.h>
6371b3fa15SDavid Xu #include <errno.h>
64fcf53d9bSJohn Marino #include <link.h>
6571b3fa15SDavid Xu #include <string.h>
6671b3fa15SDavid Xu #include <stdlib.h>
6771b3fa15SDavid Xu #include <unistd.h>
6871b3fa15SDavid Xu #include <fcntl.h>
6971b3fa15SDavid Xu #include <pthread.h>
7071b3fa15SDavid Xu #include <spinlock.h>
7198247283SMatthew Dillon #include <sys/file.h>
72fc71f871SDavid Xu #include "un-namespace.h"
7371b3fa15SDavid Xu
7471b3fa15SDavid Xu #include "libc_private.h"
7571b3fa15SDavid Xu #include "thr_private.h"
7671b3fa15SDavid Xu
77e8382b15SDavid Xu struct atfork_head _thr_atfork_list;
78fcaa7a3aSMatthew Dillon struct atfork_head _thr_atfork_kern_list;
79e8382b15SDavid Xu umtx_t _thr_atfork_lock;
80e8382b15SDavid Xu
81fcaa7a3aSMatthew Dillon /*
82fcaa7a3aSMatthew Dillon * Execute a function in parent before and after the fork, and
83fcaa7a3aSMatthew Dillon * in the child.
84fcaa7a3aSMatthew Dillon */
8571b3fa15SDavid Xu int
_pthread_atfork(void (* prepare)(void),void (* parent)(void),void (* child)(void))8671b3fa15SDavid Xu _pthread_atfork(void (*prepare)(void), void (*parent)(void),
8771b3fa15SDavid Xu void (*child)(void))
8871b3fa15SDavid Xu {
89*940be950Szrj pthread_t curthread;
9071b3fa15SDavid Xu struct pthread_atfork *af;
9171b3fa15SDavid Xu
92e7bf3f77SMatthew Dillon af = __malloc(sizeof(struct pthread_atfork));
93e7bf3f77SMatthew Dillon if (af == NULL)
9471b3fa15SDavid Xu return (ENOMEM);
9571b3fa15SDavid Xu
969e2ee207SJoerg Sonnenberger curthread = tls_get_curthread();
9771b3fa15SDavid Xu af->prepare = prepare;
9871b3fa15SDavid Xu af->parent = parent;
9971b3fa15SDavid Xu af->child = child;
10071b3fa15SDavid Xu THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
10171b3fa15SDavid Xu TAILQ_INSERT_TAIL(&_thr_atfork_list, af, qe);
10271b3fa15SDavid Xu THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
10371b3fa15SDavid Xu return (0);
10471b3fa15SDavid Xu }
10571b3fa15SDavid Xu
106fcaa7a3aSMatthew Dillon /*
107fcaa7a3aSMatthew Dillon * Private at-fork used by the rtld and sem code, guaranteed to order
108fcaa7a3aSMatthew Dillon * after user fork handlers in prepare, and before user fork handlers
109fcaa7a3aSMatthew Dillon * in the post-fork parent and in the child.
110fcaa7a3aSMatthew Dillon *
111fcaa7a3aSMatthew Dillon * This is used to ensure no interference between internal and user
112fcaa7a3aSMatthew Dillon * fork handlers, in particular we do not want to lock-out rtld or
113fcaa7a3aSMatthew Dillon * semaphores before user fork handlers run, and we want to recover
114fcaa7a3aSMatthew Dillon * before any user post-fork handlers run.
115fcaa7a3aSMatthew Dillon */
116fcaa7a3aSMatthew Dillon void
_thr_atfork_kern(void (* prepare)(void),void (* parent)(void),void (* child)(void))117fcaa7a3aSMatthew Dillon _thr_atfork_kern(void (*prepare)(void), void (*parent)(void),
118fcaa7a3aSMatthew Dillon void (*child)(void))
119fcaa7a3aSMatthew Dillon {
120*940be950Szrj pthread_t curthread;
121fcaa7a3aSMatthew Dillon struct pthread_atfork *af;
122fcaa7a3aSMatthew Dillon
123e7bf3f77SMatthew Dillon af = __malloc(sizeof(struct pthread_atfork));
124fcaa7a3aSMatthew Dillon
125fcaa7a3aSMatthew Dillon curthread = tls_get_curthread();
126fcaa7a3aSMatthew Dillon af->prepare = prepare;
127fcaa7a3aSMatthew Dillon af->parent = parent;
128fcaa7a3aSMatthew Dillon af->child = child;
129fcaa7a3aSMatthew Dillon THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
130fcaa7a3aSMatthew Dillon TAILQ_INSERT_TAIL(&_thr_atfork_kern_list, af, qe);
131fcaa7a3aSMatthew Dillon THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
132fcaa7a3aSMatthew Dillon }
133fcaa7a3aSMatthew Dillon
134fcf53d9bSJohn Marino void
__pthread_cxa_finalize(struct dl_phdr_info * phdr_info)135fcf53d9bSJohn Marino __pthread_cxa_finalize(struct dl_phdr_info *phdr_info)
136fcf53d9bSJohn Marino {
137*940be950Szrj pthread_t curthread;
138fcf53d9bSJohn Marino struct pthread_atfork *af, *af1;
139fcf53d9bSJohn Marino
140fcf53d9bSJohn Marino curthread = tls_get_curthread();
141fcf53d9bSJohn Marino THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
142fcf53d9bSJohn Marino TAILQ_FOREACH_MUTABLE(af, &_thr_atfork_list, qe, af1) {
143fcf53d9bSJohn Marino if (__elf_phdr_match_addr(phdr_info, af->prepare) ||
144fcf53d9bSJohn Marino __elf_phdr_match_addr(phdr_info, af->parent) ||
145fcf53d9bSJohn Marino __elf_phdr_match_addr(phdr_info, af->child)) {
146fcf53d9bSJohn Marino TAILQ_REMOVE(&_thr_atfork_list, af, qe);
147e7bf3f77SMatthew Dillon __free(af);
148fcf53d9bSJohn Marino }
149fcf53d9bSJohn Marino }
150fcf53d9bSJohn Marino THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
151fcf53d9bSJohn Marino }
152fcf53d9bSJohn Marino
15371b3fa15SDavid Xu /*
15471b3fa15SDavid Xu * For a while, allow libpthread to work with a libc that doesn't
15571b3fa15SDavid Xu * export the malloc lock.
15671b3fa15SDavid Xu */
15771b3fa15SDavid Xu #pragma weak __malloc_lock
15871b3fa15SDavid Xu
159fc71f871SDavid Xu pid_t _fork(void);
160fc71f871SDavid Xu
16171b3fa15SDavid Xu pid_t
_fork(void)16271b3fa15SDavid Xu _fork(void)
16371b3fa15SDavid Xu {
16471b3fa15SDavid Xu static umtx_t inprogress;
16571b3fa15SDavid Xu static int waiters;
16671b3fa15SDavid Xu umtx_t tmp;
16771b3fa15SDavid Xu
168*940be950Szrj pthread_t curthread;
16971b3fa15SDavid Xu struct pthread_atfork *af;
17071b3fa15SDavid Xu pid_t ret;
17171b3fa15SDavid Xu int errsave;
17271b3fa15SDavid Xu #ifndef __DragonFly__
17371b3fa15SDavid Xu int unlock_malloc;
17471b3fa15SDavid Xu #endif
17571b3fa15SDavid Xu
17671b3fa15SDavid Xu if (!_thr_is_inited())
17782657471SMarkus Pfeiffer return (__syscall(SYS_fork));
17871b3fa15SDavid Xu
17998247283SMatthew Dillon errsave = errno;
1809e2ee207SJoerg Sonnenberger curthread = tls_get_curthread();
18171b3fa15SDavid Xu
18271b3fa15SDavid Xu THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
18371b3fa15SDavid Xu tmp = inprogress;
18471b3fa15SDavid Xu while (tmp) {
18571b3fa15SDavid Xu waiters++;
18671b3fa15SDavid Xu THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
1879219c44cSDavid Xu _thr_umtx_wait(&inprogress, tmp, NULL, 0);
18871b3fa15SDavid Xu THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
18971b3fa15SDavid Xu waiters--;
19071b3fa15SDavid Xu tmp = inprogress;
19171b3fa15SDavid Xu }
19271b3fa15SDavid Xu inprogress = 1;
19371b3fa15SDavid Xu THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
19471b3fa15SDavid Xu
19571b3fa15SDavid Xu /* Run down atfork prepare handlers. */
19671b3fa15SDavid Xu TAILQ_FOREACH_REVERSE(af, &_thr_atfork_list, atfork_head, qe) {
19771b3fa15SDavid Xu if (af->prepare != NULL)
19871b3fa15SDavid Xu af->prepare();
19971b3fa15SDavid Xu }
20071b3fa15SDavid Xu
20171b3fa15SDavid Xu #ifndef __DragonFly__
20271b3fa15SDavid Xu /*
20371b3fa15SDavid Xu * Try our best to protect memory from being corrupted in
20471b3fa15SDavid Xu * child process because another thread in malloc code will
20571b3fa15SDavid Xu * simply be kill by fork().
20671b3fa15SDavid Xu */
20771b3fa15SDavid Xu if ((_thr_isthreaded() != 0) && (__malloc_lock != NULL)) {
20871b3fa15SDavid Xu unlock_malloc = 1;
20971b3fa15SDavid Xu _spinlock(__malloc_lock);
21071b3fa15SDavid Xu } else {
21171b3fa15SDavid Xu unlock_malloc = 0;
21271b3fa15SDavid Xu }
21371b3fa15SDavid Xu #endif
21471b3fa15SDavid Xu
215fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
216fcaa7a3aSMatthew Dillon _thr_log("fork-parent\n", 12);
217fcaa7a3aSMatthew Dillon #endif
21871b3fa15SDavid Xu
21998247283SMatthew Dillon _thr_signal_block(curthread);
22098247283SMatthew Dillon
22198247283SMatthew Dillon /*
22298247283SMatthew Dillon * Must be executed Just before the fork.
22398247283SMatthew Dillon */
22498247283SMatthew Dillon TAILQ_FOREACH_REVERSE(af, &_thr_atfork_kern_list, atfork_head, qe) {
22598247283SMatthew Dillon if (af->prepare != NULL)
22698247283SMatthew Dillon af->prepare();
22798247283SMatthew Dillon }
22898247283SMatthew Dillon
22971b3fa15SDavid Xu /* Fork a new process: */
23082657471SMarkus Pfeiffer if ((ret = __syscall(SYS_fork)) == 0) {
23198247283SMatthew Dillon /*
23298247283SMatthew Dillon * Child process.
23398247283SMatthew Dillon *
23498247283SMatthew Dillon * NOTE: We are using the saved errno from above. Do not
23598247283SMatthew Dillon * reload errno here.
23698247283SMatthew Dillon */
23771b3fa15SDavid Xu inprogress = 0;
23898247283SMatthew Dillon
23998247283SMatthew Dillon /*
24098247283SMatthew Dillon * Internal child fork handlers must be run immediately.
24198247283SMatthew Dillon */
24298247283SMatthew Dillon TAILQ_FOREACH(af, &_thr_atfork_kern_list, qe) {
24398247283SMatthew Dillon if (af->child != NULL)
24498247283SMatthew Dillon af->child();
24598247283SMatthew Dillon }
24698247283SMatthew Dillon
24771b3fa15SDavid Xu curthread->cancelflags &= ~THR_CANCEL_NEEDED;
24871b3fa15SDavid Xu /*
24971b3fa15SDavid Xu * Thread list will be reinitialized, and later we call
25071b3fa15SDavid Xu * _libpthread_init(), it will add us back to list.
25171b3fa15SDavid Xu */
25271b3fa15SDavid Xu curthread->tlflags &= ~(TLFLAGS_IN_TDLIST | TLFLAGS_DETACHED);
25371b3fa15SDavid Xu
25471b3fa15SDavid Xu /* child is a new kernel thread. */
25571b3fa15SDavid Xu curthread->tid = _thr_get_tid();
25671b3fa15SDavid Xu
25771b3fa15SDavid Xu /* clear other threads locked us. */
25871b3fa15SDavid Xu _thr_umtx_init(&curthread->lock);
25971b3fa15SDavid Xu _thr_umtx_init(&_thr_atfork_lock);
26071b3fa15SDavid Xu _thr_setthreaded(0);
26171b3fa15SDavid Xu
26271b3fa15SDavid Xu /* reinitialize libc spinlocks, this includes __malloc_lock. */
26371b3fa15SDavid Xu _thr_spinlock_init();
264fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
265fcaa7a3aSMatthew Dillon _thr_log("fork-child\n", 11);
266fcaa7a3aSMatthew Dillon #endif
2674cc8110fSMatthew Dillon _mutex_fork(curthread, curthread->tid);
26871b3fa15SDavid Xu
26971b3fa15SDavid Xu /* reinitalize library. */
27071b3fa15SDavid Xu _libpthread_init(curthread);
27171b3fa15SDavid Xu
27271b3fa15SDavid Xu /* Ready to continue, unblock signals. */
27371b3fa15SDavid Xu _thr_signal_unblock(curthread);
27471b3fa15SDavid Xu
27571b3fa15SDavid Xu /* Run down atfork child handlers. */
27671b3fa15SDavid Xu TAILQ_FOREACH(af, &_thr_atfork_list, qe) {
27771b3fa15SDavid Xu if (af->child != NULL)
27871b3fa15SDavid Xu af->child();
27971b3fa15SDavid Xu }
28071b3fa15SDavid Xu } else {
28171b3fa15SDavid Xu /* Parent process */
28271b3fa15SDavid Xu errsave = errno;
28371b3fa15SDavid Xu
28471b3fa15SDavid Xu #ifndef __DragonFly__
28571b3fa15SDavid Xu if (unlock_malloc)
28671b3fa15SDavid Xu _spinunlock(__malloc_lock);
28771b3fa15SDavid Xu #endif
288fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
289fcaa7a3aSMatthew Dillon _thr_log("fork-done\n", 10);
290fcaa7a3aSMatthew Dillon #endif
29171b3fa15SDavid Xu /* Run down atfork parent handlers. */
292fcaa7a3aSMatthew Dillon TAILQ_FOREACH(af, &_thr_atfork_kern_list, qe) {
293fcaa7a3aSMatthew Dillon if (af->parent != NULL)
294fcaa7a3aSMatthew Dillon af->parent();
295fcaa7a3aSMatthew Dillon }
29698247283SMatthew Dillon
29798247283SMatthew Dillon /* Ready to continue, unblock signals. */
29898247283SMatthew Dillon _thr_signal_unblock(curthread);
29998247283SMatthew Dillon
30071b3fa15SDavid Xu TAILQ_FOREACH(af, &_thr_atfork_list, qe) {
30171b3fa15SDavid Xu if (af->parent != NULL)
30271b3fa15SDavid Xu af->parent();
30371b3fa15SDavid Xu }
30471b3fa15SDavid Xu
30571b3fa15SDavid Xu THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
30671b3fa15SDavid Xu inprogress = 0;
30771b3fa15SDavid Xu if (waiters)
30898247283SMatthew Dillon _thr_umtx_wake(&inprogress, 0);
30971b3fa15SDavid Xu THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
31071b3fa15SDavid Xu }
31171b3fa15SDavid Xu errno = errsave;
31271b3fa15SDavid Xu
31371b3fa15SDavid Xu /* Return the process ID: */
31471b3fa15SDavid Xu return (ret);
31571b3fa15SDavid Xu }
3165a1048c8SDavid Xu
3175a1048c8SDavid Xu __strong_reference(_fork, fork);
3185a1048c8SDavid Xu __strong_reference(_pthread_atfork, pthread_atfork);
319