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