tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SharedMemoryPlatform_windows.cpp (8413B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* This source code was derived from Chromium code, and as such is also subject
      8 * to the [Chromium license](ipc/chromium/src/LICENSE). */
      9 
     10 #include "SharedMemoryPlatform.h"
     11 
     12 #include <windows.h>
     13 
     14 #include "nsDebug.h"
     15 #ifdef MOZ_MEMORY
     16 #  include "mozmemory_stall.h"
     17 #endif
     18 
     19 namespace {
     20 // NtQuerySection is an internal (but believed to be stable) API and the
     21 // structures it uses are defined in nt_internals.h.
     22 // So we have to define them ourselves.
     23 typedef enum _SECTION_INFORMATION_CLASS {
     24  SectionBasicInformation,
     25 } SECTION_INFORMATION_CLASS;
     26 
     27 typedef struct _SECTION_BASIC_INFORMATION {
     28  PVOID BaseAddress;
     29  ULONG Attributes;
     30  LARGE_INTEGER Size;
     31 } SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
     32 
     33 typedef ULONG(__stdcall* NtQuerySectionType)(
     34    HANDLE SectionHandle, SECTION_INFORMATION_CLASS SectionInformationClass,
     35    PVOID SectionInformation, ULONG SectionInformationLength,
     36    PULONG ResultLength);
     37 
     38 // Checks if the section object is safe to map. At the moment this just means
     39 // it's not an image section.
     40 bool IsSectionSafeToMap(HANDLE aHandle) {
     41  static NtQuerySectionType nt_query_section_func =
     42      reinterpret_cast<NtQuerySectionType>(
     43          ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
     44  MOZ_DIAGNOSTIC_ASSERT(nt_query_section_func,
     45                        "NtQuerySection function not found in ntdll.dll");
     46 
     47  // The handle must have SECTION_QUERY access for this to succeed.
     48  SECTION_BASIC_INFORMATION basic_information = {};
     49  ULONG status = nt_query_section_func(aHandle, SectionBasicInformation,
     50                                       &basic_information,
     51                                       sizeof(basic_information), nullptr);
     52  if (status) {
     53    return false;
     54  }
     55 
     56  return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
     57 }
     58 
     59 // Wrapper around CreateFileMappingW for pagefile-backed regions. When out of
     60 // memory, may attempt to stall and retry rather than returning immediately, in
     61 // hopes that the page file is about to be expanded by Windows. (bug 1822383,
     62 // bug 1716727)
     63 //
     64 // This method is largely a copy of the MozVirtualAlloc method from
     65 // mozjemalloc.cpp, which implements this strategy for VirtualAlloc calls,
     66 // except re-purposed to handle CreateFileMapping.
     67 HANDLE MozCreateFileMappingW(LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
     68                             DWORD flProtect, DWORD dwMaximumSizeHigh,
     69                             DWORD dwMaximumSizeLow, LPCWSTR lpName) {
     70 #ifdef MOZ_MEMORY
     71  constexpr auto IsOOMError = [] {
     72    return ::GetLastError() == ERROR_COMMITMENT_LIMIT;
     73  };
     74 
     75  {
     76    HANDLE handle = ::CreateFileMappingW(
     77        INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect,
     78        dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
     79    if (MOZ_LIKELY(handle)) {
     80      MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE,
     81                            "::CreateFileMapping should return NULL, not "
     82                            "INVALID_HANDLE_VALUE, on failure");
     83      return handle;
     84    }
     85 
     86    // We can't do anything for errors other than OOM.
     87    if (!IsOOMError()) {
     88      return nullptr;
     89    }
     90  }
     91 
     92  // Retry as many times as desired (possibly zero).
     93  const mozilla::StallSpecs stallSpecs = mozilla::GetAllocatorStallSpecs();
     94 
     95  const auto ret =
     96      stallSpecs.StallAndRetry(&::Sleep, [&]() -> std::optional<HANDLE> {
     97        HANDLE handle = ::CreateFileMappingW(
     98            INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect,
     99            dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
    100 
    101        if (handle) {
    102          MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE,
    103                                "::CreateFileMapping should return NULL, not "
    104                                "INVALID_HANDLE_VALUE, on failure");
    105          return handle;
    106        }
    107 
    108        // Failure for some reason other than OOM.
    109        if (!IsOOMError()) {
    110          return nullptr;
    111        }
    112 
    113        return std::nullopt;
    114      });
    115 
    116  return ret.value_or(nullptr);
    117 #else
    118  return ::CreateFileMappingW(INVALID_HANDLE_VALUE, lpFileMappingAttributes,
    119                              flProtect, dwMaximumSizeHigh, dwMaximumSizeLow,
    120                              lpName);
    121 #endif
    122 }
    123 
    124 }  // namespace
    125 
    126 namespace mozilla::ipc::shared_memory {
    127 
    128 static Maybe<PlatformHandle> CreateImpl(size_t aSize, bool aFreezable) {
    129  // If the shared memory object has no DACL, any process can
    130  // duplicate its handles with any access rights; e.g., re-add write
    131  // access to a read-only handle.  To prevent that, we give it an
    132  // empty DACL, so that no process can do that.
    133  SECURITY_ATTRIBUTES sa, *psa = nullptr;
    134  SECURITY_DESCRIPTOR sd;
    135  ACL dacl;
    136 
    137  if (aFreezable) {
    138    psa = &sa;
    139    sa.nLength = sizeof(sa);
    140    sa.lpSecurityDescriptor = &sd;
    141    sa.bInheritHandle = FALSE;
    142 
    143    if (NS_WARN_IF(!::InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) ||
    144        NS_WARN_IF(!::InitializeSecurityDescriptor(
    145            &sd, SECURITY_DESCRIPTOR_REVISION)) ||
    146        NS_WARN_IF(!::SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) {
    147      return Nothing();
    148    }
    149  }
    150 
    151  auto handle = MozCreateFileMappingW(psa, PAGE_READWRITE, 0,
    152                                      static_cast<DWORD>(aSize), nullptr);
    153  if (!handle) {
    154    return Nothing();
    155  } else {
    156    return Some(handle);
    157  }
    158 }
    159 
    160 bool Platform::Create(MutableHandle& aHandle, size_t aSize) {
    161  if (auto ph = CreateImpl(aSize, false)) {
    162    aHandle.mHandle = std::move(*ph);
    163    aHandle.SetSize(aSize);
    164    return true;
    165  }
    166  return false;
    167 }
    168 
    169 bool Platform::CreateFreezable(FreezableHandle& aHandle, size_t aSize) {
    170  if (auto ph = CreateImpl(aSize, true)) {
    171    aHandle.mHandle = std::move(*ph);
    172    aHandle.SetSize(aSize);
    173    return true;
    174  }
    175  return false;
    176 }
    177 
    178 PlatformHandle Platform::CloneHandle(const PlatformHandle& aHandle) {
    179  HANDLE h = INVALID_HANDLE_VALUE;
    180  if (::DuplicateHandle(::GetCurrentProcess(), aHandle.get(),
    181                        ::GetCurrentProcess(), &h, 0, false,
    182                        DUPLICATE_SAME_ACCESS)) {
    183    return PlatformHandle(h);
    184  }
    185  NS_WARNING("DuplicateHandle Failed!");
    186  return nullptr;
    187 }
    188 
    189 bool Platform::Freeze(FreezableHandle& aHandle) {
    190  HANDLE ro_handle;
    191  if (!::DuplicateHandle(::GetCurrentProcess(), aHandle.mHandle.get(),
    192                         ::GetCurrentProcess(), &ro_handle,
    193                         GENERIC_READ | FILE_MAP_READ, false, 0)) {
    194    return false;
    195  }
    196 
    197  aHandle.mHandle.reset(ro_handle);
    198  return true;
    199 }
    200 
    201 Maybe<void*> Platform::Map(const HandleBase& aHandle, uint64_t aOffset,
    202                           size_t aSize, void* aFixedAddress, bool aReadOnly) {
    203  DWORD fileOffsetHigh = (aOffset >> 32) & 0xffffffff;
    204  DWORD fileOffsetLow = aOffset & 0xffffffff;
    205  void* mem = ::MapViewOfFileEx(
    206      aHandle.mHandle.get(),
    207      aReadOnly ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE,
    208      fileOffsetHigh, fileOffsetLow, aSize, aFixedAddress);
    209  if (mem) {
    210    MOZ_ASSERT(!aFixedAddress || mem == aFixedAddress,
    211               "MapViewOfFileEx returned an expected address");
    212    return Some(mem);
    213  }
    214  return Nothing();
    215 }
    216 
    217 void Platform::Unmap(void* aMemory, size_t aSize) {
    218  ::UnmapViewOfFile(aMemory);
    219 }
    220 
    221 bool Platform::Protect(char* aAddr, size_t aSize, Access aAccess) {
    222  DWORD flags;
    223  if ((aAccess & AccessReadWrite) == AccessReadWrite)
    224    flags = PAGE_READWRITE;
    225  else if (aAccess & AccessRead)
    226    flags = PAGE_READONLY;
    227  else
    228    flags = PAGE_NOACCESS;
    229 
    230  DWORD oldflags;
    231  return ::VirtualProtect(aAddr, aSize, flags, &oldflags);
    232 }
    233 
    234 void* Platform::FindFreeAddressSpace(size_t aSize) {
    235  void* memory = ::VirtualAlloc(NULL, aSize, MEM_RESERVE, PAGE_NOACCESS);
    236  if (memory) {
    237    ::VirtualFree(memory, 0, MEM_RELEASE);
    238  }
    239  return memory;
    240 }
    241 
    242 size_t Platform::PageSize() {
    243  SYSTEM_INFO si;
    244  ::GetSystemInfo(&si);
    245  return si.dwPageSize;
    246 }
    247 
    248 size_t Platform::AllocationGranularity() {
    249  SYSTEM_INFO si;
    250  ::GetSystemInfo(&si);
    251  return si.dwAllocationGranularity;
    252 }
    253 
    254 bool Platform::IsSafeToMap(const PlatformHandle& aHandle) {
    255  return IsSectionSafeToMap(aHandle.get());
    256 }
    257 
    258 }  // namespace mozilla::ipc::shared_memory