View | Details | Raw Unified | Return to bug 215709
Collapse All | Expand All

(-)b/lib/libc/include/libc_private.h (+2 lines)
Lines 272-277 void _malloc_thread_cleanup(void); Link Here
272
 * thread is exiting, so its thread-local dtors should be called.
272
 * thread is exiting, so its thread-local dtors should be called.
273
 */
273
 */
274
void __cxa_thread_call_dtors(void);
274
void __cxa_thread_call_dtors(void);
275
int __cxa_thread_atexit_hidden(void (*dtor_func)(void *), void *obj,
276
    void *dso_symbol) __hidden;
275
277
276
/*
278
/*
277
 * These functions are used by the threading libraries in order to protect
279
 * These functions are used by the threading libraries in order to protect
(-)b/lib/libc/stdlib/Makefile.inc (-1 / +3 lines)
Lines 5-11 Link Here
5
.PATH: ${LIBC_SRCTOP}/${LIBC_ARCH}/stdlib ${LIBC_SRCTOP}/stdlib
5
.PATH: ${LIBC_SRCTOP}/${LIBC_ARCH}/stdlib ${LIBC_SRCTOP}/stdlib
6
6
7
MISRCS+=C99_Exit.c a64l.c abort.c abs.c atexit.c atof.c atoi.c atol.c atoll.c \
7
MISRCS+=C99_Exit.c a64l.c abort.c abs.c atexit.c atof.c atoi.c atol.c atoll.c \
8
	bsearch.c cxa_thread_atexit.c div.c exit.c getenv.c getopt.c getopt_long.c \
8
	bsearch.c \
9
	cxa_thread_atexit.c cxa_thread_atexit_impl.c \
10
	div.c exit.c getenv.c getopt.c getopt_long.c \
9
	getsubopt.c hcreate.c hcreate_r.c hdestroy_r.c heapsort.c heapsort_b.c \
11
	getsubopt.c hcreate.c hcreate_r.c hdestroy_r.c heapsort.c heapsort_b.c \
10
	hsearch_r.c imaxabs.c imaxdiv.c \
12
	hsearch_r.c imaxabs.c imaxdiv.c \
11
	insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c \
13
	insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c \
(-)b/lib/libc/stdlib/Symbol.map (+1 lines)
Lines 118-123 FBSD_1.4 { Link Here
118
118
119
FBSD_1.5 {
119
FBSD_1.5 {
120
	__cxa_thread_atexit;
120
	__cxa_thread_atexit;
121
	__cxa_thread_atexit_impl;
121
};
122
};
122
123
123
FBSDprivate_1.0 {
124
FBSDprivate_1.0 {
(-)b/lib/libc/stdlib/cxa_thread_atexit.c (-105 / +5 lines)
Lines 1-7 Link Here
1
/*-
1
/*-
2
 * Copyright (c) 2016 Mahdi Mokhtari <mokhi64@gmail.com>
2
 * Copyright (c) 2017 The FreeBSD Foundation
3
 * All rights reserved.
3
 * All rights reserved.
4
 *
4
 *
5
 * Portions of this software were developed by Konstantin Belousov
6
 * under sponsorship from the FreeBSD Foundation.
7
 *
5
 * Redistribution and use in source and binary forms, with or without
8
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
9
 * modification, are permitted provided that the following conditions
7
 * are met:
10
 * are met:
Lines 27-140 Link Here
27
#include <sys/cdefs.h>
30
#include <sys/cdefs.h>
28
__FBSDID("$FreeBSD$");
31
__FBSDID("$FreeBSD$");
29
32
30
#include <sys/queue.h>
31
#include "namespace.h"
32
#include <errno.h>
33
#include <link.h>
34
#include <pthread.h>
35
#include <stddef.h>
36
#include <stdlib.h>
37
#include <stdio.h>
38
#include "un-namespace.h"
39
#include "libc_private.h"
33
#include "libc_private.h"
40
34
41
/*
42
 * C++11 introduces the thread_local scope (like __thread with some
43
 * additions).  As a key-feature it should support non-trivial
44
 * destructors, registered with __cxa_thread_atexit() to be executed
45
 * at the thread termination.
46
 *
47
 * The implemention keeps a _Thread_local list of destructors per each
48
 * thread, and calls __cxa_thread_call_dtors() on each thread's exit
49
 * to do cleanup.  For a thread calling exit(3), in particular, for
50
 * the initial thread returning from main(), we call
51
 * __cxa_thread_call_dtors() inside exit().
52
 *
53
 * It could be possible that a dynamically loaded library, use
54
 * thread_local variable but is dlclose()'d before thread exit.  The
55
 * destructor of this variable will then try to access the address,
56
 * for calling it but it's unloaded, so it'll crash.  We're using
57
 * __elf_phdr_match_addr() to detect and prevent such cases and so
58
 * prevent the crash.
59
 */
