]> code.delx.au - pulseaudio/blob - src/pulsecore/memtrap.c
When MAP_ANONYMOUS is missing, fallback to MAP_ANON.
[pulseaudio] / src / pulsecore / memtrap.c
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, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <signal.h>
27 #include <sys/mman.h>
28
29 /* This is deprecated on glibc but is still used by FreeBSD */
30 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
31 # define MAP_ANONYMOUS MAP_ANON
32 #endif
33
34 #include <pulse/xmalloc.h>
35
36 #include <pulsecore/core-util.h>
37 #include <pulsecore/aupdate.h>
38 #include <pulsecore/atomic.h>
39 #include <pulsecore/once.h>
40
41 #include "memtrap.h"
42
43 struct pa_memtrap {
44 void *start;
45 size_t size;
46 pa_atomic_t bad;
47 pa_memtrap *next[2], *prev[2];
48 };
49
50 static pa_memtrap *memtraps[2] = { NULL, NULL };
51 static pa_aupdate *aupdate;
52
53 static void allocate_aupdate(void) {
54 PA_ONCE_BEGIN {
55 aupdate = pa_aupdate_new();
56 } PA_ONCE_END;
57 }
58
59 pa_bool_t pa_memtrap_is_good(pa_memtrap *m) {
60 pa_assert(m);
61
62 return !pa_atomic_load(&m->bad);
63 }
64
65 static void sigsafe_error(const char *s) {
66 write(STDERR_FILENO, s, strlen(s));
67 }
68
69 static void signal_handler(int sig, siginfo_t* si, void *data) {
70 unsigned j;
71 pa_memtrap *m;
72 void *r;
73
74 j = pa_aupdate_read_begin(aupdate);
75
76 for (m = memtraps[j]; m; m = m->next[j])
77 if (si->si_addr >= m->start &&
78 (uint8_t*) si->si_addr < (uint8_t*) m->start + m->size)
79 break;
80
81 if (!m)
82 goto fail;
83
84 pa_atomic_store(&m->bad, 1);
85
86 /* Remap anonymous memory into the bad segment */
87 if ((r = mmap(m->start, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
88 sigsafe_error("mmap() failed.\n");
89 goto fail;
90 }
91
92 pa_assert(r == m->start);
93
94 pa_aupdate_read_end(aupdate);
95 return;
96
97 fail:
98 pa_aupdate_read_end(aupdate);
99
100 sigsafe_error("Failed to handle SIGBUS.\n");
101 abort();
102 }
103
104 static void memtrap_link(pa_memtrap *m, unsigned j) {
105 pa_assert(m);
106
107 m->prev[j] = NULL;
108 m->next[j] = memtraps[j];
109 memtraps[j] = m;
110 }
111
112 static void memtrap_unlink(pa_memtrap *m, unsigned j) {
113 pa_assert(m);
114
115 if (m->next[j])
116 m->next[j]->prev[j] = m->prev[j];
117
118 if (m->prev[j])
119 m->prev[j]->next[j] = m->next[j];
120 else
121 memtraps[j] = m->next[j];
122 }
123
124 pa_memtrap* pa_memtrap_add(const void *start, size_t size) {
125 pa_memtrap *m = NULL;
126 unsigned j;
127
128 pa_assert(start);
129 pa_assert(size > 0);
130
131 start = PA_PAGE_ALIGN_PTR(start);
132 size = PA_PAGE_ALIGN(size);
133
134 m = pa_xnew(pa_memtrap, 1);
135 m->start = (void*) start;
136 m->size = size;
137 pa_atomic_store(&m->bad, 0);
138
139 allocate_aupdate();
140
141 j = pa_aupdate_write_begin(aupdate);
142 memtrap_link(m, j);
143 j = pa_aupdate_write_swap(aupdate);
144 memtrap_link(m, j);
145 pa_aupdate_write_end(aupdate);
146
147 return m;
148 }
149
150 void pa_memtrap_remove(pa_memtrap *m) {
151 unsigned j;
152
153 pa_assert(m);
154
155 allocate_aupdate();
156
157 j = pa_aupdate_write_begin(aupdate);
158 memtrap_unlink(m, j);
159 j = pa_aupdate_write_swap(aupdate);
160 memtrap_unlink(m, j);
161 pa_aupdate_write_end(aupdate);
162
163 pa_xfree(m);
164 }
165
166 pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
167 unsigned j;
168
169 pa_assert(m);
170
171 pa_assert(start);
172 pa_assert(size > 0);
173
174 start = PA_PAGE_ALIGN_PTR(start);
175 size = PA_PAGE_ALIGN(size);
176
177 allocate_aupdate();
178
179 j = pa_aupdate_write_begin(aupdate);
180
181 if (m->start == start && m->size == size)
182 goto unlock;
183
184 memtrap_unlink(m, j);
185 j = pa_aupdate_write_swap(aupdate);
186
187 m->start = (void*) start;
188 m->size = size;
189 pa_atomic_store(&m->bad, 0);
190
191 j = pa_aupdate_write_swap(aupdate);
192 memtrap_link(m, j);
193
194 unlock:
195 pa_aupdate_write_end(aupdate);
196
197 return m;
198 }
199
200 void pa_memtrap_install(void) {
201 struct sigaction sa;
202
203 allocate_aupdate();
204
205 memset(&sa, 0, sizeof(sa));
206 sa.sa_sigaction = signal_handler;
207 sa.sa_flags = SA_RESTART|SA_SIGINFO;
208
209 pa_assert_se(sigaction(SIGBUS, &sa, NULL) == 0);
210 }