1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <signal.h> 25 26#ifdef HAVE_SYS_MMAN_H 27#include <sys/mman.h> 28#endif 29 30/* This is deprecated on glibc but is still used by FreeBSD */ 31#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) 32# define MAP_ANONYMOUS MAP_ANON 33#endif 34 35#include <pulse/xmalloc.h> 36 37#include <pulsecore/core-util.h> 38#include <pulsecore/aupdate.h> 39#include <pulsecore/atomic.h> 40#include <pulsecore/once.h> 41#include <pulsecore/mutex.h> 42 43#include "memtrap.h" 44 45struct pa_memtrap { 46 void *start; 47 size_t size; 48 pa_atomic_t bad; 49 pa_memtrap *next[2], *prev[2]; 50}; 51 52static pa_memtrap *memtraps[2] = { NULL, NULL }; 53static pa_aupdate *aupdate; 54static pa_static_mutex mutex = PA_STATIC_MUTEX_INIT; /* only required to serialize access to the write side */ 55 56static void allocate_aupdate(void) { 57 PA_ONCE_BEGIN { 58 aupdate = pa_aupdate_new(); 59 } PA_ONCE_END; 60} 61 62bool pa_memtrap_is_good(pa_memtrap *m) { 63 pa_assert(m); 64 65 return !pa_atomic_load(&m->bad); 66} 67 68#ifdef HAVE_SIGACTION 69static void sigsafe_error(const char *s) { 70 size_t ret PA_GCC_UNUSED; 71 ret = write(STDERR_FILENO, s, strlen(s)); 72} 73 74static void signal_handler(int sig, siginfo_t* si, void *data) { 75 unsigned j; 76 pa_memtrap *m; 77 void *r; 78 79 j = pa_aupdate_read_begin(aupdate); 80 81 for (m = memtraps[j]; m; m = m->next[j]) 82 if (si->si_addr >= m->start && 83 (uint8_t*) si->si_addr < (uint8_t*) m->start + m->size) 84 break; 85 86 if (!m) 87 goto fail; 88 89 pa_atomic_store(&m->bad, 1); 90 91 /* Remap anonymous memory into the bad segment */ 92 if ((r = mmap(m->start, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0)) == MAP_FAILED) { 93 sigsafe_error("mmap() failed.\n"); 94 goto fail; 95 } 96 97 pa_assert(r == m->start); 98 99 pa_aupdate_read_end(aupdate); 100 return; 101 102fail: 103 pa_aupdate_read_end(aupdate); 104 105 sigsafe_error("Failed to handle SIGBUS.\n"); 106 abort(); 107} 108#endif 109 110static void memtrap_link(pa_memtrap *m, unsigned j) { 111 pa_assert(m); 112 113 m->prev[j] = NULL; 114 115 if ((m->next[j] = memtraps[j])) 116 m->next[j]->prev[j] = m; 117 118 memtraps[j] = m; 119} 120 121static void memtrap_unlink(pa_memtrap *m, unsigned j) { 122 pa_assert(m); 123 124 if (m->next[j]) 125 m->next[j]->prev[j] = m->prev[j]; 126 127 if (m->prev[j]) 128 m->prev[j]->next[j] = m->next[j]; 129 else 130 memtraps[j] = m->next[j]; 131} 132 133pa_memtrap* pa_memtrap_add(const void *start, size_t size) { 134 pa_memtrap *m = NULL; 135 unsigned j; 136 pa_mutex *mx; 137 138 pa_assert(start); 139 pa_assert(size > 0); 140 141 start = PA_PAGE_ALIGN_PTR(start); 142 size = PA_PAGE_ALIGN(size); 143 144 m = pa_xnew(pa_memtrap, 1); 145 m->start = (void*) start; 146 m->size = size; 147 pa_atomic_store(&m->bad, 0); 148 149 allocate_aupdate(); 150 151 mx = pa_static_mutex_get(&mutex, false, true); 152 pa_mutex_lock(mx); 153 154 j = pa_aupdate_write_begin(aupdate); 155 memtrap_link(m, j); 156 j = pa_aupdate_write_swap(aupdate); 157 memtrap_link(m, j); 158 pa_aupdate_write_end(aupdate); 159 160 pa_mutex_unlock(mx); 161 162 return m; 163} 164 165void pa_memtrap_remove(pa_memtrap *m) { 166 unsigned j; 167 pa_mutex *mx; 168 169 pa_assert(m); 170 171 allocate_aupdate(); 172 173 mx = pa_static_mutex_get(&mutex, false, true); 174 pa_mutex_lock(mx); 175 176 j = pa_aupdate_write_begin(aupdate); 177 memtrap_unlink(m, j); 178 j = pa_aupdate_write_swap(aupdate); 179 memtrap_unlink(m, j); 180 pa_aupdate_write_end(aupdate); 181 182 pa_mutex_unlock(mx); 183 184 pa_xfree(m); 185} 186 187pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) { 188 unsigned j; 189 pa_mutex *mx; 190 191 pa_assert(m); 192 193 pa_assert(start); 194 pa_assert(size > 0); 195 196 start = PA_PAGE_ALIGN_PTR(start); 197 size = PA_PAGE_ALIGN(size); 198 199 allocate_aupdate(); 200 201 mx = pa_static_mutex_get(&mutex, false, true); 202 pa_mutex_lock(mx); 203 204 j = pa_aupdate_write_begin(aupdate); 205 206 if (m->start == start && m->size == size) 207 goto unlock; 208 209 memtrap_unlink(m, j); 210 pa_aupdate_write_swap(aupdate); 211 212 m->start = (void*) start; 213 m->size = size; 214 pa_atomic_store(&m->bad, 0); 215 216 pa_assert_se(pa_aupdate_write_swap(aupdate) == j); 217 memtrap_link(m, j); 218 219unlock: 220 pa_aupdate_write_end(aupdate); 221 222 pa_mutex_unlock(mx); 223 224 return m; 225} 226 227void pa_memtrap_install(void) { 228#ifdef HAVE_SIGACTION 229 struct sigaction sa; 230 231 allocate_aupdate(); 232 233 memset(&sa, 0, sizeof(sa)); 234 sa.sa_sigaction = signal_handler; 235 sa.sa_flags = SA_RESTART|SA_SIGINFO; 236 237 pa_assert_se(sigaction(SIGBUS, &sa, NULL) == 0); 238#ifdef __FreeBSD_kernel__ 239 pa_assert_se(sigaction(SIGSEGV, &sa, NULL) == 0); 240#endif 241#endif 242} 243