summaryrefslogtreecommitdiffstats
path: root/ipc/chromium/src/base/shared_memory_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/chromium/src/base/shared_memory_win.cc')
-rw-r--r--ipc/chromium/src/base/shared_memory_win.cc266
1 files changed, 266 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/shared_memory_win.cc b/ipc/chromium/src/base/shared_memory_win.cc
new file mode 100644
index 0000000000..8269912156
--- /dev/null
+++ b/ipc/chromium/src/base/shared_memory_win.cc
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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/shared_memory.h"
+
+#include "base/logging.h"
+#include "base/win_util.h"
+#include "base/string_util.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/RandomNum.h"
+#include "nsDebug.h"
+#include "nsString.h"
+#ifdef MOZ_MEMORY
+# include "mozmemory_utils.h"
+#endif
+
+namespace {
+// NtQuerySection is an internal (but believed to be stable) API and the
+// structures it uses are defined in nt_internals.h.
+// So we have to define them ourselves.
+typedef enum _SECTION_INFORMATION_CLASS {
+ SectionBasicInformation,
+} SECTION_INFORMATION_CLASS;
+
+typedef struct _SECTION_BASIC_INFORMATION {
+ PVOID BaseAddress;
+ ULONG Attributes;
+ LARGE_INTEGER Size;
+} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
+
+typedef ULONG(__stdcall* NtQuerySectionType)(
+ HANDLE SectionHandle, SECTION_INFORMATION_CLASS SectionInformationClass,
+ PVOID SectionInformation, ULONG SectionInformationLength,
+ PULONG ResultLength);
+
+// Checks if the section object is safe to map. At the moment this just means
+// it's not an image section.
+bool IsSectionSafeToMap(HANDLE handle) {
+ static NtQuerySectionType nt_query_section_func =
+ reinterpret_cast<NtQuerySectionType>(
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
+ DCHECK(nt_query_section_func);
+
+ // The handle must have SECTION_QUERY access for this to succeed.
+ SECTION_BASIC_INFORMATION basic_information = {};
+ ULONG status =
+ nt_query_section_func(handle, SectionBasicInformation, &basic_information,
+ sizeof(basic_information), nullptr);
+ if (status) {
+ return false;
+ }
+
+ return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
+}
+
+} // namespace
+
+namespace base {
+
+void SharedMemory::MappingDeleter::operator()(void* ptr) {
+ UnmapViewOfFile(ptr);
+}
+
+SharedMemory::~SharedMemory() = default;
+
+bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
+ DCHECK(!mapped_file_);
+
+ external_section_ = true;
+ freezeable_ = false; // just in case
+ mapped_file_ = std::move(handle);
+ read_only_ = read_only;
+ return true;
+}
+
+// static
+bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
+ return handle != nullptr;
+}
+
+// static
+SharedMemoryHandle SharedMemory::NULLHandle() { return nullptr; }
+
+// Wrapper around CreateFileMappingW for pagefile-backed regions. When out of
+// memory, may attempt to stall and retry rather than returning immediately, in
+// hopes that the page file is about to be expanded by Windows. (bug 1822383,
+// bug 1716727)
+//
+// This method is largely a copy of the MozVirtualAlloc method from
+// mozjemalloc.cpp, which implements this strategy for VirtualAlloc calls,
+// except re-purposed to handle CreateFileMapping.
+static HANDLE MozCreateFileMappingW(
+ LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect,
+ DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName) {
+#ifdef MOZ_MEMORY
+ constexpr auto IsOOMError = [] {
+ return ::GetLastError() == ERROR_COMMITMENT_LIMIT;
+ };
+
+ {
+ HANDLE handle = ::CreateFileMappingW(
+ INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect,
+ dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
+ if (MOZ_LIKELY(handle)) {
+ MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE,
+ "::CreateFileMapping should return NULL, not "
+ "INVALID_HANDLE_VALUE, on failure");
+ return handle;
+ }
+
+ // We can't do anything for errors other than OOM.
+ if (!IsOOMError()) {
+ return nullptr;
+ }
+ }
+
+ // Retry as many times as desired (possibly zero).
+ const mozilla::StallSpecs stallSpecs = mozilla::GetAllocatorStallSpecs();
+
+ const auto ret =
+ stallSpecs.StallAndRetry(&::Sleep, [&]() -> std::optional<HANDLE> {
+ HANDLE handle = ::CreateFileMappingW(
+ INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect,
+ dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
+
+ if (handle) {
+ MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE,
+ "::CreateFileMapping should return NULL, not "
+ "INVALID_HANDLE_VALUE, on failure");
+ return handle;
+ }
+
+ // Failure for some reason other than OOM.
+ if (!IsOOMError()) {
+ return nullptr;
+ }
+
+ return std::nullopt;
+ });
+
+ return ret.value_or(nullptr);
+#else
+ return ::CreateFileMappingW(INVALID_HANDLE_VALUE, lpFileMappingAttributes,
+ flProtect, dwMaximumSizeHigh, dwMaximumSizeLow,
+ lpName);
+#endif
+}
+
+bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
+ DCHECK(!mapped_file_);
+ read_only_ = false;
+
+ // If the shared memory object has no DACL, any process can
+ // duplicate its handles with any access rights; e.g., re-add write
+ // access to a read-only handle. To prevent that, we give it an
+ // empty DACL, so that no process can do that.
+ SECURITY_ATTRIBUTES sa, *psa = nullptr;
+ SECURITY_DESCRIPTOR sd;
+ ACL dacl;
+
+ if (freezeable) {
+ psa = &sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = &sd;
+ sa.bInheritHandle = FALSE;
+
+ if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) ||
+ NS_WARN_IF(
+ !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) ||
+ NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) {
+ return false;
+ }
+ }
+
+ mapped_file_.reset(MozCreateFileMappingW(psa, PAGE_READWRITE, 0,
+ static_cast<DWORD>(size), nullptr));
+ if (!mapped_file_) return false;
+
+ max_size_ = size;
+ freezeable_ = freezeable;
+ return true;
+}
+
+bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
+ DCHECK(!read_only_);
+ CHECK(freezeable_);
+
+ if (ro_out == this) {
+ DCHECK(!memory_);
+ }
+
+ HANDLE ro_handle;
+ if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_.release(),
+ GetCurrentProcess(), &ro_handle,
+ GENERIC_READ | FILE_MAP_READ, false,
+ DUPLICATE_CLOSE_SOURCE)) {
+ // DUPLICATE_CLOSE_SOURCE applies even if there is an error.
+ return false;
+ }
+
+ freezeable_ = false;
+
+ ro_out->Close();
+ ro_out->mapped_file_.reset(ro_handle);
+ ro_out->max_size_ = max_size_;
+ ro_out->read_only_ = true;
+ ro_out->freezeable_ = false;
+ ro_out->external_section_ = external_section_;
+
+ return true;
+}
+
+bool SharedMemory::Map(size_t bytes, void* fixed_address) {
+ if (!mapped_file_) {
+ return false;
+ }
+
+ if (external_section_ && !IsSectionSafeToMap(mapped_file_.get())) {
+ return false;
+ }
+
+ void* mem = MapViewOfFileEx(
+ mapped_file_.get(),
+ read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, bytes,
+ fixed_address);
+ if (mem) {
+ MOZ_ASSERT(!fixed_address || mem == fixed_address,
+ "MapViewOfFileEx returned an expected address");
+ memory_.reset(mem);
+ return true;
+ }
+ return false;
+}
+
+void* SharedMemory::FindFreeAddressSpace(size_t size) {
+ void* memory = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
+ if (memory) {
+ VirtualFree(memory, 0, MEM_RELEASE);
+ }
+ return memory;
+}
+
+SharedMemoryHandle SharedMemory::CloneHandle() {
+ freezeable_ = false;
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ if (DuplicateHandle(GetCurrentProcess(), mapped_file_.get(),
+ GetCurrentProcess(), &handle, 0, false,
+ DUPLICATE_SAME_ACCESS)) {
+ return SharedMemoryHandle(handle);
+ }
+ NS_WARNING("DuplicateHandle Failed!");
+ return nullptr;
+}
+
+void SharedMemory::Close(bool unmap_view) {
+ if (unmap_view) {
+ Unmap();
+ }
+
+ mapped_file_ = nullptr;
+}
+
+} // namespace base