From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- ipc/chromium/src/base/shared_memory_win.cc | 282 +++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 ipc/chromium/src/base/shared_memory_win.cc (limited to 'ipc/chromium/src/base/shared_memory_win.cc') 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..a22a68c7e2 --- /dev/null +++ b/ipc/chromium/src/base/shared_memory_win.cc @@ -0,0 +1,282 @@ +/* -*- 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 "mozilla/WindowsVersion.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( + ::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 = ::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; + nsAutoStringN name; + + 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; + } + + // Older versions of Windows will silently ignore the security + // attributes unless the object has a name. + if (!mozilla::IsWin8Point1OrLater()) { + name.AssignLiteral("MozSharedMem_"); + for (size_t i = 0; i < 4; ++i) { + mozilla::Maybe randomNum = mozilla::RandomUint64(); + if (NS_WARN_IF(randomNum.isNothing())) { + return false; + } + name.AppendPrintf("%016llx", *randomNum); + } + } + } + + mapped_file_.reset( + MozCreateFileMappingW(psa, PAGE_READWRITE, 0, static_cast(size), + name.IsEmpty() ? nullptr : name.get())); + 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 -- cgit v1.2.3