platform_shared_memory_region_android.cc 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/memory/platform_shared_memory_region.h"
  5. #include <sys/mman.h>
  6. #include "base/bits.h"
  7. #include "base/logging.h"
  8. #include "base/memory/page_size.h"
  9. #include "base/memory/shared_memory_tracker.h"
  10. #include "base/metrics/histogram_macros.h"
  11. #include "base/posix/eintr_wrapper.h"
  12. #include "third_party/ashmem/ashmem.h"
  13. namespace base {
  14. namespace subtle {
  15. // For Android, we use ashmem to implement SharedMemory. ashmem_create_region
  16. // will automatically pin the region. We never explicitly call pin/unpin. When
  17. // all the file descriptors from different processes associated with the region
  18. // are closed, the memory buffer will go away.
  19. namespace {
  20. int GetAshmemRegionProtectionMask(int fd) {
  21. int prot = ashmem_get_prot_region(fd);
  22. if (prot < 0) {
  23. // TODO(crbug.com/838365): convert to DLOG when bug fixed.
  24. PLOG(ERROR) << "ashmem_get_prot_region failed";
  25. return -1;
  26. }
  27. return prot;
  28. }
  29. } // namespace
  30. // static
  31. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
  32. ScopedFD fd,
  33. Mode mode,
  34. size_t size,
  35. const UnguessableToken& guid) {
  36. if (!fd.is_valid())
  37. return {};
  38. if (size == 0)
  39. return {};
  40. if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
  41. return {};
  42. CHECK(CheckPlatformHandlePermissionsCorrespondToMode(fd.get(), mode, size));
  43. return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid);
  44. }
  45. int PlatformSharedMemoryRegion::GetPlatformHandle() const {
  46. return handle_.get();
  47. }
  48. bool PlatformSharedMemoryRegion::IsValid() const {
  49. return handle_.is_valid();
  50. }
  51. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
  52. if (!IsValid())
  53. return {};
  54. CHECK_NE(mode_, Mode::kWritable)
  55. << "Duplicating a writable shared memory region is prohibited";
  56. ScopedFD duped_fd(HANDLE_EINTR(dup(handle_.get())));
  57. if (!duped_fd.is_valid()) {
  58. DPLOG(ERROR) << "dup(" << handle_.get() << ") failed";
  59. return {};
  60. }
  61. return PlatformSharedMemoryRegion(std::move(duped_fd), mode_, size_, guid_);
  62. }
  63. bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
  64. if (!IsValid())
  65. return false;
  66. CHECK_EQ(mode_, Mode::kWritable)
  67. << "Only writable shared memory region can be converted to read-only";
  68. ScopedFD handle_copy(handle_.release());
  69. int prot = GetAshmemRegionProtectionMask(handle_copy.get());
  70. if (prot < 0)
  71. return false;
  72. prot &= ~PROT_WRITE;
  73. int ret = ashmem_set_prot_region(handle_copy.get(), prot);
  74. if (ret != 0) {
  75. DPLOG(ERROR) << "ashmem_set_prot_region failed";
  76. return false;
  77. }
  78. handle_ = std::move(handle_copy);
  79. mode_ = Mode::kReadOnly;
  80. return true;
  81. }
  82. bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
  83. if (!IsValid())
  84. return false;
  85. CHECK_EQ(mode_, Mode::kWritable)
  86. << "Only writable shared memory region can be converted to unsafe";
  87. mode_ = Mode::kUnsafe;
  88. return true;
  89. }
  90. // static
  91. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
  92. size_t size) {
  93. if (size == 0) {
  94. return {};
  95. }
  96. // Align size as required by ashmem_create_region() API documentation. This
  97. // operation may overflow so check that the result doesn't decrease.
  98. size_t rounded_size = bits::AlignUp(size, GetPageSize());
  99. if (rounded_size < size ||
  100. rounded_size > static_cast<size_t>(std::numeric_limits<int>::max())) {
  101. return {};
  102. }
  103. CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
  104. "lead to this region being non-modifiable";
  105. UnguessableToken guid = UnguessableToken::Create();
  106. int fd = ashmem_create_region(
  107. SharedMemoryTracker::GetDumpNameForTracing(guid).c_str(), rounded_size);
  108. if (fd < 0) {
  109. DPLOG(ERROR) << "ashmem_create_region failed";
  110. return {};
  111. }
  112. ScopedFD scoped_fd(fd);
  113. int err = ashmem_set_prot_region(scoped_fd.get(), PROT_READ | PROT_WRITE);
  114. if (err < 0) {
  115. DPLOG(ERROR) << "ashmem_set_prot_region failed";
  116. return {};
  117. }
  118. return PlatformSharedMemoryRegion(std::move(scoped_fd), mode, size, guid);
  119. }
  120. bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
  121. PlatformSharedMemoryHandle handle,
  122. Mode mode,
  123. size_t size) {
  124. int prot = GetAshmemRegionProtectionMask(handle);
  125. if (prot < 0)
  126. return false;
  127. bool is_read_only = (prot & PROT_WRITE) == 0;
  128. bool expected_read_only = mode == Mode::kReadOnly;
  129. if (is_read_only != expected_read_only) {
  130. // TODO(crbug.com/838365): convert to DLOG when bug fixed.
  131. LOG(ERROR) << "Ashmem region has a wrong protection mask: it is"
  132. << (is_read_only ? " " : " not ") << "read-only but it should"
  133. << (expected_read_only ? " " : " not ") << "be";
  134. return false;
  135. }
  136. return true;
  137. }
  138. PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
  139. ScopedFD fd,
  140. Mode mode,
  141. size_t size,
  142. const UnguessableToken& guid)
  143. : handle_(std::move(fd)), mode_(mode), size_(size), guid_(guid) {}
  144. } // namespace subtle
  145. } // namespace base