123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/memory/platform_shared_memory_region.h"
- #include <fcntl.h>
- #include <sys/mman.h>
- #include "base/files/file.h"
- #include "base/files/file_util.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/threading/thread_restrictions.h"
- #include "build/build_config.h"
- namespace base {
- namespace subtle {
- namespace {
- struct ScopedPathUnlinkerTraits {
- static const FilePath* InvalidValue() { return nullptr; }
- static void Free(const FilePath* path) {
- if (unlink(path->value().c_str()))
- PLOG(WARNING) << "unlink";
- }
- };
- // Unlinks the FilePath when the object is destroyed.
- using ScopedPathUnlinker =
- ScopedGeneric<const FilePath*, ScopedPathUnlinkerTraits>;
- #if !BUILDFLAG(IS_NACL)
- bool CheckFDAccessMode(int fd, int expected_mode) {
- int fd_status = fcntl(fd, F_GETFL);
- if (fd_status == -1) {
- // TODO(crbug.com/838365): convert to DLOG when bug fixed.
- PLOG(ERROR) << "fcntl(" << fd << ", F_GETFL) failed";
- return false;
- }
- int mode = fd_status & O_ACCMODE;
- if (mode != expected_mode) {
- // TODO(crbug.com/838365): convert to DLOG when bug fixed.
- LOG(ERROR) << "Descriptor access mode (" << mode
- << ") differs from expected (" << expected_mode << ")";
- return false;
- }
- return true;
- }
- #endif // !BUILDFLAG(IS_NACL)
- } // namespace
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- // static
- ScopedFD PlatformSharedMemoryRegion::ExecutableRegion::CreateFD(size_t size) {
- PlatformSharedMemoryRegion region =
- Create(Mode::kUnsafe, size, true /* executable */);
- if (region.IsValid())
- return region.PassPlatformHandle().fd;
- return ScopedFD();
- }
- #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- // static
- PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
- ScopedFDPair handle,
- Mode mode,
- size_t size,
- const UnguessableToken& guid) {
- if (!handle.fd.is_valid())
- return {};
- if (size == 0)
- return {};
- if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
- return {};
- CHECK(
- CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
- switch (mode) {
- case Mode::kReadOnly:
- case Mode::kUnsafe:
- if (handle.readonly_fd.is_valid()) {
- handle.readonly_fd.reset();
- DLOG(WARNING) << "Readonly handle shouldn't be valid for a "
- "non-writable memory region; closing";
- }
- break;
- case Mode::kWritable:
- if (!handle.readonly_fd.is_valid()) {
- DLOG(ERROR)
- << "Readonly handle must be valid for writable memory region";
- return {};
- }
- break;
- default:
- DLOG(ERROR) << "Invalid permission mode: " << static_cast<int>(mode);
- return {};
- }
- return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
- }
- // static
- PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
- ScopedFD handle,
- Mode mode,
- size_t size,
- const UnguessableToken& guid) {
- CHECK_NE(mode, Mode::kWritable);
- return Take(ScopedFDPair(std::move(handle), ScopedFD()), mode, size, guid);
- }
- FDPair PlatformSharedMemoryRegion::GetPlatformHandle() const {
- return handle_.get();
- }
- bool PlatformSharedMemoryRegion::IsValid() const {
- return handle_.fd.is_valid() &&
- (mode_ == Mode::kWritable ? handle_.readonly_fd.is_valid() : true);
- }
- PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
- if (!IsValid())
- return {};
- CHECK_NE(mode_, Mode::kWritable)
- << "Duplicating a writable shared memory region is prohibited";
- ScopedFD duped_fd(HANDLE_EINTR(dup(handle_.fd.get())));
- if (!duped_fd.is_valid()) {
- DPLOG(ERROR) << "dup(" << handle_.fd.get() << ") failed";
- return {};
- }
- return PlatformSharedMemoryRegion({std::move(duped_fd), ScopedFD()}, mode_,
- size_, guid_);
- }
- bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
- if (!IsValid())
- return false;
- CHECK_EQ(mode_, Mode::kWritable)
- << "Only writable shared memory region can be converted to read-only";
- handle_.fd.reset(handle_.readonly_fd.release());
- mode_ = Mode::kReadOnly;
- return true;
- }
- bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
- if (!IsValid())
- return false;
- CHECK_EQ(mode_, Mode::kWritable)
- << "Only writable shared memory region can be converted to unsafe";
- handle_.readonly_fd.reset();
- mode_ = Mode::kUnsafe;
- return true;
- }
- // static
- PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
- size_t size
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- ,
- bool executable
- #endif
- ) {
- #if BUILDFLAG(IS_NACL)
- // Untrusted code can't create descriptors or handles.
- return {};
- #else
- if (size == 0) {
- return {};
- }
- if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
- return {};
- }
- CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
- "lead to this region being non-modifiable";
- // This function theoretically can block on the disk, but realistically
- // the temporary files we create will just go into the buffer cache
- // and be deleted before they ever make it out to disk.
- ThreadRestrictions::ScopedAllowIO allow_io;
- // We don't use shm_open() API in order to support the --disable-dev-shm-usage
- // flag.
- FilePath directory;
- if (!GetShmemTempDir(
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- executable,
- #else
- false /* executable */,
- #endif
- &directory)) {
- return {};
- }
- FilePath path;
- ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(directory, &path);
- File shm_file(fd.release());
- if (!shm_file.IsValid()) {
- PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
- FilePath dir = path.DirName();
- if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
- PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value();
- if (dir.value() == "/dev/shm") {
- LOG(FATAL) << "This is frequently caused by incorrect permissions on "
- << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
- }
- }
- return {};
- }
- // Deleting the file prevents anyone else from mapping it in (making it
- // private), and prevents the need for cleanup (once the last fd is
- // closed, it is truly freed).
- ScopedPathUnlinker path_unlinker(&path);
- ScopedFD readonly_fd;
- if (mode == Mode::kWritable) {
- // Also open as readonly so that we can ConvertToReadOnly().
- readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
- if (!readonly_fd.is_valid()) {
- DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
- return {};
- }
- }
- if (!AllocateFileRegion(&shm_file, 0, size)) {
- return {};
- }
- if (readonly_fd.is_valid()) {
- stat_wrapper_t shm_stat;
- if (File::Fstat(shm_file.GetPlatformFile(), &shm_stat) != 0) {
- DPLOG(ERROR) << "fstat(fd) failed";
- return {};
- }
- stat_wrapper_t readonly_stat;
- if (File::Fstat(readonly_fd.get(), &readonly_stat) != 0) {
- DPLOG(ERROR) << "fstat(readonly_fd) failed";
- return {};
- }
- if (shm_stat.st_dev != readonly_stat.st_dev ||
- shm_stat.st_ino != readonly_stat.st_ino) {
- LOG(ERROR) << "Writable and read-only inodes don't match; bailing";
- return {};
- }
- }
- return PlatformSharedMemoryRegion(
- {ScopedFD(shm_file.TakePlatformFile()), std::move(readonly_fd)}, mode,
- size, UnguessableToken::Create());
- #endif // !BUILDFLAG(IS_NACL)
- }
- bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
- PlatformSharedMemoryHandle handle,
- Mode mode,
- size_t size) {
- #if !BUILDFLAG(IS_NACL)
- if (!CheckFDAccessMode(handle.fd,
- mode == Mode::kReadOnly ? O_RDONLY : O_RDWR)) {
- return false;
- }
- if (mode == Mode::kWritable)
- return CheckFDAccessMode(handle.readonly_fd, O_RDONLY);
- // The second descriptor must be invalid in kReadOnly and kUnsafe modes.
- if (handle.readonly_fd != -1) {
- // TODO(crbug.com/838365): convert to DLOG when bug fixed.
- LOG(ERROR) << "The second descriptor must be invalid";
- return false;
- }
- return true;
- #else
- // fcntl(_, F_GETFL) is not implemented on NaCl.
- // We also cannot try to mmap() a region as writable and look at the return
- // status because the plugin process crashes if system mmap() fails.
- return true;
- #endif // !BUILDFLAG(IS_NACL)
- }
- PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
- ScopedFDPair handle,
- Mode mode,
- size_t size,
- const UnguessableToken& guid)
- : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
- } // namespace subtle
- } // namespace base
|