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