• 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 } 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