1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6
7 #include "base/shared_memory.h"
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sys/mman.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14
15 #ifdef ANDROID
16 # include "mozilla/Ashmem.h"
17 #endif
18
19 #ifdef OS_LINUX
20 # include "linux_memfd_defs.h"
21 #endif
22
23 #ifdef __FreeBSD__
24 # include <sys/capsicum.h>
25 #endif
26
27 #ifdef MOZ_VALGRIND
28 # include <valgrind/valgrind.h>
29 #endif
30
31 #include "base/eintr_wrapper.h"
32 #include "base/logging.h"
33 #include "base/string_util.h"
34 #include "mozilla/Atomics.h"
35 #include "mozilla/UniquePtrExtensions.h"
36 #include "prenv.h"
37 #include "GeckoProfiler.h"
38
39 namespace base {
40
operator ()(void * ptr)41 void SharedMemory::MappingDeleter::operator()(void* ptr) {
42 // Check that this isn't a default-constructed deleter. (`munmap`
43 // is specified to fail with `EINVAL` if the length is 0, so this
44 // assertion isn't load-bearing.)
45 DCHECK(mapped_size_ != 0);
46 munmap(ptr, mapped_size_);
47 // Guard against multiple calls of the same deleter, which shouldn't
48 // happen (but could, if `UniquePtr::reset` were used). Calling
49 // `munmap` with an incorrect non-zero length would be bad.
50 mapped_size_ = 0;
51 }
52
~SharedMemory()53 SharedMemory::~SharedMemory() {
54 // This is almost equal to the default destructor, except for the
55 // warning message about unfrozen freezable memory.
56 Close();
57 }
58
SetHandle(SharedMemoryHandle handle,bool read_only)59 bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
60 DCHECK(!mapped_file_);
61 #ifndef ANDROID
62 DCHECK(!frozen_file_);
63 #endif
64
65 freezeable_ = false;
66 mapped_file_.reset(handle.fd);
67 read_only_ = read_only;
68 // is_memfd_ only matters for freezing, which isn't possible
69 return true;
70 }
71
72 // static
IsHandleValid(const SharedMemoryHandle & handle)73 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
74 return handle.fd >= 0;
75 }
76
77 // static
NULLHandle()78 SharedMemoryHandle SharedMemory::NULLHandle() { return SharedMemoryHandle(); }
79
80 #ifdef ANDROID
81
82 // Android has its own shared memory API, ashmem. It doesn't support
83 // POSIX shm_open, and the memfd support (see below) also doesn't work
84 // because its SELinux policy prevents the procfs operations we'd use
85 // (see bug 1670277 for more details).
86
AppendPosixShmPrefix(std::string * str,pid_t pid)87 bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) {
88 return false;
89 }
90
CreateInternal(size_t size,bool freezeable)91 bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
92 read_only_ = false;
93
94 DCHECK(size > 0);
95 DCHECK(!mapped_file_);
96
97 int fd = mozilla::android::ashmem_create(nullptr, size);
98 if (fd < 0) {
99 CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
100 return false;
101 }
102
103 mapped_file_.reset(fd);
104 max_size_ = size;
105 freezeable_ = freezeable;
106 return true;
107 }
108
ReadOnlyCopy(SharedMemory * ro_out)109 bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
110 DCHECK(mapped_file_);
111 DCHECK(!read_only_);
112 CHECK(freezeable_);
113
114 if (ro_out == this) {
115 DCHECK(!memory_);
116 }
117
118 if (mozilla::android::ashmem_setProt(mapped_file_.get(), PROT_READ) != 0) {
119 CHROMIUM_LOG(WARNING) << "failed to set ashmem read-only: "
120 << strerror(errno);
121 return false;
122 }
123
124 mozilla::UniqueFileHandle ro_file = std::move(mapped_file_);
125
126 freezeable_ = false;
127 ro_out->Close();
128 ro_out->mapped_file_ = std::move(ro_file);
129 ro_out->max_size_ = max_size_;
130 ro_out->read_only_ = true;
131 ro_out->freezeable_ = false;
132
133 return true;
134 }
135
136 #else // not Android
137
138 // memfd_create is a nonstandard interface for creating anonymous
139 // shared memory accessible as a file descriptor but not tied to any
140 // filesystem. It first appeared in Linux 3.17, and was adopted by
141 // FreeBSD in version 13.
142
143 # if !defined(HAVE_MEMFD_CREATE) && defined(OS_LINUX) && \
144 defined(SYS_memfd_create)
145
146 // Older libc versions (e.g., glibc before 2.27) don't have the
147 // wrapper, but we can supply our own; see `linux_memfd_defs.h`.
148
memfd_create(const char * name,unsigned int flags)149 static int memfd_create(const char* name, unsigned int flags) {
150 return syscall(SYS_memfd_create, name, flags);
151 }
152
153 # define HAVE_MEMFD_CREATE 1
154 # endif
155
156 // memfd supports having "seals" applied to the file, to prevent
157 // various types of changes (which apply to all fds referencing the
158 // file). Unfortunately, we can't rely on F_SEAL_WRITE to implement
159 // Freeze(); see the comments in ReadOnlyCopy() below.
160 //
161 // Instead, to prevent a child process from regaining write access to
162 // a read-only copy, the OS must also provide a way to remove write
163 // permissions at the file descriptor level. This next section
164 // attempts to accomplish that.
165
166 # ifdef HAVE_MEMFD_CREATE
167 # ifdef XP_LINUX
168 # define USE_MEMFD_CREATE 1
169
170 // To create a read-only duplicate of an fd, we can use procfs; the
171 // same operation could restore write access, but sandboxing prevents
172 // child processes from accessing /proc.
173 //
174 // (Note: if this ever changes to not use /proc, also reconsider how
175 // and if HaveMemfd should check whether this works.)
176
DupReadOnly(int fd)177 static int DupReadOnly(int fd) {
178 std::string path = StringPrintf("/proc/self/fd/%d", fd);
179 // procfs opens probably won't EINTR, but checking for it can't hurt
180 return HANDLE_EINTR(open(path.c_str(), O_RDONLY | O_CLOEXEC));
181 }
182
183 # elif defined(__FreeBSD__)
184 # define USE_MEMFD_CREATE 1
185
186 // FreeBSD's Capsicum framework allows irrevocably restricting the
187 // operations permitted on a file descriptor.
188
DupReadOnly(int fd)189 static int DupReadOnly(int fd) {
190 int rofd = dup(fd);
191 if (rofd < 0) {
192 return -1;
193 }
194
195 cap_rights_t rights;
196 cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R);
197 if (cap_rights_limit(rofd, &rights) < 0) {
198 int err = errno;
199 close(rofd);
200 errno = err;
201 return -1;
202 }
203
204 return rofd;
205 }
206
207 # else // unhandled OS
208 # warning "OS has memfd_create but no DupReadOnly implementation"
209 # endif // OS selection
210 # endif // HAVE_MEMFD_CREATE
211
212 // Runtime detection for memfd support.
HaveMemfd()213 static bool HaveMemfd() {
214 # ifdef USE_MEMFD_CREATE
215 static const bool kHave = [] {
216 mozilla::UniqueFileHandle fd(
217 memfd_create("mozilla-ipc-test", MFD_CLOEXEC | MFD_ALLOW_SEALING));
218 if (!fd) {
219 DCHECK_EQ(errno, ENOSYS);
220 return false;
221 }
222
223 // Verify that DupReadOnly works; on Linux it's known to fail if:
224 //
225 // * SELinux assigns the memfd a type for which this process's
226 // domain doesn't have "open" permission; this is always the
227 // case on Android but could occur on desktop as well
228 //
229 // * /proc (used by the DupReadOnly implementation) isn't mounted,
230 // which is a configuration that the Tor Browser project is
231 // interested in as a way to reduce fingerprinting risk
232 //
233 // Sandboxed processes on Linux also can't use it if sandboxing
234 // has already been started, but that's expected. It should be
235 // safe for sandboxed child processes to use memfd even if an
236 // unsandboxed process couldn't freeze them, because freezing
237 // isn't allowed (or meaningful) for memory created by another
238 // process.
239
240 if (!PR_GetEnv("MOZ_SANDBOXED")) {
241 mozilla::UniqueFileHandle rofd(DupReadOnly(fd.get()));
242 if (!rofd) {
243 CHROMIUM_LOG(WARNING) << "read-only dup failed (" << strerror(errno)
244 << "); not using memfd";
245 return false;
246 }
247 }
248 return true;
249 }();
250 return kHave;
251 # else
252 return false;
253 # endif // USE_MEMFD_CREATE
254 }
255
256 // static
AppendPosixShmPrefix(std::string * str,pid_t pid)257 bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) {
258 if (HaveMemfd()) {
259 return false;
260 }
261 *str += '/';
262 # ifdef OS_LINUX
263 // The Snap package environment doesn't provide a private /dev/shm
264 // (it's used for communication with services like PulseAudio);
265 // instead AppArmor is used to restrict access to it. Anything with
266 // this prefix is allowed:
267 static const char* const kSnap = [] {
268 auto instanceName = PR_GetEnv("SNAP_INSTANCE_NAME");
269 if (instanceName != nullptr) {
270 return instanceName;
271 }
272 // Compatibility for snapd <= 2.35:
273 return PR_GetEnv("SNAP_NAME");
274 }();
275
276 if (kSnap) {
277 StringAppendF(str, "snap.%s.", kSnap);
278 }
279 # endif // OS_LINUX
280 // Hopefully the "implementation defined" name length limit is long
281 // enough for this.
282 StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid));
283 return true;
284 }
285
CreateInternal(size_t size,bool freezeable)286 bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
287 read_only_ = false;
288
289 DCHECK(size > 0);
290 DCHECK(!mapped_file_);
291 DCHECK(!frozen_file_);
292
293 mozilla::UniqueFileHandle fd;
294 mozilla::UniqueFileHandle frozen_fd;
295 bool is_memfd = false;
296
297 # ifdef USE_MEMFD_CREATE
298 if (HaveMemfd()) {
299 const unsigned flags = MFD_CLOEXEC | (freezeable ? MFD_ALLOW_SEALING : 0);
300 fd.reset(memfd_create("mozilla-ipc", flags));
301 if (!fd) {
302 // In general it's too late to fall back here -- in a sandboxed
303 // child process, shm_open is already blocked. And it shouldn't
304 // be necessary.
305 CHROMIUM_LOG(WARNING) << "failed to create memfd: " << strerror(errno);
306 return false;
307 }
308 is_memfd = true;
309 if (freezeable) {
310 frozen_fd.reset(DupReadOnly(fd.get()));
311 if (!frozen_fd) {
312 CHROMIUM_LOG(WARNING)
313 << "failed to create read-only memfd: " << strerror(errno);
314 return false;
315 }
316 }
317 }
318 # endif
319
320 if (!fd) {
321 // Generic Unix: shm_open + shm_unlink
322 do {
323 // The names don't need to be unique, but it saves time if they
324 // usually are.
325 static mozilla::Atomic<size_t> sNameCounter;
326 std::string name;
327 CHECK(AppendPosixShmPrefix(&name, getpid()));
328 StringAppendF(&name, "%zu", sNameCounter++);
329 // O_EXCL means the names being predictable shouldn't be a problem.
330 fd.reset(HANDLE_EINTR(
331 shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600)));
332 if (fd) {
333 if (freezeable) {
334 frozen_fd.reset(HANDLE_EINTR(shm_open(name.c_str(), O_RDONLY, 0400)));
335 if (!frozen_fd) {
336 int open_err = errno;
337 shm_unlink(name.c_str());
338 DLOG(FATAL) << "failed to re-open freezeable shm: "
339 << strerror(open_err);
340 return false;
341 }
342 }
343 if (shm_unlink(name.c_str()) != 0) {
344 // This shouldn't happen, but if it does: assume the file is
345 // in fact leaked, and bail out now while it's still 0-length.
346 DLOG(FATAL) << "failed to unlink shm: " << strerror(errno);
347 return false;
348 }
349 }
350 } while (!fd && errno == EEXIST);
351 }
352
353 if (!fd) {
354 CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
355 return false;
356 }
357
358 # if defined(HAVE_POSIX_FALLOCATE)
359 // Using posix_fallocate will ensure that there's actually space for this
360 // file. Otherwise we end up with a sparse file that can give SIGBUS if we
361 // run out of space while writing to it.
362 int rv;
363 {
364 // Avoid repeated interruptions of posix_fallocate by the profiler's
365 // SIGPROF sampling signal. Indicating "thread sleep" here means we'll
366 // get up to one interruption but not more. See bug 1658847 for more.
367 // This has to be scoped outside the HANDLE_RV_EINTR retry loop.
368 AUTO_PROFILER_THREAD_SLEEP;
369 rv =
370 HANDLE_RV_EINTR(posix_fallocate(fd.get(), 0, static_cast<off_t>(size)));
371 }
372 if (rv != 0) {
373 if (rv == EOPNOTSUPP || rv == EINVAL || rv == ENODEV) {
374 // Some filesystems have trouble with posix_fallocate. For now, we must
375 // fallback ftruncate and accept the allocation failures like we do
376 // without posix_fallocate.
377 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1618914
378 int fallocate_errno = rv;
379 rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size)));
380 if (rv != 0) {
381 CHROMIUM_LOG(WARNING) << "fallocate failed to set shm size: "
382 << strerror(fallocate_errno);
383 CHROMIUM_LOG(WARNING)
384 << "ftruncate failed to set shm size: " << strerror(errno);
385 return false;
386 }
387 } else {
388 CHROMIUM_LOG(WARNING)
389 << "fallocate failed to set shm size: " << strerror(rv);
390 return false;
391 }
392 }
393 # else
394 int rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size)));
395 if (rv != 0) {
396 CHROMIUM_LOG(WARNING) << "ftruncate failed to set shm size: "
397 << strerror(errno);
398 return false;
399 }
400 # endif
401
402 mapped_file_ = std::move(fd);
403 frozen_file_ = std::move(frozen_fd);
404 max_size_ = size;
405 freezeable_ = freezeable;
406 is_memfd_ = is_memfd;
407 return true;
408 }
409
ReadOnlyCopy(SharedMemory * ro_out)410 bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
411 DCHECK(mapped_file_);
412 DCHECK(!read_only_);
413 CHECK(freezeable_);
414
415 if (ro_out == this) {
416 DCHECK(!memory_);
417 }
418
419 # ifdef USE_MEMFD_CREATE
420 # ifdef MOZ_VALGRIND
421 // Valgrind allows memfd_create but doesn't understand F_ADD_SEALS.
422 static const bool haveSeals = RUNNING_ON_VALGRIND == 0;
423 # else
424 static const bool haveSeals = true;
425 # endif
426 static const bool useSeals = !PR_GetEnv("MOZ_SHM_NO_SEALS");
427 if (is_memfd_ && haveSeals && useSeals) {
428 // Seals are added to the file as defense-in-depth. The primary
429 // method of access control is creating a read-only fd (using
430 // procfs in this case) and requiring that sandboxes processes not
431 // have access to /proc/self/fd to regain write permission; this
432 // is the same as with shm_open.
433 //
434 // Unfortunately, F_SEAL_WRITE is unreliable: if the process
435 // forked while there was a writeable mapping, it will inherit a
436 // copy of the mapping, which causes the seal to fail.
437 //
438 // (Also, in the future we may want to split this into separate
439 // classes for mappings and shared memory handles, which would
440 // complicate identifying the case where `F_SEAL_WRITE` would be
441 // possible even in the absence of races with fork.)
442 //
443 // However, Linux 5.1 added F_SEAL_FUTURE_WRITE, which prevents
444 // write operations afterwards, but existing writeable mappings
445 // are unaffected (similar to ashmem protection semantics).
446
447 const int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL;
448 int sealError = EINVAL;
449
450 # ifdef F_SEAL_FUTURE_WRITE
451 sealError =
452 fcntl(mapped_file_.get(), F_ADD_SEALS, seals | F_SEAL_FUTURE_WRITE) == 0
453 ? 0
454 : errno;
455 # endif // F_SEAL_FUTURE_WRITE
456 if (sealError == EINVAL) {
457 sealError =
458 fcntl(mapped_file_.get(), F_ADD_SEALS, seals) == 0 ? 0 : errno;
459 }
460 if (sealError != 0) {
461 CHROMIUM_LOG(WARNING) << "failed to seal memfd: " << strerror(errno);
462 return false;
463 }
464 }
465 # else // !USE_MEMFD_CREATE
466 DCHECK(!is_memfd_);
467 # endif
468
469 DCHECK(frozen_file_);
470 DCHECK(mapped_file_);
471 mapped_file_ = nullptr;
472 mozilla::UniqueFileHandle ro_file = std::move(frozen_file_);
473
474 DCHECK(ro_file);
475 freezeable_ = false;
476 ro_out->Close();
477 ro_out->mapped_file_ = std::move(ro_file);
478 ro_out->max_size_ = max_size_;
479 ro_out->read_only_ = true;
480 ro_out->freezeable_ = false;
481
482 return true;
483 }
484
485 #endif // not Android
486
Map(size_t bytes,void * fixed_address)487 bool SharedMemory::Map(size_t bytes, void* fixed_address) {
488 if (!mapped_file_) {
489 return false;
490 }
491 DCHECK(!memory_);
492
493 // Don't use MAP_FIXED when a fixed_address was specified, since that can
494 // replace pages that are alread mapped at that address.
495 void* mem =
496 mmap(fixed_address, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
497 MAP_SHARED, mapped_file_.get(), 0);
498
499 if (mem == MAP_FAILED) {
500 CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno);
501 return false;
502 }
503
504 if (fixed_address && mem != fixed_address) {
505 bool munmap_succeeded = munmap(mem, bytes) == 0;
506 DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno;
507 return false;
508 }
509
510 memory_ = UniqueMapping(mem, MappingDeleter(bytes));
511 return true;
512 }
513
FindFreeAddressSpace(size_t size)514 void* SharedMemory::FindFreeAddressSpace(size_t size) {
515 void* memory =
516 mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
517 munmap(memory, size);
518 return memory != MAP_FAILED ? memory : NULL;
519 }
520
ShareToProcessCommon(ProcessId processId,SharedMemoryHandle * new_handle,bool close_self)521 bool SharedMemory::ShareToProcessCommon(ProcessId processId,
522 SharedMemoryHandle* new_handle,
523 bool close_self) {
524 freezeable_ = false;
525 const int new_fd = dup(mapped_file_.get());
526 if (new_fd < 0) {
527 CHROMIUM_LOG(WARNING) << "failed to duplicate file descriptor: "
528 << strerror(errno);
529 return false;
530 }
531 new_handle->fd = new_fd;
532 new_handle->auto_close = true;
533
534 if (close_self) Close();
535
536 return true;
537 }
538
Close(bool unmap_view)539 void SharedMemory::Close(bool unmap_view) {
540 if (unmap_view) {
541 Unmap();
542 }
543
544 mapped_file_ = nullptr;
545 #ifndef ANDROID
546 if (frozen_file_) {
547 CHROMIUM_LOG(WARNING) << "freezeable shared memory was never frozen";
548 frozen_file_ = nullptr;
549 }
550 #endif
551 }
552
553 } // namespace base
554