• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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