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