GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/stdlib/atexit.c Lines: 61 84 72.6 %
Date: 2017-11-07 Branches: 32 74 43.2 %

Line Branch Exec Source
1
/*	$OpenBSD: atexit.c,v 1.24 2015/11/10 04:14:03 guenther Exp $ */
2
/*
3
 * Copyright (c) 2002 Daniel Hartmeier
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
 *
10
 *    - Redistributions of source code must retain the above copyright
11
 *      notice, this list of conditions and the following disclaimer.
12
 *    - Redistributions in binary form must reproduce the above
13
 *      copyright notice, this list of conditions and the following
14
 *      disclaimer in the documentation and/or other materials provided
15
 *      with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
 * POSSIBILITY OF SUCH DAMAGE.
29
 *
30
 */
31
32
#include <sys/types.h>
33
#include <sys/mman.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37
#include "atexit.h"
38
#include "atfork.h"
39
#include "thread_private.h"
40
41
struct atexit *__atexit;
42
static int restartloop;
43
44
/* define and initialize the list */
45
struct atfork_listhead _atfork_list = TAILQ_HEAD_INITIALIZER(_atfork_list);
46
47
48
/*
49
 * Function pointers are stored in a linked list of pages. The list
50
 * is initially empty, and pages are allocated on demand. The first
51
 * function pointer in the first allocated page (the last one in
52
 * the linked list) is reserved for the cleanup function.
53
 *
54
 * Outside the following functions, all pages are mprotect()'ed
55
 * to prevent unintentional/malicious corruption.
56
 */
57
58
/*
59
 * Register a function to be performed at exit or when a shared object
60
 * with the given dso handle is unloaded dynamically.  Also used as
61
 * the backend for atexit().  For more info on this API, see:
62
 *
63
 *	http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor
64
 */
65
int
66
__cxa_atexit(void (*func)(void *), void *arg, void *dso)
67
{
68
	struct atexit *p = __atexit;
69
	struct atexit_fn *fnp;
70
3351234
	int pgsize = getpagesize();
71
	int ret = -1;
72
73
1675617
	if (pgsize < sizeof(*p))
74
		return (-1);
75
1675617
	_ATEXIT_LOCK();
76
1675617
	p = __atexit;
77
1675617
	if (p != NULL) {
78
1600936
		if (p->ind + 1 >= p->max)
79
8127
			p = NULL;
80
1592809
		else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
81
			goto unlock;
82
	}
83
1675617
	if (p == NULL) {
84
82808
		p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
85
		    MAP_ANON | MAP_PRIVATE, -1, 0);
86
82808
		if (p == MAP_FAILED)
87
			goto unlock;
88
82808
		if (__atexit == NULL) {
89
74681
			memset(&p->fns[0], 0, sizeof(p->fns[0]));
90
			p->ind = 1;
91
74681
		} else
92
			p->ind = 0;
93
82808
		p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
94
		    sizeof(p->fns[0]);
95
82808
		p->next = __atexit;
96
82808
		__atexit = p;
97
82808
	}
98
1675617
	fnp = &p->fns[p->ind++];
99
1675617
	fnp->fn_ptr = func;
100
1675617
	fnp->fn_arg = arg;
101
1675617
	fnp->fn_dso = dso;
102
1675617
	if (mprotect(p, pgsize, PROT_READ))
103
		goto unlock;
104
1675617
	restartloop = 1;
105
1675617
	ret = 0;
106
unlock:
107
1675617
	_ATEXIT_UNLOCK();
108
1675617
	return (ret);
109
1675617
}
110
DEF_STRONG(__cxa_atexit);
111
112
/*
113
 * Copy of atexit() used by libc and anything staticly linked into the
114
 * executable.  This passes NULL for the dso, so the callbacks are only
115
 * invoked by exit() and not dlclose()
116
 */
117
int
118
atexit(void (*fn)(void))
119
{
120
3351234
	return (__cxa_atexit((void (*)(void *))fn, NULL, NULL));
121
}
122
DEF_STRONG(atexit);
123
124
/*
125
 * Call all handlers registered with __cxa_atexit() for the shared
126
 * object owning 'dso'.
127
 * Note: if 'dso' is NULL, then all remaining handlers are called.
128
 */
