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