1 /* 2 * Copyright (c) 2002 Daniel Hartmeier 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * - Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * - Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following 13 * disclaimer in the documentation and/or other materials provided 14 * with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 * 29 */ 30 31 #if defined(LIBC_SCCS) && !defined(lint) 32 static char *rcsid = "$OpenBSD: atexit.c,v 1.7 2002/09/14 22:03:14 dhartmei Exp $"; 33 #endif /* LIBC_SCCS and not lint */ 34 35 #include <sys/types.h> 36 #include <sys/mman.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include "atexit.h" 40 41 int __atexit_invalid = 1; 42 struct atexit *__atexit; 43 44 /* 45 * Function pointers are stored in a linked list of pages. The list 46 * is initially empty, and pages are allocated on demand. The first 47 * function pointer in the first allocated page (the last one in 48 * the linked list) is reserved for the cleanup function. 49 * 50 * Outside the following two functions, all pages are mprotect()'ed 51 * to prevent unintentional/malicious corruption. 52 * 53 * The free(malloc(1)) is a workaround causing malloc_init() to 54 * ensure that malloc.c gets the first mmap() call for its sbrk() 55 * games. 56 */ 57 58 /* 59 * Register a function to be performed at exit. 60 */ 61 int 62 atexit(fn) 63 void (*fn)(); 64 { 65 register struct atexit *p = __atexit; 66 register int pgsize = getpagesize(); 67 68 if (pgsize < sizeof(*p)) 69 return (-1); 70 if (p != NULL) { 71 if (p->ind + 1 >= p->max) 72 p = NULL; 73 else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE)) 74 return (-1); 75 } 76 if (p == NULL) { 77 if (__atexit_invalid) { 78 free(malloc(1)); 79 __atexit_invalid = 0; 80 } 81 p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, 82 MAP_ANON | MAP_PRIVATE, -1, 0); 83 if (p == MAP_FAILED) 84 return (-1); 85 if (__atexit == NULL) { 86 p->fns[0] = NULL; 87 p->ind = 1; 88 } else 89 p->ind = 0; 90 p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) / 91 sizeof(p->fns[0]); 92 p->next = __atexit; 93 __atexit = p; 94 } 95 p->fns[p->ind++] = fn; 96 if (mprotect(p, pgsize, PROT_READ)) 97 return (-1); 98 return (0); 99 } 100 101 /* 102 * Register the cleanup function 103 */ 104 void 105 __atexit_register_cleanup(fn) 106 void (*fn)(); 107 { 108 register struct atexit *p = __atexit; 109 register int pgsize = getpagesize(); 110 111 if (pgsize < sizeof(*p)) 112 return; 113 while (p != NULL && p->next != NULL) 114 p = p->next; 115 if (p == NULL) { 116 if (__atexit_invalid) { 117 free(malloc(1)); 118 __atexit_invalid = 0; 119 } 120 p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, 121 MAP_ANON | MAP_PRIVATE, -1, 0); 122 if (p == MAP_FAILED) 123 return; 124 p->ind = 1; 125 p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) / 126 sizeof(p->fns[0]); 127 p->next = NULL; 128 __atexit = p; 129 } else { 130 if (mprotect(p, pgsize, PROT_READ | PROT_WRITE)) 131 return; 132 } 133 p->fns[0] = fn; 134 mprotect(p, pgsize, PROT_READ); 135 } 136