1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <dirent.h>
34 #include <signal.h>
35
36 #ifdef HAVE_SYS_MMAN_H
37 #include <sys/mman.h>
38 #endif
39
40 /* This is deprecated on glibc but is still used by FreeBSD */
41 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
42 # define MAP_ANONYMOUS MAP_ANON
43 #endif
44
45 #include <pulse/xmalloc.h>
46 #include <pulse/gccmacro.h>
47
48 #include <pulsecore/memfd-wrappers.h>
49 #include <pulsecore/core-error.h>
50 #include <pulsecore/log.h>
51 #include <pulsecore/random.h>
52 #include <pulsecore/core-util.h>
53 #include <pulsecore/macro.h>
54 #include <pulsecore/atomic.h>
55 #include <pulsecore/mem.h>
56
57 #include "shm.h"
58
59 #if defined(__linux__) && !defined(MADV_REMOVE)
60 #define MADV_REMOVE 9
61 #endif
62
63 /* 1 GiB at max */
64 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024))
65
66 #ifdef __linux__
67 /* On Linux we know that the shared memory blocks are files in
68 * /dev/shm. We can use that information to list all blocks and
69 * cleanup unused ones */
70 #define SHM_PATH "/dev/shm/"
71 #define SHM_ID_LEN 10
72 #elif defined(__sun)
73 #define SHM_PATH "/tmp"
74 #define SHM_ID_LEN 15
75 #else
76 #undef SHM_PATH
77 #undef SHM_ID_LEN
78 #endif
79
80 #define SHM_MARKER ((int) 0xbeefcafe)
81
82 /* We now put this SHM marker at the end of each segment. It's
83 * optional, to not require a reboot when upgrading, though. Note that
84 * on multiarch systems 32bit and 64bit processes might access this
85 * region simultaneously. The header fields need to be independent
86 * from the process' word with */
87 struct shm_marker {
88 pa_atomic_t marker; /* 0xbeefcafe */
89 pa_atomic_t pid;
90 uint64_t _reserved1;
91 uint64_t _reserved2;
92 uint64_t _reserved3;
93 uint64_t _reserved4;
94 } PA_GCC_PACKED;
95
shm_marker_size(pa_mem_type_t type)96 static inline size_t shm_marker_size(pa_mem_type_t type) {
97 if (type == PA_MEM_TYPE_SHARED_POSIX)
98 return PA_ALIGN(sizeof(struct shm_marker));
99
100 return 0;
101 }
102
103 #ifdef HAVE_SHM_OPEN
segment_name(char * fn,size_t l,unsigned id)104 static char *segment_name(char *fn, size_t l, unsigned id) {
105 pa_snprintf(fn, l, "/pulse-shm-%u", id);
106 return fn;
107 }
108 #endif
109
privatemem_create(pa_shm * m,size_t size)110 static int privatemem_create(pa_shm *m, size_t size) {
111 pa_assert(m);
112 pa_assert(size > 0);
113
114 m->type = PA_MEM_TYPE_PRIVATE;
115 m->id = 0;
116 m->size = size;
117 m->do_unlink = false;
118 m->fd = -1;
119
120 #ifdef MAP_ANONYMOUS
121 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
122 pa_log("mmap() failed: %s", pa_cstrerror(errno));
123 return -1;
124 }
125 #elif defined(HAVE_POSIX_MEMALIGN)
126 {
127 int r;
128
129 if ((r = posix_memalign(&m->ptr, pa_page_size(), size)) < 0) {
130 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
131 return r;
132 }
133 }
134 #else
135 m->ptr = pa_xmalloc(m->size);
136 #endif
137
138 return 0;
139 }
140
sharedmem_create(pa_shm * m,pa_mem_type_t type,size_t size,mode_t mode)141 static int sharedmem_create(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) {
142 #if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
143 char fn[32];
144 int fd = -1;
145 struct shm_marker *marker;
146 bool do_unlink = false;
147
148 /* Each time we create a new SHM area, let's first drop all stale
149 * ones */
150 pa_shm_cleanup();
151
152 pa_random(&m->id, sizeof(m->id));
153
154 switch (type) {
155 #ifdef HAVE_SHM_OPEN
156 case PA_MEM_TYPE_SHARED_POSIX:
157 segment_name(fn, sizeof(fn), m->id);
158 fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode);
159 do_unlink = true;
160 break;
161 #endif
162 #ifdef HAVE_MEMFD
163 case PA_MEM_TYPE_SHARED_MEMFD:
164 fd = memfd_create("pulseaudio", MFD_ALLOW_SEALING);
165 break;
166 #endif
167 default:
168 goto fail;
169 }
170
171 if (fd < 0) {
172 pa_log("%s open() failed: %s", pa_mem_type_to_string(type), pa_cstrerror(errno));
173 goto fail;
174 }
175
176 m->type = type;
177 m->size = size + shm_marker_size(type);
178 m->do_unlink = do_unlink;
179
180 if (ftruncate(fd, (off_t) m->size) < 0) {
181 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
182 goto fail;
183 }
184
185 #ifndef MAP_NORESERVE
186 #define MAP_NORESERVE 0
187 #endif
188
189 if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) {
190 pa_log("mmap() failed: %s", pa_cstrerror(errno));
191 goto fail;
192 }
193
194 if (type == PA_MEM_TYPE_SHARED_POSIX) {
195 /* We store our PID at the end of the shm block, so that we
196 * can check for dead shm segments later */
197 marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - shm_marker_size(type));
198 pa_atomic_store(&marker->pid, (int) getpid());
199 pa_atomic_store(&marker->marker, SHM_MARKER);
200 }
201
202 /* For memfds, we keep the fd open until we pass it
203 * to the other PA endpoint over unix domain socket. */
204 if (type != PA_MEM_TYPE_SHARED_MEMFD) {
205 pa_assert_se(pa_close(fd) == 0);
206 m->fd = -1;
207 }
208 #ifdef HAVE_MEMFD
209 else
210 m->fd = fd;
211 #endif
212
213 return 0;
214
215 fail:
216 if (fd >= 0) {
217 #ifdef HAVE_SHM_OPEN
218 if (type == PA_MEM_TYPE_SHARED_POSIX)
219 shm_unlink(fn);
220 #endif
221 pa_close(fd);
222 }
223 #endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */
224
225 return -1;
226 }
227
pa_shm_create_rw(pa_shm * m,pa_mem_type_t type,size_t size,mode_t mode)228 int pa_shm_create_rw(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) {
229 pa_assert(m);
230 pa_assert(size > 0);
231 pa_assert(size <= MAX_SHM_SIZE);
232 pa_assert(!(mode & ~0777));
233 pa_assert(mode >= 0600);
234
235 /* Round up to make it page aligned */
236 size = PA_PAGE_ALIGN(size);
237
238 if (type == PA_MEM_TYPE_PRIVATE)
239 return privatemem_create(m, size);
240
241 return sharedmem_create(m, type, size, mode);
242 }
243
privatemem_free(pa_shm * m)244 static void privatemem_free(pa_shm *m) {
245 pa_assert(m);
246 pa_assert(m->ptr);
247 pa_assert(m->size > 0);
248
249 #ifdef MAP_ANONYMOUS
250 if (munmap(m->ptr, m->size) < 0)
251 pa_log("munmap() failed: %s", pa_cstrerror(errno));
252 #elif defined(HAVE_POSIX_MEMALIGN)
253 free(m->ptr);
254 #else
255 pa_xfree(m->ptr);
256 #endif
257 }
258
pa_shm_free(pa_shm * m)259 void pa_shm_free(pa_shm *m) {
260 pa_assert(m);
261 pa_assert(m->ptr);
262 pa_assert(m->size > 0);
263
264 #ifdef MAP_FAILED
265 pa_assert(m->ptr != MAP_FAILED);
266 #endif
267
268 if (m->type == PA_MEM_TYPE_PRIVATE) {
269 privatemem_free(m);
270 goto finish;
271 }
272
273 #if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
274 if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
275 pa_log("munmap() failed: %s", pa_cstrerror(errno));
276
277 #ifdef HAVE_SHM_OPEN
278 if (m->type == PA_MEM_TYPE_SHARED_POSIX && m->do_unlink) {
279 char fn[32];
280
281 segment_name(fn, sizeof(fn), m->id);
282 if (shm_unlink(fn) < 0)
283 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
284 }
285 #endif
286 #ifdef HAVE_MEMFD
287 if (m->type == PA_MEM_TYPE_SHARED_MEMFD && m->fd != -1)
288 pa_assert_se(pa_close(m->fd) == 0);
289 #endif
290
291 #else
292 /* We shouldn't be here without shm or memfd support */
293 pa_log("remove pa_assert_not_reached call");
294 #endif
295
296 finish:
297 pa_zero(*m);
298 }
299
pa_shm_punch(pa_shm * m,size_t offset,size_t size)300 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
301 void *ptr;
302 size_t o;
303 const size_t page_size = pa_page_size();
304
305 pa_assert(m);
306 pa_assert(m->ptr);
307 pa_assert(m->size > 0);
308 pa_assert(offset+size <= m->size);
309
310 #ifdef MAP_FAILED
311 pa_assert(m->ptr != MAP_FAILED);
312 #endif
313
314 /* You're welcome to implement this as NOOP on systems that don't
315 * support it */
316
317 /* Align the pointer up to multiples of the page size */
318 ptr = (uint8_t*) m->ptr + offset;
319 o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
320
321 if (o > 0) {
322 size_t delta = page_size - o;
323 ptr = (uint8_t*) ptr + delta;
324 size -= delta;
325 }
326
327 /* Align the size down to multiples of page size */
328 size = (size / page_size) * page_size;
329
330 #ifdef MADV_REMOVE
331 if (madvise(ptr, size, MADV_REMOVE) >= 0)
332 return;
333 #endif
334
335 #ifdef MADV_FREE
336 if (madvise(ptr, size, MADV_FREE) >= 0)
337 return;
338 #endif
339
340 #ifdef MADV_DONTNEED
341 madvise(ptr, size, MADV_DONTNEED);
342 #elif defined(POSIX_MADV_DONTNEED)
343 posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
344 #endif
345 }
346
shm_attach(pa_shm * m,pa_mem_type_t type,unsigned id,int memfd_fd,bool writable,bool for_cleanup)347 static int shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable, bool for_cleanup) {
348 #if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
349 char fn[32];
350 int fd = -1;
351 int prot;
352 struct stat st;
353
354 pa_assert(m);
355
356 switch (type) {
357 #ifdef HAVE_SHM_OPEN
358 case PA_MEM_TYPE_SHARED_POSIX:
359 pa_assert(memfd_fd == -1);
360 segment_name(fn, sizeof(fn), id);
361 if ((fd = shm_open(fn, writable ? O_RDWR : O_RDONLY, 0)) < 0) {
362 if ((errno != EACCES && errno != ENOENT) || !for_cleanup)
363 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
364 goto fail;
365 }
366 break;
367 #endif
368 #ifdef HAVE_MEMFD
369 case PA_MEM_TYPE_SHARED_MEMFD:
370 pa_assert(memfd_fd != -1);
371 fd = memfd_fd;
372 break;
373 #endif
374 default:
375 goto fail;
376 }
377
378 if (fstat(fd, &st) < 0) {
379 pa_log("fstat() failed: %s", pa_cstrerror(errno));
380 goto fail;
381 }
382
383 if (st.st_size <= 0 ||
384 st.st_size > (off_t) MAX_SHM_SIZE + (off_t) shm_marker_size(type) ||
385 PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
386 pa_log("Invalid shared memory segment size");
387 goto fail;
388 }
389
390 prot = writable ? PROT_READ | PROT_WRITE : PROT_READ;
391 if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(st.st_size), prot, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
392 pa_log("mmap() failed: %s", pa_cstrerror(errno));
393 goto fail;
394 }
395
396 /* In case of attaching to memfd areas, _the caller_ maintains
397 * ownership of the passed fd and has the sole responsibility
398 * of closing it down.. For other types, we're the code path
399 * which created the fd in the first place and we're thus the
400 * ones responsible for closing it down */
401 if (type != PA_MEM_TYPE_SHARED_MEMFD)
402 pa_assert_se(pa_close(fd) == 0);
403
404 m->type = type;
405 m->id = id;
406 m->size = (size_t) st.st_size;
407 m->do_unlink = false;
408 m->fd = -1;
409
410 return 0;
411
412 fail:
413 /* In case of memfds, caller maintains fd ownership */
414 if (fd >= 0 && type != PA_MEM_TYPE_SHARED_MEMFD)
415 pa_close(fd);
416
417 #endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */
418
419 return -1;
420 }
421
422 /* Caller owns passed @memfd_fd and must close it down when appropriate. */
pa_shm_attach(pa_shm * m,pa_mem_type_t type,unsigned id,int memfd_fd,bool writable)423 int pa_shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable) {
424 return shm_attach(m, type, id, memfd_fd, writable, false);
425 }
426
pa_shm_cleanup(void)427 int pa_shm_cleanup(void) {
428
429 #ifdef HAVE_SHM_OPEN
430 #ifdef SHM_PATH
431 DIR *d;
432 struct dirent *de;
433
434 if (!(d = opendir(SHM_PATH))) {
435 pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
436 return -1;
437 }
438
439 while ((de = readdir(d))) {
440 pa_shm seg;
441 unsigned id;
442 pid_t pid;
443 char fn[128];
444 struct shm_marker *m;
445
446 #if defined(__sun)
447 if (strncmp(de->d_name, ".SHMDpulse-shm-", SHM_ID_LEN))
448 #else
449 if (strncmp(de->d_name, "pulse-shm-", SHM_ID_LEN))
450 #endif
451 continue;
452
453 if (pa_atou(de->d_name + SHM_ID_LEN, &id) < 0)
454 continue;
455
456 if (shm_attach(&seg, PA_MEM_TYPE_SHARED_POSIX, id, -1, false, true) < 0)
457 continue;
458
459 if (seg.size < shm_marker_size(seg.type)) {
460 pa_shm_free(&seg);
461 continue;
462 }
463
464 m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - shm_marker_size(seg.type));
465
466 if (pa_atomic_load(&m->marker) != SHM_MARKER) {
467 pa_shm_free(&seg);
468 continue;
469 }
470
471 if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
472 pa_shm_free(&seg);
473 continue;
474 }
475
476 if (kill(pid, 0) == 0 || errno != ESRCH) {
477 pa_shm_free(&seg);
478 continue;
479 }
480
481 pa_shm_free(&seg);
482
483 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
484 segment_name(fn, sizeof(fn), id);
485
486 if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
487 pa_log_warn("Failed to remove SHM segment %s: %s", fn, pa_cstrerror(errno));
488 }
489
490 closedir(d);
491 #endif /* SHM_PATH */
492 #endif /* HAVE_SHM_OPEN */
493
494 return 0;
495 }
496