60
61
#define CXA_DTORS_ITERATIONS 4
62
63
struct cxa_thread_dtor {
64
	void *obj;
65
	void (*func)(void *);
66
	void *dso;
67
	LIST_ENTRY(cxa_thread_dtor) entry;
68
};
69
static _Thread_local LIST_HEAD(dtor_list, cxa_thread_dtor) dtors =
70
    LIST_HEAD_INITIALIZER(dtors);
71
72
int
35
int
73
__cxa_thread_atexit(void (*dtor_func)(void *), void *obj, void *dso_symbol)
36
__cxa_thread_atexit(void (*dtor_func)(void *), void *obj, void *dso_symbol)
74
{
37
{
75
	struct cxa_thread_dtor *new_dtor;
76
77
	new_dtor = malloc(sizeof(*new_dtor));
78
	if (new_dtor == NULL) {
79
		errno = ENOMEM; /* forcibly override malloc(3) error */
80
		return (-1);
81
	}
82
83
	new_dtor->obj = obj;
84
	new_dtor->func = dtor_func;
85
	new_dtor->dso = dso_symbol;
86
	LIST_INSERT_HEAD(&dtors, new_dtor, entry);
87
	return (0);
88
}
89
90
static void
91
walk_cb_call(struct cxa_thread_dtor *dtor)
92
{
93
	struct dl_phdr_info phdr_info;
94
95
	if (_rtld_addr_phdr(dtor->dso, &phdr_info) &&
96
	    __elf_phdr_match_addr(&phdr_info, dtor->func))
97
		dtor->func(dtor->obj);
98
	else
99
		fprintf(stderr, "__cxa_thread_call_dtors: dtr %p from "
100
		    "unloaded dso, skipping\n", (void *)(dtor->func));
101
}
102
103
static void
104
walk_cb_nocall(struct cxa_thread_dtor *dtor __unused)
105
{
106
}
107
108
static void
109
cxa_thread_walk(void (*cb)(struct cxa_thread_dtor *))
110
{
111
	struct cxa_thread_dtor *dtor, *tdtor;
112
113
	LIST_FOREACH_SAFE(dtor, &dtors, entry, tdtor) {
114
		LIST_REMOVE(dtor, entry);
115
		cb(dtor);
116
		free(dtor);
117
	}
118
}
119
120
/*
121
 * This is the callback function we use to call destructors, once for
122
 * each thread.  It is called in exit(3) in libc/stdlib/exit.c and
123
 * before exit_thread() in libthr/thread/thr_exit.c.
124
 */
125
void
126
__cxa_thread_call_dtors(void)
127
{
128
	int i;
129
130
	for (i = 0; i < CXA_DTORS_ITERATIONS && !LIST_EMPTY(&dtors); i++)
131
		cxa_thread_walk(walk_cb_call);
132
38
133
	if (!LIST_EMPTY(&dtors)) {
39
	return (__cxa_thread_atexit_hidden(dtor_func, obj, dso_symbol));
134
		fprintf(stderr, "Thread %p is exiting with more "
135
		    "thread-specific dtors created after %d iterations "
136
		    "of destructor calls\n",
137
		    _pthread_self(), i);
138
		cxa_thread_walk(walk_cb_nocall);
139
	}
140
}
40
}
(-)b/lib/libc/stdlib/cxa_thread_atexit_impl.c (+153 lines)
Added Link Here
1
/*-
2
 * Copyright (c) 2016 Mahdi Mokhtari <mokhi64@gmail.com>
3
 * Copyright (c) 2016, 2017 The FreeBSD Foundation
4
 * All rights reserved.
5
 *
6
 * Portions of this software were developed by Konstantin Belousov
7
 * under sponsorship from the FreeBSD Foundation.
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 */
30
31
#include <sys/cdefs.h>
32
__FBSDID("$FreeBSD$");
33
34
#include <sys/queue.h>
35
#include "namespace.h"
36
#include <errno.h>
37
#include <link.h>
38
#include <pthread.h>
39
#include <stddef.h>
40
#include <stdlib.h>
41
#include <stdio.h>
42
#include "un-namespace.h"
43
#include "libc_private.h"
44
45
/*
46
 * C++11 introduces the thread_local scope (like __thread with some
47
 * additions).  As a key-feature it should support non-trivial
48
 * destructors, registered with __cxa_thread_atexit() to be executed
49
 * at the thread termination.
50
 *
51
 * The implemention keeps a _Thread_local list of destructors per each
52
 * thread, and calls __cxa_thread_call_dtors() on each thread's exit
53
 * to do cleanup.  For a thread calling exit(3), in particular, for
54
 * the initial thread returning from main(), we call
55
 * __cxa_thread_call_dtors() inside exit().
56
 *
57
 * It could be possible that a dynamically loaded library, use
58
 * thread_local variable but is dlclose()'d before thread exit.  The
59
 * destructor of this variable will then try to access the address,
60
 * for calling it but it's unloaded, so it'll crash.  We're using
61
 * __elf_phdr_match_addr() to detect and prevent such cases and so
62
 * prevent the crash.
63
 */
