123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- // 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 <sys/mman.h>
- #include "base/bits.h"
- #include "base/logging.h"
- #include "base/memory/page_size.h"
- #include "base/memory/shared_memory_tracker.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/posix/eintr_wrapper.h"
- #include "third_party/ashmem/ashmem.h"
- namespace base {
- namespace subtle {
- // For Android, we use ashmem to implement SharedMemory. ashmem_create_region
- // will automatically pin the region. We never explicitly call pin/unpin. When
- // all the file descriptors from different processes associated with the region
- // are closed, the memory buffer will go away.
- namespace {
- int GetAshmemRegionProtectionMask(int fd) {
- int prot = ashmem_get_prot_region(fd);
- if (prot < 0) {
- // TODO(crbug.com/838365): convert to DLOG when bug fixed.
- PLOG(ERROR) << "ashmem_get_prot_region failed";
- return -1;
- }
- return prot;
- }
- } // namespace
- // static
- PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
- ScopedFD fd,
- Mode mode,
- size_t size,
- const UnguessableToken& guid) {
- if (!fd.is_valid())
- return {};
- if (size == 0)
- return {};
- if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
- return {};
- CHECK(CheckPlatformHandlePermissionsCorrespondToMode(fd.get(), mode, size));
- return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid);
- }
- int PlatformSharedMemoryRegion::GetPlatformHandle() const {
- return handle_.get();
- }
- bool PlatformSharedMemoryRegion::IsValid() const {
- return handle_.is_valid();
- }
- 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_.get())));
- if (!duped_fd.is_valid()) {
- DPLOG(ERROR) << "dup(" << handle_.get() << ") failed";
- return {};
- }
- return PlatformSharedMemoryRegion(std::move(duped_fd), 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";
- ScopedFD handle_copy(handle_.release());
- int prot = GetAshmemRegionProtectionMask(handle_copy.get());
- if (prot < 0)
- return false;
- prot &= ~PROT_WRITE;
- int ret = ashmem_set_prot_region(handle_copy.get(), prot);
- if (ret != 0) {
- DPLOG(ERROR) << "ashmem_set_prot_region failed";
- return false;
- }
- handle_ = std::move(handle_copy);
- 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";
- mode_ = Mode::kUnsafe;
- return true;
- }
- // static
- PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
- size_t size) {
- if (size == 0) {
- return {};
- }
- // Align size as required by ashmem_create_region() API documentation. This
- // operation may overflow so check that the result doesn't decrease.
- size_t rounded_size = bits::AlignUp(size, GetPageSize());
- if (rounded_size < size ||
- rounded_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";
- UnguessableToken guid = UnguessableToken::Create();
- int fd = ashmem_create_region(
- SharedMemoryTracker::GetDumpNameForTracing(guid).c_str(), rounded_size);
- if (fd < 0) {
- DPLOG(ERROR) << "ashmem_create_region failed";
- return {};
- }
- ScopedFD scoped_fd(fd);
- int err = ashmem_set_prot_region(scoped_fd.get(), PROT_READ | PROT_WRITE);
- if (err < 0) {
- DPLOG(ERROR) << "ashmem_set_prot_region failed";
- return {};
- }
- return PlatformSharedMemoryRegion(std::move(scoped_fd), mode, size, guid);
- }
- bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
- PlatformSharedMemoryHandle handle,
- Mode mode,
- size_t size) {
- int prot = GetAshmemRegionProtectionMask(handle);
- if (prot < 0)
- return false;
- bool is_read_only = (prot & PROT_WRITE) == 0;
- bool expected_read_only = mode == Mode::kReadOnly;
- if (is_read_only != expected_read_only) {
- // TODO(crbug.com/838365): convert to DLOG when bug fixed.
- LOG(ERROR) << "Ashmem region has a wrong protection mask: it is"
- << (is_read_only ? " " : " not ") << "read-only but it should"
- << (expected_read_only ? " " : " not ") << "be";
- return false;
- }
- return true;
- }
- PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
- ScopedFD fd,
- Mode mode,
- size_t size,
- const UnguessableToken& guid)
- : handle_(std::move(fd)), mode_(mode), size_(size), guid_(guid) {}
- } // namespace subtle
- } // namespace base
|