platform_shared_memory_region_posix.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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 <fcntl.h>
  6. #include <sys/mman.h>
  7. #include "base/files/file.h"
  8. #include "base/files/file_util.h"
  9. #include "base/logging.h"
  10. #include "base/metrics/histogram_macros.h"
  11. #include "base/threading/thread_restrictions.h"
  12. #include "build/build_config.h"
  13. namespace base {
  14. namespace subtle {
  15. namespace {
  16. struct ScopedPathUnlinkerTraits {
  17. static const FilePath* InvalidValue() { return nullptr; }
  18. static void Free(const FilePath* path) {
  19. if (unlink(path->value().c_str()))
  20. PLOG(WARNING) << "unlink";
  21. }
  22. };
  23. // Unlinks the FilePath when the object is destroyed.
  24. using ScopedPathUnlinker =
  25. ScopedGeneric<const FilePath*, ScopedPathUnlinkerTraits>;
  26. #if !BUILDFLAG(IS_NACL)
  27. bool CheckFDAccessMode(int fd, int expected_mode) {
  28. int fd_status = fcntl(fd, F_GETFL);
  29. if (fd_status == -1) {
  30. // TODO(crbug.com/838365): convert to DLOG when bug fixed.
  31. PLOG(ERROR) << "fcntl(" << fd << ", F_GETFL) failed";
  32. return false;
  33. }
  34. int mode = fd_status & O_ACCMODE;
  35. if (mode != expected_mode) {
  36. // TODO(crbug.com/838365): convert to DLOG when bug fixed.
  37. LOG(ERROR) << "Descriptor access mode (" << mode
  38. << ") differs from expected (" << expected_mode << ")";
  39. return false;
  40. }
  41. return true;
  42. }
  43. #endif // !BUILDFLAG(IS_NACL)
  44. } // namespace
  45. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  46. // static
  47. ScopedFD PlatformSharedMemoryRegion::ExecutableRegion::CreateFD(size_t size) {
  48. PlatformSharedMemoryRegion region =
  49. Create(Mode::kUnsafe, size, true /* executable */);
  50. if (region.IsValid())
  51. return region.PassPlatformHandle().fd;
  52. return ScopedFD();
  53. }
  54. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  55. // static
  56. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
  57. ScopedFDPair handle,
  58. Mode mode,
  59. size_t size,
  60. const UnguessableToken& guid) {
  61. if (!handle.fd.is_valid())
  62. return {};
  63. if (size == 0)
  64. return {};
  65. if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
  66. return {};
  67. CHECK(
  68. CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
  69. switch (mode) {
  70. case Mode::kReadOnly:
  71. case Mode::kUnsafe:
  72. if (handle.readonly_fd.is_valid()) {
  73. handle.readonly_fd.reset();
  74. DLOG(WARNING) << "Readonly handle shouldn't be valid for a "
  75. "non-writable memory region; closing";
  76. }
  77. break;
  78. case Mode::kWritable:
  79. if (!handle.readonly_fd.is_valid()) {
  80. DLOG(ERROR)
  81. << "Readonly handle must be valid for writable memory region";
  82. return {};
  83. }
  84. break;
  85. default:
  86. DLOG(ERROR) << "Invalid permission mode: " << static_cast<int>(mode);
  87. return {};
  88. }
  89. return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
  90. }
  91. // static
  92. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
  93. ScopedFD handle,
  94. Mode mode,
  95. size_t size,
  96. const UnguessableToken& guid) {
  97. CHECK_NE(mode, Mode::kWritable);
  98. return Take(ScopedFDPair(std::move(handle), ScopedFD()), mode, size, guid);
  99. }
  100. FDPair PlatformSharedMemoryRegion::GetPlatformHandle() const {
  101. return handle_.get();
  102. }
  103. bool PlatformSharedMemoryRegion::IsValid() const {
  104. return handle_.fd.is_valid() &&
  105. (mode_ == Mode::kWritable ? handle_.readonly_fd.is_valid() : true);
  106. }
  107. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
  108. if (!IsValid())
  109. return {};
  110. CHECK_NE(mode_, Mode::kWritable)
  111. << "Duplicating a writable shared memory region is prohibited";
  112. ScopedFD duped_fd(HANDLE_EINTR(dup(handle_.fd.get())));
  113. if (!duped_fd.is_valid()) {
  114. DPLOG(ERROR) << "dup(" << handle_.fd.get() << ") failed";
  115. return {};
  116. }
  117. return PlatformSharedMemoryRegion({std::move(duped_fd), ScopedFD()}, mode_,
  118. size_, guid_);
  119. }
  120. bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
  121. if (!IsValid())
  122. return false;
  123. CHECK_EQ(mode_, Mode::kWritable)
  124. << "Only writable shared memory region can be converted to read-only";
  125. handle_.fd.reset(handle_.readonly_fd.release());
  126. mode_ = Mode::kReadOnly;
  127. return true;
  128. }
  129. bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
  130. if (!IsValid())
  131. return false;
  132. CHECK_EQ(mode_, Mode::kWritable)
  133. << "Only writable shared memory region can be converted to unsafe";
  134. handle_.readonly_fd.reset();
  135. mode_ = Mode::kUnsafe;
  136. return true;
  137. }
  138. // static
  139. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
  140. size_t size
  141. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  142. ,
  143. bool executable
  144. #endif
  145. ) {
  146. #if BUILDFLAG(IS_NACL)
  147. // Untrusted code can't create descriptors or handles.
  148. return {};
  149. #else
  150. if (size == 0) {
  151. return {};
  152. }
  153. if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
  154. return {};
  155. }
  156. CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
  157. "lead to this region being non-modifiable";
  158. // This function theoretically can block on the disk, but realistically
  159. // the temporary files we create will just go into the buffer cache
  160. // and be deleted before they ever make it out to disk.
  161. ThreadRestrictions::ScopedAllowIO allow_io;
  162. // We don't use shm_open() API in order to support the --disable-dev-shm-usage
  163. // flag.
  164. FilePath directory;
  165. if (!GetShmemTempDir(
  166. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  167. executable,
  168. #else
  169. false /* executable */,
  170. #endif
  171. &directory)) {
  172. return {};
  173. }
  174. FilePath path;
  175. ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(directory, &path);
  176. File shm_file(fd.release());
  177. if (!shm_file.IsValid()) {
  178. PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
  179. FilePath dir = path.DirName();
  180. if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
  181. PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value();
  182. if (dir.value() == "/dev/shm") {
  183. LOG(FATAL) << "This is frequently caused by incorrect permissions on "
  184. << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
  185. }
  186. }
  187. return {};
  188. }
  189. // Deleting the file prevents anyone else from mapping it in (making it
  190. // private), and prevents the need for cleanup (once the last fd is
  191. // closed, it is truly freed).
  192. ScopedPathUnlinker path_unlinker(&path);
  193. ScopedFD readonly_fd;
  194. if (mode == Mode::kWritable) {
  195. // Also open as readonly so that we can ConvertToReadOnly().
  196. readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
  197. if (!readonly_fd.is_valid()) {
  198. DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
  199. return {};
  200. }
  201. }
  202. if (!AllocateFileRegion(&shm_file, 0, size)) {
  203. return {};
  204. }
  205. if (readonly_fd.is_valid()) {
  206. stat_wrapper_t shm_stat;
  207. if (File::Fstat(shm_file.GetPlatformFile(), &shm_stat) != 0) {
  208. DPLOG(ERROR) << "fstat(fd) failed";
  209. return {};
  210. }
  211. stat_wrapper_t readonly_stat;
  212. if (File::Fstat(readonly_fd.get(), &readonly_stat) != 0) {
  213. DPLOG(ERROR) << "fstat(readonly_fd) failed";
  214. return {};
  215. }
  216. if (shm_stat.st_dev != readonly_stat.st_dev ||
  217. shm_stat.st_ino != readonly_stat.st_ino) {
  218. LOG(ERROR) << "Writable and read-only inodes don't match; bailing";
  219. return {};
  220. }
  221. }
  222. return PlatformSharedMemoryRegion(
  223. {ScopedFD(shm_file.TakePlatformFile()), std::move(readonly_fd)}, mode,
  224. size, UnguessableToken::Create());
  225. #endif // !BUILDFLAG(IS_NACL)
  226. }
  227. bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
  228. PlatformSharedMemoryHandle handle,
  229. Mode mode,
  230. size_t size) {
  231. #if !BUILDFLAG(IS_NACL)
  232. if (!CheckFDAccessMode(handle.fd,
  233. mode == Mode::kReadOnly ? O_RDONLY : O_RDWR)) {
  234. return false;
  235. }
  236. if (mode == Mode::kWritable)
  237. return CheckFDAccessMode(handle.readonly_fd, O_RDONLY);
  238. // The second descriptor must be invalid in kReadOnly and kUnsafe modes.
  239. if (handle.readonly_fd != -1) {
  240. // TODO(crbug.com/838365): convert to DLOG when bug fixed.
  241. LOG(ERROR) << "The second descriptor must be invalid";
  242. return false;
  243. }
  244. return true;
  245. #else
  246. // fcntl(_, F_GETFL) is not implemented on NaCl.
  247. // We also cannot try to mmap() a region as writable and look at the return
  248. // status because the plugin process crashes if system mmap() fails.
  249. return true;
  250. #endif // !BUILDFLAG(IS_NACL)
  251. }
  252. PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
  253. ScopedFDPair handle,
  254. Mode mode,
  255. size_t size,
  256. const UnguessableToken& guid)
  257. : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
  258. } // namespace subtle
  259. } // namespace base