platform_shared_memory_region_win.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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 <aclapi.h>
  6. #include <stddef.h>
  7. #include <stdint.h>
  8. #include "base/allocator/partition_allocator/page_allocator.h"
  9. #include "base/bits.h"
  10. #include "base/logging.h"
  11. #include "base/metrics/histogram_functions.h"
  12. #include "base/metrics/histogram_macros.h"
  13. #include "base/process/process_handle.h"
  14. #include "base/rand_util.h"
  15. #include "base/strings/string_util.h"
  16. #include "base/strings/stringprintf.h"
  17. #include "base/strings/utf_string_conversions.h"
  18. #include "base/win/windows_version.h"
  19. namespace base {
  20. namespace subtle {
  21. namespace {
  22. typedef enum _SECTION_INFORMATION_CLASS {
  23. SectionBasicInformation,
  24. } SECTION_INFORMATION_CLASS;
  25. typedef struct _SECTION_BASIC_INFORMATION {
  26. PVOID BaseAddress;
  27. ULONG Attributes;
  28. LARGE_INTEGER Size;
  29. } SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
  30. typedef ULONG(__stdcall* NtQuerySectionType)(
  31. HANDLE SectionHandle,
  32. SECTION_INFORMATION_CLASS SectionInformationClass,
  33. PVOID SectionInformation,
  34. ULONG SectionInformationLength,
  35. PULONG ResultLength);
  36. // Checks if the section object is safe to map. At the moment this just means
  37. // it's not an image section.
  38. bool IsSectionSafeToMap(HANDLE handle) {
  39. static NtQuerySectionType nt_query_section_func =
  40. reinterpret_cast<NtQuerySectionType>(
  41. ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
  42. DCHECK(nt_query_section_func);
  43. // The handle must have SECTION_QUERY access for this to succeed.
  44. SECTION_BASIC_INFORMATION basic_information = {};
  45. ULONG status =
  46. nt_query_section_func(handle, SectionBasicInformation, &basic_information,
  47. sizeof(basic_information), nullptr);
  48. if (status)
  49. return false;
  50. return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
  51. }
  52. // Returns a HANDLE on success and |nullptr| on failure.
  53. // This function is similar to CreateFileMapping, but removes the permissions
  54. // WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE.
  55. //
  56. // A newly created file mapping has two sets of permissions. It has access
  57. // control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and
  58. // file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). The Chrome sandbox
  59. // prevents HANDLEs with the WRITE_DAC permission from being duplicated into
  60. // unprivileged processes.
  61. //
  62. // In order to remove the access control permissions, after being created the
  63. // handle is duplicated with only the file access permissions.
  64. HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa,
  65. size_t rounded_size,
  66. LPCWSTR name) {
  67. HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0,
  68. static_cast<DWORD>(rounded_size), name);
  69. if (!h) {
  70. return nullptr;
  71. }
  72. HANDLE h2;
  73. ProcessHandle process = GetCurrentProcess();
  74. BOOL success = ::DuplicateHandle(
  75. process, h, process, &h2, FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY,
  76. FALSE, 0);
  77. BOOL rv = ::CloseHandle(h);
  78. DCHECK(rv);
  79. if (!success) {
  80. return nullptr;
  81. }
  82. return h2;
  83. }
  84. } // namespace
  85. // static
  86. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
  87. win::ScopedHandle handle,
  88. Mode mode,
  89. size_t size,
  90. const UnguessableToken& guid) {
  91. if (!handle.is_valid())
  92. return {};
  93. if (size == 0)
  94. return {};
  95. if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
  96. return {};
  97. if (!IsSectionSafeToMap(handle.get()))
  98. return {};
  99. CHECK(
  100. CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
  101. return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
  102. }
  103. HANDLE PlatformSharedMemoryRegion::GetPlatformHandle() const {
  104. return handle_.get();
  105. }
  106. bool PlatformSharedMemoryRegion::IsValid() const {
  107. return handle_.is_valid();
  108. }
  109. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
  110. if (!IsValid())
  111. return {};
  112. CHECK_NE(mode_, Mode::kWritable)
  113. << "Duplicating a writable shared memory region is prohibited";
  114. HANDLE duped_handle;
  115. ProcessHandle process = GetCurrentProcess();
  116. BOOL success =
  117. ::DuplicateHandle(process, handle_.get(), process, &duped_handle, 0,
  118. FALSE, DUPLICATE_SAME_ACCESS);
  119. if (!success)
  120. return {};
  121. return PlatformSharedMemoryRegion(win::ScopedHandle(duped_handle), mode_,
  122. size_, guid_);
  123. }
  124. bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
  125. if (!IsValid())
  126. return false;
  127. CHECK_EQ(mode_, Mode::kWritable)
  128. << "Only writable shared memory region can be converted to read-only";
  129. win::ScopedHandle handle_copy(handle_.release());
  130. HANDLE duped_handle;
  131. ProcessHandle process = GetCurrentProcess();
  132. BOOL success =
  133. ::DuplicateHandle(process, handle_copy.get(), process, &duped_handle,
  134. FILE_MAP_READ | SECTION_QUERY, FALSE, 0);
  135. if (!success)
  136. return false;
  137. handle_.Set(duped_handle);
  138. mode_ = Mode::kReadOnly;
  139. return true;
  140. }
  141. bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
  142. if (!IsValid())
  143. return false;
  144. CHECK_EQ(mode_, Mode::kWritable)
  145. << "Only writable shared memory region can be converted to unsafe";
  146. mode_ = Mode::kUnsafe;
  147. return true;
  148. }
  149. // static
  150. PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
  151. size_t size) {
  152. // TODO(crbug.com/210609): NaCl forces us to round up 64k here, wasting 32k
  153. // per mapping on average.
  154. static const size_t kSectionSize = 65536;
  155. if (size == 0) {
  156. return {};
  157. }
  158. // Aligning may overflow so check that the result doesn't decrease.
  159. size_t rounded_size = bits::AlignUp(size, kSectionSize);
  160. if (rounded_size < size ||
  161. rounded_size > static_cast<size_t>(std::numeric_limits<int>::max())) {
  162. return {};
  163. }
  164. CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
  165. "lead to this region being non-modifiable";
  166. // Add an empty DACL to enforce anonymous read-only sections.
  167. ACL dacl;
  168. SECURITY_DESCRIPTOR sd;
  169. if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
  170. return {};
  171. }
  172. if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
  173. return {};
  174. }
  175. if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) {
  176. return {};
  177. }
  178. std::u16string name;
  179. if (win::GetVersion() < win::Version::WIN8_1) {
  180. // Windows < 8.1 ignores DACLs on certain unnamed objects (like shared
  181. // sections). So, we generate a random name when we need to enforce
  182. // read-only.
  183. uint64_t rand_values[4];
  184. RandBytes(&rand_values, sizeof(rand_values));
  185. name = ASCIIToUTF16(StringPrintf("CrSharedMem_%016llx%016llx%016llx%016llx",
  186. rand_values[0], rand_values[1],
  187. rand_values[2], rand_values[3]));
  188. DCHECK(!name.empty());
  189. }
  190. SECURITY_ATTRIBUTES sa = {sizeof(sa), &sd, FALSE};
  191. // Ask for the file mapping with reduced permisions to avoid passing the
  192. // access control permissions granted by default into unpriviledged process.
  193. HANDLE h = CreateFileMappingWithReducedPermissions(
  194. &sa, rounded_size, name.empty() ? nullptr : as_wcstr(name));
  195. if (h == nullptr) {
  196. // The error is logged within CreateFileMappingWithReducedPermissions().
  197. return {};
  198. }
  199. win::ScopedHandle scoped_h(h);
  200. // Check if the shared memory pre-exists.
  201. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  202. return {};
  203. }
  204. return PlatformSharedMemoryRegion(std::move(scoped_h), mode, size,
  205. UnguessableToken::Create());
  206. }
  207. // static
  208. bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
  209. PlatformSharedMemoryHandle handle,
  210. Mode mode,
  211. size_t size) {
  212. // Call ::DuplicateHandle() with FILE_MAP_WRITE as a desired access to check
  213. // if the |handle| has a write access.
  214. ProcessHandle process = GetCurrentProcess();
  215. HANDLE duped_handle;
  216. BOOL success = ::DuplicateHandle(process, handle, process, &duped_handle,
  217. FILE_MAP_WRITE, FALSE, 0);
  218. if (success) {
  219. BOOL rv = ::CloseHandle(duped_handle);
  220. DCHECK(rv);
  221. }
  222. bool is_read_only = !success;
  223. bool expected_read_only = mode == Mode::kReadOnly;
  224. if (is_read_only != expected_read_only) {
  225. DLOG(ERROR) << "File mapping handle has wrong access rights: it is"
  226. << (is_read_only ? " " : " not ") << "read-only but it should"
  227. << (expected_read_only ? " " : " not ") << "be";
  228. return false;
  229. }
  230. return true;
  231. }
  232. PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
  233. win::ScopedHandle handle,
  234. Mode mode,
  235. size_t size,
  236. const UnguessableToken& guid)
  237. : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
  238. } // namespace subtle
  239. } // namespace base