• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
45 struct pa_memtrap {
46     void *start;
47     size_t size;
48     pa_atomic_t bad;
49     pa_memtrap *next[2], *prev[2];
50 };
51 
52 static pa_memtrap *memtraps[2] = { NULL, NULL };
53 static pa_aupdate *aupdate;
54 static pa_static_mutex mutex = PA_STATIC_MUTEX_INIT; /* only required to serialize access to the write side */
55 
allocate_aupdate(void)56 static void allocate_aupdate(void) {
57     PA_ONCE_BEGIN {
58         aupdate = pa_aupdate_new();
59     } PA_ONCE_END;
60 }
61 
pa_memtrap_is_good(pa_memtrap * m)62 bool 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
sigsafe_error(const char * s)69 static void sigsafe_error(const char *s) {
70     size_t ret PA_GCC_UNUSED;
71     ret = write(STDERR_FILENO, s, strlen(s));
72 }
73 
signal_handler(int sig,siginfo_t * si,void * data)74 static 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 
102 fail:
103     pa_aupdate_read_end(aupdate);
104 
105     sigsafe_error("Failed to handle SIGBUS.\n");
106     abort();
107 }
108 #endif
109 
memtrap_link(pa_memtrap * m,unsigned j)110 static 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 
memtrap_unlink(pa_memtrap * m,unsigned j)121 static 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 
pa_memtrap_add(const void * start,size_t size)133 pa_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 
pa_memtrap_remove(pa_memtrap * m)165 void 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 
pa_memtrap_update(pa_memtrap * m,const void * start,size_t size)187 pa_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 
219 unlock:
220     pa_aupdate_write_end(aupdate);
221 
222     pa_mutex_unlock(mx);
223 
224     return m;
225 }
226 
pa_memtrap_install(void)227 void 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