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 int pgsize = getpagesize(); 71 int ret = -1; 72 73 if (pgsize < sizeof(*p)) 74 return (-1); 75 _ATEXIT_LOCK(); 76 p = __atexit; 77 if (p != NULL) { 78 if (p->ind + 1 >= p->max) 79 p = NULL; 80 else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE)) 81 goto unlock; 82 } 83 if (p == NULL) { 84 p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, 85 MAP_ANON | MAP_PRIVATE, -1, 0); 86 if (p == MAP_FAILED) 87 goto unlock; 88 if (__atexit == NULL) { 89 memset(&p->fns[0], 0, sizeof(p->fns[0])); 90 p->ind = 1; 91 } else 92 p->ind = 0; 93 p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) / 94 sizeof(p->fns[0]); 95 p->next = __atexit; 96 __atexit = p; 97 } 98 fnp = &p->fns[p->ind++]; 99 fnp->fn_ptr = func; 100 fnp->fn_arg = arg; 101 fnp->fn_dso = dso; 102 if (mprotect(p, pgsize, PROT_READ)) 103 goto unlock; 104 restartloop = 1; 105 ret = 0; 106 unlock: 107 _ATEXIT_UNLOCK(); 108 return (ret); 109 } 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 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 int n, pgsize = getpagesize(); 135 static int call_depth; 136 137 _ATEXIT_LOCK(); 138 call_depth++; 139 140 restart: 141 restartloop = 0; 142 for (p = __atexit; p != NULL; p = p->next) { 143 for (n = p->ind; --n >= 0;) { 144 if (p->fns[n].fn_ptr == NULL) 145 continue; /* already called */ 146 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 fn = p->fns[n]; 154 if (mprotect(p, pgsize, PROT_READ | PROT_WRITE) == 0) { 155 p->fns[n].fn_ptr = NULL; 156 mprotect(p, pgsize, PROT_READ); 157 } 158 _ATEXIT_UNLOCK(); 159 (*fn.fn_ptr)(fn.fn_arg); 160 _ATEXIT_LOCK(); 161 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 int pgsize = getpagesize(); 210 211 if (pgsize < sizeof(*p)) 212 return; 213 _ATEXIT_LOCK(); 214 p = __atexit; 215 while (p != NULL && p->next != NULL) 216 p = p->next; 217 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 if (mprotect(p, pgsize, PROT_READ | PROT_WRITE)) 229 goto unlock; 230 } 231 p->fns[0].fn_ptr = (void (*)(void *))func; 232 p->fns[0].fn_arg = NULL; 233 p->fns[0].fn_dso = NULL; 234 mprotect(p, pgsize, PROT_READ); 235 restartloop = 1; 236 unlock: 237 _ATEXIT_UNLOCK(); 238 } 239