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