129
void
130
__cxa_finalize(void *dso)
131
{
132
	struct atexit *p, *q;
133
	struct atexit_fn fn;
134
149362
	int n, pgsize = getpagesize();
135
	static int call_depth;
136
137
74681
	_ATEXIT_LOCK();
138
74681
	call_depth++;
139
140
restart:
141
74681
	restartloop = 0;
142
165616
	for (p = __atexit; p != NULL; p = p->next) {
143
1617190
		for (n = p->ind; --n >= 0;) {
144
1526255
			if (p->fns[n].fn_ptr == NULL)
145
				continue;	/* already called */
146

1526255
			if (dso != NULL && dso != p->fns[n].fn_dso)
147
				continue;	/* wrong DSO */
148
149
			/*
150
			 * Mark handler as having been already called to avoid
151
			 * dupes and loops, then call the appropriate function.
152
			 */
153
1526255
			fn = p->fns[n];
154
1526255
			if (mprotect(p, pgsize, PROT_READ | PROT_WRITE) == 0) {
155
1526255
				p->fns[n].fn_ptr = NULL;
156
1526255
				mprotect(p, pgsize, PROT_READ);
157
1526255
			}
158
1526255
			_ATEXIT_UNLOCK();
159
1451574
			(*fn.fn_ptr)(fn.fn_arg);
160
1451574
			_ATEXIT_LOCK();
161
1451574
			if (restartloop)
162
				goto restart;
163
		}
164
	}
165
166
	call_depth--;
167
168
	/*
169
	 * If called via exit(), unmap the pages since we have now run
170
	 * all the handlers.  We defer this until calldepth == 0 so that
171
	 * we don't unmap things prematurely if called recursively.
172
	 */
173
	if (dso == NULL && call_depth == 0) {
174
		for (p = __atexit; p != NULL; ) {
175
			q = p;
176
			p = p->next;
177
			munmap(q, pgsize);
178
		}
179
		__atexit = NULL;
180
	}
181
	_ATEXIT_UNLOCK();
182
183
	/*
184
	 * If unloading a DSO, unregister any atfork handlers registered
185
	 * by it.  Skip the locking if the list is currently empty.
186
	 */
187
	if (dso != NULL && TAILQ_FIRST(&_atfork_list) != NULL) {
188
		struct atfork_fn *af, *afnext;
189
190
		_ATFORK_LOCK();
191
		TAILQ_FOREACH_SAFE(af, &_atfork_list, fn_next, afnext)
192
			if (af->fn_dso == dso) {
193
				TAILQ_REMOVE(&_atfork_list, af, fn_next);
194
				free(af);
195
			}
196
		_ATFORK_UNLOCK();
197
198
	}
199
}
200
DEF_STRONG(__cxa_finalize);
201
202
/*
203
 * Register the cleanup function
204
 */
205
void
206
__atexit_register_cleanup(void (*func)(void))
207
{
208
	struct atexit *p;
209
149376
	int pgsize = getpagesize();
210
211
74688
	if (pgsize < sizeof(*p))
212
		return;
213
74688
	_ATEXIT_LOCK();
214
74688
	p = __atexit;
215

309588
	while (p != NULL && p->next != NULL)
216
2709
		p = p->next;
217
74688
	if (p == NULL) {
218
		p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
219
		    MAP_ANON | MAP_PRIVATE, -1, 0);
220
		if (p == MAP_FAILED)
221
			goto unlock;
222
		p->ind = 1;
223
		p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
224
		    sizeof(p->fns[0]);
225
		p->next = NULL;
226
		__atexit = p;
227
	} else {
228
74688
		if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
229
			goto unlock;
230
	}
231
74688
	p->fns[0].fn_ptr = (void (*)(void *))func;
232
74688
	p->fns[0].fn_arg = NULL;
233
74688
	p->fns[0].fn_dso = NULL;
234
74688
	mprotect(p, pgsize, PROT_READ);
235
74688
	restartloop = 1;
236
unlock:
237
74688
	_ATEXIT_UNLOCK();
238
149376
}