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 |
} |