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