64
65
#define CXA_DTORS_ITERATIONS 4
66
67
struct cxa_thread_dtor {
68
	void *obj;
69
	void (*func)(void *);
70
	void *dso;
71
	LIST_ENTRY(cxa_thread_dtor) entry;
72
};
73
static _Thread_local LIST_HEAD(dtor_list, cxa_thread_dtor) dtors =
74
    LIST_HEAD_INITIALIZER(dtors);
75
76
int
77
__cxa_thread_atexit_impl(void (*dtor_func)(void *), void *obj,
78
    void *dso_symbol)
79
{
80
81
	return (__cxa_thread_atexit_hidden(dtor_func, obj, dso_symbol));
82
}
83
84
int
85
__cxa_thread_atexit_hidden(void (*dtor_func)(void *), void *obj,
86
    void *dso_symbol)
87
{
88
	struct cxa_thread_dtor *new_dtor;
89
90
	new_dtor = malloc(sizeof(*new_dtor));
91
	if (new_dtor == NULL) {
92
		errno = ENOMEM; /* forcibly override malloc(3) error */
93
		return (-1);
94
	}
95
96
	new_dtor->obj = obj;
97
	new_dtor->func = dtor_func;
98
	new_dtor->dso = dso_symbol;
99
	LIST_INSERT_HEAD(&dtors, new_dtor, entry);
100
	return (0);
101
}
102
103
static void
104
walk_cb_call(struct cxa_thread_dtor *dtor)
105
{
106
	struct dl_phdr_info phdr_info;
107
108
	if (_rtld_addr_phdr(dtor->dso, &phdr_info) &&
109
	    __elf_phdr_match_addr(&phdr_info, dtor->func))
110
		dtor->func(dtor->obj);
111
	else
112
		fprintf(stderr, "__cxa_thread_call_dtors: dtr %p from "
113
		    "unloaded dso, skipping\n", (void *)(dtor->func));
114
}
115
116
static void
117
walk_cb_nocall(struct cxa_thread_dtor *dtor __unused)
118
{
119
}
120
121
static void
122
cxa_thread_walk(void (*cb)(struct cxa_thread_dtor *))
123
{
124
	struct cxa_thread_dtor *dtor, *tdtor;
125
126
	LIST_FOREACH_SAFE(dtor, &dtors, entry, tdtor) {
127
		LIST_REMOVE(dtor, entry);
128
		cb(dtor);
129
		free(dtor);
130
	}
131
}
132
133
/*
134
 * This is the callback function we use to call destructors, once for
135
 * each thread.  It is called in exit(3) in libc/stdlib/exit.c and
136
 * before exit_thread() in libthr/thread/thr_exit.c.
137
 */
138
void
139
__cxa_thread_call_dtors(void)
140
{
141
	int i;
142
143
	for (i = 0; i < CXA_DTORS_ITERATIONS && !LIST_EMPTY(&dtors); i++)
144
		cxa_thread_walk(walk_cb_call);
145
146
	if (!LIST_EMPTY(&dtors)) {
147
		fprintf(stderr, "Thread %p is exiting with more "
148
		    "thread-specific dtors created after %d iterations "
149
		    "of destructor calls\n",
150
		    _pthread_self(), i);
151
		cxa_thread_walk(walk_cb_nocall);
152
	}
153
}

Return to bug 215709