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