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 --- .../sandbox/chromium/base/win/current_module.h | 17 + security/sandbox/chromium/base/win/pe_image.cc | 652 +++++++++++++++++++++ security/sandbox/chromium/base/win/pe_image.h | 308 ++++++++++ .../sandbox/chromium/base/win/scoped_handle.cc | 44 ++ security/sandbox/chromium/base/win/scoped_handle.h | 184 ++++++ .../chromium/base/win/scoped_handle_verifier.cc | 238 ++++++++ .../chromium/base/win/scoped_handle_verifier.h | 88 +++ .../base/win/scoped_process_information.cc | 107 ++++ .../chromium/base/win/scoped_process_information.h | 75 +++ .../chromium/base/win/startup_information.cc | 59 ++ .../chromium/base/win/startup_information.h | 53 ++ .../sandbox/chromium/base/win/static_constants.cc | 13 + .../sandbox/chromium/base/win/static_constants.h | 21 + security/sandbox/chromium/base/win/windows_types.h | 278 +++++++++ .../sandbox/chromium/base/win/windows_version.cc | 313 ++++++++++ .../sandbox/chromium/base/win/windows_version.h | 187 ++++++ 16 files changed, 2637 insertions(+) create mode 100644 security/sandbox/chromium/base/win/current_module.h create mode 100644 security/sandbox/chromium/base/win/pe_image.cc create mode 100644 security/sandbox/chromium/base/win/pe_image.h create mode 100644 security/sandbox/chromium/base/win/scoped_handle.cc create mode 100644 security/sandbox/chromium/base/win/scoped_handle.h create mode 100644 security/sandbox/chromium/base/win/scoped_handle_verifier.cc create mode 100644 security/sandbox/chromium/base/win/scoped_handle_verifier.h create mode 100644 security/sandbox/chromium/base/win/scoped_process_information.cc create mode 100644 security/sandbox/chromium/base/win/scoped_process_information.h create mode 100644 security/sandbox/chromium/base/win/startup_information.cc create mode 100644 security/sandbox/chromium/base/win/startup_information.h create mode 100644 security/sandbox/chromium/base/win/static_constants.cc create mode 100644 security/sandbox/chromium/base/win/static_constants.h create mode 100644 security/sandbox/chromium/base/win/windows_types.h create mode 100644 security/sandbox/chromium/base/win/windows_version.cc create mode 100644 security/sandbox/chromium/base/win/windows_version.h (limited to 'security/sandbox/chromium/base/win') diff --git a/security/sandbox/chromium/base/win/current_module.h b/security/sandbox/chromium/base/win/current_module.h new file mode 100644 index 0000000000..ee141db211 --- /dev/null +++ b/security/sandbox/chromium/base/win/current_module.h @@ -0,0 +1,17 @@ +// Copyright 2016 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. + +#ifndef BASE_WIN_CURRENT_MODULE_H_ +#define BASE_WIN_CURRENT_MODULE_H_ + +#include + +// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx +extern "C" IMAGE_DOS_HEADER __ImageBase; + +// Returns the HMODULE of the dll the macro was expanded in. +// Only use in cc files, not in h files. +#define CURRENT_MODULE() reinterpret_cast(&__ImageBase) + +#endif // BASE_WIN_CURRENT_MODULE_H_ diff --git a/security/sandbox/chromium/base/win/pe_image.cc b/security/sandbox/chromium/base/win/pe_image.cc new file mode 100644 index 0000000000..3d52964a24 --- /dev/null +++ b/security/sandbox/chromium/base/win/pe_image.cc @@ -0,0 +1,652 @@ +// Copyright (c) 2010 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. + +// This file implements PEImage, a generic class to manipulate PE files. +// This file was adapted from GreenBorder's Code. + +#include "base/win/pe_image.h" + +#include +#include +#include + +#include "base/no_destructor.h" +#include "base/win/current_module.h" + +namespace base { +namespace win { + +// Structure to perform imports enumerations. +struct EnumAllImportsStorage { + PEImage::EnumImportsFunction callback; + PVOID cookie; +}; + +namespace { + +// PdbInfo Signature +const DWORD kPdbInfoSignature = 'SDSR'; + +// Compare two strings byte by byte on an unsigned basis. +// if s1 == s2, return 0 +// if s1 < s2, return negative +// if s1 > s2, return positive +// Exception if inputs are invalid. +int StrCmpByByte(LPCSTR s1, LPCSTR s2) { + while (*s1 != '\0' && *s1 == *s2) { + ++s1; + ++s2; + } + + return (*reinterpret_cast(s1) - + *reinterpret_cast(s2)); +} + +struct PdbInfo { + DWORD Signature; + GUID Guid; + DWORD Age; + char PdbFileName[1]; +}; + +#define LDR_IS_DATAFILE(handle) (((ULONG_PTR)(handle)) & (ULONG_PTR)1) +#define LDR_IS_IMAGEMAPPING(handle) (((ULONG_PTR)(handle)) & (ULONG_PTR)2) +#define LDR_IS_RESOURCE(handle) \ + (LDR_IS_IMAGEMAPPING(handle) || LDR_IS_DATAFILE(handle)) + +} // namespace + +// Callback used to enumerate imports. See EnumImportChunksFunction. +bool ProcessImportChunk(const PEImage& image, + LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) { + EnumAllImportsStorage& storage = + *reinterpret_cast(cookie); + + return image.EnumOneImportChunk(storage.callback, module, name_table, iat, + storage.cookie); +} + +// Callback used to enumerate delay imports. See EnumDelayImportChunksFunction. +bool ProcessDelayImportChunk(const PEImage& image, + PImgDelayDescr delay_descriptor, + LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) { + EnumAllImportsStorage& storage = + *reinterpret_cast(cookie); + + return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor, + module, name_table, iat, storage.cookie); +} + +void PEImage::set_module(HMODULE module) { + module_ = module; +} + +PIMAGE_DOS_HEADER PEImage::GetDosHeader() const { + return reinterpret_cast(module_); +} + +PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const { + PIMAGE_DOS_HEADER dos_header = GetDosHeader(); + + return reinterpret_cast( + reinterpret_cast(dos_header) + dos_header->e_lfanew); +} + +PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const { + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers); + + if (section < nt_headers->FileHeader.NumberOfSections) + return first_section + section; + else + return nullptr; +} + +WORD PEImage::GetNumSections() const { + return GetNTHeaders()->FileHeader.NumberOfSections; +} + +DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const { + const IMAGE_DATA_DIRECTORY* const entry = GetDataDirectory(directory); + return entry ? entry->Size : 0; +} + +PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const { + const IMAGE_DATA_DIRECTORY* const entry = GetDataDirectory(directory); + return entry ? RVAToAddr(entry->VirtualAddress) : nullptr; +} + +PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const { + PBYTE target = reinterpret_cast(address); + PIMAGE_SECTION_HEADER section; + + for (UINT i = 0; nullptr != (section = GetSectionHeader(i)); i++) { + // Don't use the virtual RVAToAddr. + PBYTE start = + reinterpret_cast(PEImage::RVAToAddr(section->VirtualAddress)); + + DWORD size = section->Misc.VirtualSize; + + if ((start <= target) && (start + size > target)) + return section; + } + + return nullptr; +} + +PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName( + LPCSTR section_name) const { + if (nullptr == section_name) + return nullptr; + + PIMAGE_SECTION_HEADER ret = nullptr; + int num_sections = GetNumSections(); + + for (int i = 0; i < num_sections; i++) { + PIMAGE_SECTION_HEADER section = GetSectionHeader(i); + if (_strnicmp(reinterpret_cast(section->Name), section_name, + sizeof(section->Name)) == 0) { + ret = section; + break; + } + } + + return ret; +} + +bool PEImage::GetDebugId(LPGUID guid, + LPDWORD age, + LPCSTR* pdb_filename, + size_t* pdb_filename_length) const { + DWORD debug_directory_size = + GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG); + PIMAGE_DEBUG_DIRECTORY debug_directory = + reinterpret_cast( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG)); + if (!debug_directory) + return false; + + size_t directory_count = debug_directory_size / sizeof(IMAGE_DEBUG_DIRECTORY); + for (size_t index = 0; index < directory_count; ++index) { + const IMAGE_DEBUG_DIRECTORY& entry = debug_directory[index]; + if (entry.Type != IMAGE_DEBUG_TYPE_CODEVIEW) + continue; // Unsupported debugging info format. + if (entry.SizeOfData < sizeof(PdbInfo)) + continue; // The data is too small to hold PDB info. + const PdbInfo* pdb_info = + reinterpret_cast(RVAToAddr(entry.AddressOfRawData)); + if (!pdb_info) + continue; // The data is not present in a mapped section. + if (pdb_info->Signature != kPdbInfoSignature) + continue; // Unsupported PdbInfo signature + + if (guid) + *guid = pdb_info->Guid; + if (age) + *age = pdb_info->Age; + if (pdb_filename) { + const size_t length_max = + entry.SizeOfData - offsetof(PdbInfo, PdbFileName); + const char* eos = pdb_info->PdbFileName; + for (const char* const end = pdb_info->PdbFileName + length_max; + eos < end && *eos; ++eos) + ; + *pdb_filename_length = eos - pdb_info->PdbFileName; + *pdb_filename = pdb_info->PdbFileName; + } + return true; + } + return false; +} + +PDWORD PEImage::GetExportEntry(LPCSTR name) const { + PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory(); + + if (nullptr == exports) + return nullptr; + + WORD ordinal = 0; + if (!GetProcOrdinal(name, &ordinal)) + return nullptr; + + PDWORD functions = + reinterpret_cast(RVAToAddr(exports->AddressOfFunctions)); + + return functions + ordinal - exports->Base; +} + +FARPROC PEImage::GetProcAddress(LPCSTR function_name) const { + PDWORD export_entry = GetExportEntry(function_name); + if (nullptr == export_entry) + return nullptr; + + PBYTE function = reinterpret_cast(RVAToAddr(*export_entry)); + + PBYTE exports = reinterpret_cast( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT)); + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT); + if (!exports || !size) + return nullptr; + + // Check for forwarded exports as a special case. + if (exports <= function && exports + size > function) + return reinterpret_cast(-1); + + return reinterpret_cast(function); +} + +bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD* ordinal) const { + if (nullptr == ordinal) + return false; + + PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory(); + + if (nullptr == exports) + return false; + + if (IsOrdinal(function_name)) { + *ordinal = ToOrdinal(function_name); + } else { + PDWORD names = reinterpret_cast(RVAToAddr(exports->AddressOfNames)); + PDWORD lower = names; + PDWORD upper = names + exports->NumberOfNames; + int cmp = -1; + + // Binary Search for the name. + while (lower != upper) { + PDWORD middle = lower + (upper - lower) / 2; + LPCSTR name = reinterpret_cast(RVAToAddr(*middle)); + + // This may be called by sandbox before MSVCRT dll loads, so can't use + // CRT function here. + cmp = StrCmpByByte(function_name, name); + + if (cmp == 0) { + lower = middle; + break; + } + + if (cmp > 0) + lower = middle + 1; + else + upper = middle; + } + + if (cmp != 0) + return false; + + PWORD ordinals = + reinterpret_cast(RVAToAddr(exports->AddressOfNameOrdinals)); + + *ordinal = ordinals[lower - names] + static_cast(exports->Base); + } + + return true; +} + +bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const { + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + UINT num_sections = nt_headers->FileHeader.NumberOfSections; + PIMAGE_SECTION_HEADER section = GetSectionHeader(0); + + for (UINT i = 0; i < num_sections; i++, section++) { + PVOID section_start = RVAToAddr(section->VirtualAddress); + DWORD size = section->Misc.VirtualSize; + + if (!callback(*this, section, section_start, size, cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const { + PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT); + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT); + + // Check if there are any exports at all. + if (!directory || !size) + return true; + + PIMAGE_EXPORT_DIRECTORY exports = + reinterpret_cast(directory); + UINT ordinal_base = exports->Base; + UINT num_funcs = exports->NumberOfFunctions; + UINT num_names = exports->NumberOfNames; + PDWORD functions = + reinterpret_cast(RVAToAddr(exports->AddressOfFunctions)); + PDWORD names = reinterpret_cast(RVAToAddr(exports->AddressOfNames)); + PWORD ordinals = + reinterpret_cast(RVAToAddr(exports->AddressOfNameOrdinals)); + + for (UINT count = 0; count < num_funcs; count++) { + PVOID func = RVAToAddr(functions[count]); + if (nullptr == func) + continue; + + // Check for a name. + LPCSTR name = nullptr; + UINT hint; + for (hint = 0; hint < num_names; hint++) { + if (ordinals[hint] == count) { + name = reinterpret_cast(RVAToAddr(names[hint])); + break; + } + } + + if (name == nullptr) + hint = 0; + + // Check for forwarded exports. + LPCSTR forward = nullptr; + if (reinterpret_cast(func) >= reinterpret_cast(directory) && + reinterpret_cast(func) <= + reinterpret_cast(directory) + size) { + forward = reinterpret_cast(func); + func = nullptr; + } + + if (!callback(*this, ordinal_base + count, hint, name, func, forward, + cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const { + PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC); + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC); + + if (!directory || !size) + return true; + + PIMAGE_BASE_RELOCATION base = + reinterpret_cast(directory); + while (size >= sizeof(IMAGE_BASE_RELOCATION) && base->SizeOfBlock && + size >= base->SizeOfBlock) { + PWORD reloc = reinterpret_cast(base + 1); + UINT num_relocs = + (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); + + for (UINT i = 0; i < num_relocs; i++, reloc++) { + WORD type = *reloc >> 12; + PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF)); + + if (!callback(*this, type, address, cookie)) + return false; + } + + size -= base->SizeOfBlock; + base = reinterpret_cast( + reinterpret_cast(base) + base->SizeOfBlock); + } + + return true; +} + +bool PEImage::EnumImportChunks(EnumImportChunksFunction callback, + PVOID cookie, + LPCSTR target_module_name) const { + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT); + PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk(); + + if (import == nullptr || size < sizeof(IMAGE_IMPORT_DESCRIPTOR)) + return true; + + for (; import->FirstThunk; import++) { + LPCSTR module_name = reinterpret_cast(RVAToAddr(import->Name)); + PIMAGE_THUNK_DATA name_table = reinterpret_cast( + RVAToAddr(import->OriginalFirstThunk)); + PIMAGE_THUNK_DATA iat = + reinterpret_cast(RVAToAddr(import->FirstThunk)); + + if (target_module_name == nullptr || + (lstrcmpiA(module_name, target_module_name) == 0)) { + if (!callback(*this, module_name, name_table, iat, cookie)) + return false; + } + } + + return true; +} + +bool PEImage::EnumOneImportChunk(EnumImportsFunction callback, + LPCSTR module_name, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) const { + if (nullptr == name_table) + return false; + + for (; name_table && name_table->u1.Ordinal; name_table++, iat++) { + LPCSTR name = nullptr; + WORD ordinal = 0; + WORD hint = 0; + + if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) { + ordinal = static_cast(IMAGE_ORDINAL32(name_table->u1.Ordinal)); + } else { + PIMAGE_IMPORT_BY_NAME import = reinterpret_cast( + RVAToAddr(name_table->u1.ForwarderString)); + + hint = import->Hint; + name = reinterpret_cast(&import->Name); + } + + if (!callback(*this, module_name, ordinal, name, hint, iat, cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumAllImports(EnumImportsFunction callback, + PVOID cookie, + LPCSTR target_module_name) const { + EnumAllImportsStorage temp = {callback, cookie}; + return EnumImportChunks(ProcessImportChunk, &temp, target_module_name); +} + +bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback, + PVOID cookie, + LPCSTR target_module_name) const { + PVOID directory = + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT); + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT); + + if (!directory || !size) + return true; + + PImgDelayDescr delay_descriptor = reinterpret_cast(directory); + for (; delay_descriptor->rvaHmod; delay_descriptor++) { + PIMAGE_THUNK_DATA name_table; + PIMAGE_THUNK_DATA iat; + LPCSTR module_name; + + // check if VC7-style imports, using RVAs instead of + // VC6-style addresses. + bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0; + + if (rvas) { + module_name = + reinterpret_cast(RVAToAddr(delay_descriptor->rvaDLLName)); + name_table = reinterpret_cast( + RVAToAddr(delay_descriptor->rvaINT)); + iat = reinterpret_cast( + RVAToAddr(delay_descriptor->rvaIAT)); + } else { + // Values in IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT are 32-bit, even on 64-bit + // platforms. See section 4.8 of PECOFF image spec rev 8.3. + module_name = reinterpret_cast( + static_cast(delay_descriptor->rvaDLLName)); + name_table = reinterpret_cast( + static_cast(delay_descriptor->rvaINT)); + iat = reinterpret_cast( + static_cast(delay_descriptor->rvaIAT)); + } + + if (target_module_name == nullptr || + (lstrcmpiA(module_name, target_module_name) == 0)) { + if (target_module_name) { + // Ensure all imports are properly loaded for the target module so that + // the callback is operating on a fully-realized set of imports. + // This call only loads the imports for the module where this code is + // executing, so it is only helpful or meaningful to do this if the + // current module is the module whose IAT we are enumerating. + // Use the module_name as retrieved from the IAT because this method + // is case sensitive. + if (module_ == CURRENT_MODULE() && !LDR_IS_RESOURCE(module_)) { + static base::NoDestructor> loaded_dlls; + // pair.second is true if this is a new element + if (loaded_dlls.get()->emplace(module_name).second) + ::__HrLoadAllImportsForDll(module_name); + } + } + + if (!callback(*this, delay_descriptor, module_name, name_table, iat, + cookie)) + return false; + } + } + + return true; +} + +bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback, + PImgDelayDescr delay_descriptor, + LPCSTR module_name, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) const { + for (; name_table->u1.Ordinal; name_table++, iat++) { + LPCSTR name = nullptr; + WORD ordinal = 0; + WORD hint = 0; + + if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) { + ordinal = static_cast(IMAGE_ORDINAL32(name_table->u1.Ordinal)); + } else { + PIMAGE_IMPORT_BY_NAME import; + bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0; + + if (rvas) { + import = reinterpret_cast( + RVAToAddr(name_table->u1.ForwarderString)); + } else { + import = reinterpret_cast( + name_table->u1.ForwarderString); + } + + hint = import->Hint; + name = reinterpret_cast(&import->Name); + } + + if (!callback(*this, module_name, ordinal, name, hint, iat, cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumAllDelayImports(EnumImportsFunction callback, + PVOID cookie, + LPCSTR target_module_name) const { + EnumAllImportsStorage temp = {callback, cookie}; + return EnumDelayImportChunks(ProcessDelayImportChunk, &temp, + target_module_name); +} + +bool PEImage::VerifyMagic() const { + PIMAGE_DOS_HEADER dos_header = GetDosHeader(); + + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) + return false; + + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) + return false; + + if (nt_headers->FileHeader.SizeOfOptionalHeader != + sizeof(IMAGE_OPTIONAL_HEADER)) + return false; + + if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) + return false; + + return true; +} + +bool PEImage::ImageRVAToOnDiskOffset(uintptr_t rva, + DWORD* on_disk_offset) const { + LPVOID address = RVAToAddr(rva); + return ImageAddrToOnDiskOffset(address, on_disk_offset); +} + +bool PEImage::ImageAddrToOnDiskOffset(LPVOID address, + DWORD* on_disk_offset) const { + if (nullptr == address) + return false; + + // Get the section that this address belongs to. + PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address); + if (nullptr == section_header) + return false; + + // Don't follow the virtual RVAToAddr, use the one on the base. + DWORD offset_within_section = + static_cast(reinterpret_cast(address)) - + static_cast(reinterpret_cast( + PEImage::RVAToAddr(section_header->VirtualAddress))); + + *on_disk_offset = section_header->PointerToRawData + offset_within_section; + return true; +} + +PVOID PEImage::RVAToAddr(uintptr_t rva) const { + if (rva == 0) + return nullptr; + + return reinterpret_cast(module_) + rva; +} + +const IMAGE_DATA_DIRECTORY* PEImage::GetDataDirectory(UINT directory) const { + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + + // Does the image report that it includes this directory entry? + if (directory >= nt_headers->OptionalHeader.NumberOfRvaAndSizes) + return nullptr; + + // Is there space for this directory entry in the optional header? + if (nt_headers->FileHeader.SizeOfOptionalHeader < + (offsetof(IMAGE_OPTIONAL_HEADER, DataDirectory) + + (directory + 1) * sizeof(IMAGE_DATA_DIRECTORY))) { + return nullptr; + } + + return &nt_headers->OptionalHeader.DataDirectory[directory]; +} + +PVOID PEImageAsData::RVAToAddr(uintptr_t rva) const { + if (rva == 0) + return nullptr; + + PVOID in_memory = PEImage::RVAToAddr(rva); + DWORD disk_offset; + + if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset)) + return nullptr; + + return PEImage::RVAToAddr(disk_offset); +} + +} // namespace win +} // namespace base diff --git a/security/sandbox/chromium/base/win/pe_image.h b/security/sandbox/chromium/base/win/pe_image.h new file mode 100644 index 0000000000..cec959a940 --- /dev/null +++ b/security/sandbox/chromium/base/win/pe_image.h @@ -0,0 +1,308 @@ +// Copyright (c) 2010 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. + +// This file was adapted from GreenBorder's Code. +// To understand what this class is about (for other than well known functions +// as GetProcAddress), a good starting point is "An In-Depth Look into the +// Win32 Portable Executable File Format" by Matt Pietrek: +// http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx + +#ifndef BASE_WIN_PE_IMAGE_H_ +#define BASE_WIN_PE_IMAGE_H_ + +#include + +#include + +#if defined(_WIN32_WINNT_WIN8) +// The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h. +#undef FACILITY_VISUALCPP +#endif +#include + +namespace base { +namespace win { + +// This class is a wrapper for the Portable Executable File Format (PE). +// Its main purpose is to provide an easy way to work with imports and exports +// from a file, mapped in memory as image. +class PEImage { + public: + // Callback to enumerate sections. + // cookie is the value passed to the enumerate method. + // Returns true to continue the enumeration. + using EnumSectionsFunction = + bool (*)(const PEImage&, PIMAGE_SECTION_HEADER, PVOID, DWORD, PVOID); + + // Callback to enumerate exports. + // function is the actual address of the symbol. If forward is not null, it + // contains the dll and symbol to forward this export to. cookie is the value + // passed to the enumerate method. + // Returns true to continue the enumeration. + using EnumExportsFunction = + bool (*)(const PEImage&, DWORD, DWORD, LPCSTR, PVOID, LPCSTR, PVOID); + + // Callback to enumerate import blocks. + // name_table and iat point to the imports name table and address table for + // this block. cookie is the value passed to the enumerate method. + // Returns true to continue the enumeration. + using EnumImportChunksFunction = bool (*)(const PEImage&, + LPCSTR, + PIMAGE_THUNK_DATA, + PIMAGE_THUNK_DATA, + PVOID); + + // Callback to enumerate imports. + // module is the dll that exports this symbol. cookie is the value passed to + // the enumerate method. + // Returns true to continue the enumeration. + using EnumImportsFunction = bool (*)(const PEImage&, + LPCSTR, + DWORD, + LPCSTR, + DWORD, + PIMAGE_THUNK_DATA, + PVOID); + + // Callback to enumerate delayed import blocks. + // module is the dll that exports this block of symbols. cookie is the value + // passed to the enumerate method. + // Returns true to continue the enumeration. + using EnumDelayImportChunksFunction = bool (*)(const PEImage&, + PImgDelayDescr, + LPCSTR, + PIMAGE_THUNK_DATA, + PIMAGE_THUNK_DATA, + PVOID); + + // Callback to enumerate relocations. + // cookie is the value passed to the enumerate method. + // Returns true to continue the enumeration. + using EnumRelocsFunction = bool (*)(const PEImage&, WORD, PVOID, PVOID); + + explicit PEImage(HMODULE module) : module_(module) {} + explicit PEImage(const void* module) { + module_ = reinterpret_cast(const_cast(module)); + } + + virtual ~PEImage() = default; + + // Gets the HMODULE for this object. + HMODULE module() const; + + // Sets this object's HMODULE. + void set_module(HMODULE module); + + // Checks if this symbol is actually an ordinal. + static bool IsOrdinal(LPCSTR name); + + // Converts a named symbol to the corresponding ordinal. + static WORD ToOrdinal(LPCSTR name); + + // Returns the DOS_HEADER for this PE. + PIMAGE_DOS_HEADER GetDosHeader() const; + + // Returns the NT_HEADER for this PE. + PIMAGE_NT_HEADERS GetNTHeaders() const; + + // Returns number of sections of this PE. + WORD GetNumSections() const; + + // Returns the header for a given section. + // returns NULL if there is no such section. + PIMAGE_SECTION_HEADER GetSectionHeader(UINT section) const; + + // Returns the size of a given directory entry or 0 if |directory| is out of + // bounds. + DWORD GetImageDirectoryEntrySize(UINT directory) const; + + // Returns the address of a given directory entry or NULL if |directory| is + // out of bounds. + PVOID GetImageDirectoryEntryAddr(UINT directory) const; + + // Returns the section header for a given address. + // Use: s = image.GetImageSectionFromAddr(a); + // Post: 's' is the section header of the section that contains 'a' + // or NULL if there is no such section. + PIMAGE_SECTION_HEADER GetImageSectionFromAddr(PVOID address) const; + + // Returns the section header for a given section. + PIMAGE_SECTION_HEADER GetImageSectionHeaderByName(LPCSTR section_name) const; + + // Returns the first block of imports. + PIMAGE_IMPORT_DESCRIPTOR GetFirstImportChunk() const; + + // Returns the exports directory. + PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const; + + // Retrieves the contents of the image's CodeView debug entry, returning true + // if such an entry is found and is within a section mapped into the current + // process's memory. |guid|, |age|, and |pdb_filename| are each optional and + // may be NULL. |pdb_filename_length| is mandatory if |pdb_filename| is not + // NULL, as the latter is populated with a direct reference to a string in the + // image that is is not guaranteed to be terminated (note: informal + // documentation indicates that it should be terminated, but the data is + // untrusted). Furthermore, owing to its nature of being a string in the + // image, it is only valid while the image is mapped into the process, and the + // caller is not responsible for freeing it. |pdb_filename_length| is + // populated with the string length of |pdb_filename| (not including a + // terminator) and must be used rather than relying on |pdb_filename| being + // properly terminated. + bool GetDebugId(LPGUID guid, + LPDWORD age, + LPCSTR* pdb_filename, + size_t* pdb_filename_length) const; + + // Returns a given export entry. + // Use: e = image.GetExportEntry(f); + // Pre: 'f' is either a zero terminated string or ordinal + // Post: 'e' is a pointer to the export directory entry + // that contains 'f's export RVA, or NULL if 'f' + // is not exported from this image + PDWORD GetExportEntry(LPCSTR name) const; + + // Returns the address for a given exported symbol. + // Use: p = image.GetProcAddress(f); + // Pre: 'f' is either a zero terminated string or ordinal. + // Post: if 'f' is a non-forwarded export from image, 'p' is + // the exported function. If 'f' is a forwarded export + // then p is the special value -1. In this case + // RVAToAddr(*GetExportEntry) can be used to resolve + // the string that describes the forward. + FARPROC GetProcAddress(LPCSTR function_name) const; + + // Retrieves the ordinal for a given exported symbol. + // Returns true if the symbol was found. + bool GetProcOrdinal(LPCSTR function_name, WORD* ordinal) const; + + // Enumerates PE sections. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumSections(EnumSectionsFunction callback, PVOID cookie) const; + + // Enumerates PE exports. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumExports(EnumExportsFunction callback, PVOID cookie) const; + + // Enumerates PE imports. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + // Use |target_module_name| to ensure the callback is only invoked for the + // specified module. + bool EnumAllImports(EnumImportsFunction callback, + PVOID cookie, + LPCSTR target_module_name) const; + + // Enumerates PE import blocks. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + // Use |target_module_name| to ensure the callback is only invoked for the + // specified module. + bool EnumImportChunks(EnumImportChunksFunction callback, + PVOID cookie, + LPCSTR target_module_name) const; + + // Enumerates the imports from a single PE import block. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumOneImportChunk(EnumImportsFunction callback, + LPCSTR module_name, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) const; + + // Enumerates PE delay imports. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + // Use |target_module_name| to ensure the callback is only invoked for the + // specified module. If this parameter is non-null then all delayloaded + // imports are resolved when the target module is found. + bool EnumAllDelayImports(EnumImportsFunction callback, + PVOID cookie, + LPCSTR target_module_name) const; + + // Enumerates PE delay import blocks. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + // Use |target_module_name| to ensure the callback is only invoked for the + // specified module. If this parameter is non-null then all delayloaded + // imports are resolved when the target module is found. + bool EnumDelayImportChunks(EnumDelayImportChunksFunction callback, + PVOID cookie, + LPCSTR target_module_name) const; + + // Enumerates imports from a single PE delay import block. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumOneDelayImportChunk(EnumImportsFunction callback, + PImgDelayDescr delay_descriptor, + LPCSTR module_name, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) const; + + // Enumerates PE relocation entries. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const; + + // Verifies the magic values on the PE file. + // Returns true if all values are correct. + bool VerifyMagic() const; + + // Converts an rva value to the appropriate address. + virtual PVOID RVAToAddr(uintptr_t rva) const; + + // Converts an rva value to an offset on disk. + // Returns true on success. + bool ImageRVAToOnDiskOffset(uintptr_t rva, DWORD* on_disk_offset) const; + + // Converts an address to an offset on disk. + // Returns true on success. + bool ImageAddrToOnDiskOffset(LPVOID address, DWORD* on_disk_offset) const; + + private: + // Returns a pointer to a data directory, or NULL if |directory| is out of + // range. + const IMAGE_DATA_DIRECTORY* GetDataDirectory(UINT directory) const; + + HMODULE module_; +}; + +// This class is an extension to the PEImage class that allows working with PE +// files mapped as data instead of as image file. +class PEImageAsData : public PEImage { + public: + explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {} + + PVOID RVAToAddr(uintptr_t rva) const override; +}; + +inline bool PEImage::IsOrdinal(LPCSTR name) { + return reinterpret_cast(name) <= 0xFFFF; +} + +inline WORD PEImage::ToOrdinal(LPCSTR name) { + return static_cast(reinterpret_cast(name)); +} + +inline HMODULE PEImage::module() const { + return module_; +} + +inline PIMAGE_IMPORT_DESCRIPTOR PEImage::GetFirstImportChunk() const { + return reinterpret_cast( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_IMPORT)); +} + +inline PIMAGE_EXPORT_DIRECTORY PEImage::GetExportDirectory() const { + return reinterpret_cast( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT)); +} + +} // namespace win +} // namespace base + +#endif // BASE_WIN_PE_IMAGE_H_ diff --git a/security/sandbox/chromium/base/win/scoped_handle.cc b/security/sandbox/chromium/base/win/scoped_handle.cc new file mode 100644 index 0000000000..de6854591b --- /dev/null +++ b/security/sandbox/chromium/base/win/scoped_handle.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2012 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/win/scoped_handle.h" +#include "base/win/scoped_handle_verifier.h" +#include "base/win/windows_types.h" + +namespace base { +namespace win { + +using base::win::internal::ScopedHandleVerifier; + +// Static. +bool HandleTraits::CloseHandle(HANDLE handle) { + return ScopedHandleVerifier::Get()->CloseHandle(handle); +} + +// Static. +void VerifierTraits::StartTracking(HANDLE handle, + const void* owner, + const void* pc1, + const void* pc2) { + return ScopedHandleVerifier::Get()->StartTracking(handle, owner, pc1, pc2); +} + +// Static. +void VerifierTraits::StopTracking(HANDLE handle, + const void* owner, + const void* pc1, + const void* pc2) { + return ScopedHandleVerifier::Get()->StopTracking(handle, owner, pc1, pc2); +} + +void DisableHandleVerifier() { + return ScopedHandleVerifier::Get()->Disable(); +} + +void OnHandleBeingClosed(HANDLE handle) { + return ScopedHandleVerifier::Get()->OnHandleBeingClosed(handle); +} + +} // namespace win +} // namespace base diff --git a/security/sandbox/chromium/base/win/scoped_handle.h b/security/sandbox/chromium/base/win/scoped_handle.h new file mode 100644 index 0000000000..02c2533649 --- /dev/null +++ b/security/sandbox/chromium/base/win/scoped_handle.h @@ -0,0 +1,184 @@ +// Copyright (c) 2012 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. + +#ifndef BASE_WIN_SCOPED_HANDLE_H_ +#define BASE_WIN_SCOPED_HANDLE_H_ + +#include "base/win/windows_types.h" + +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" + +// TODO(rvargas): remove this with the rest of the verifier. +#if defined(COMPILER_MSVC) +#include +#define BASE_WIN_GET_CALLER _ReturnAddress() +#elif defined(COMPILER_GCC) +#define BASE_WIN_GET_CALLER \ + __builtin_extract_return_addr(__builtin_return_address(0)) +#endif + +namespace base { +namespace win { + +// Generic wrapper for raw handles that takes care of closing handles +// automatically. The class interface follows the style of +// the ScopedFILE class with two additions: +// - IsValid() method can tolerate multiple invalid handle values such as NULL +// and INVALID_HANDLE_VALUE (-1) for Win32 handles. +// - Set() (and the constructors and assignment operators that call it) +// preserve the Windows LastError code. This ensures that GetLastError() can +// be called after stashing a handle in a GenericScopedHandle object. Doing +// this explicitly is necessary because of bug 528394 and VC++ 2015. +template +class GenericScopedHandle { + public: + using Handle = typename Traits::Handle; + + GenericScopedHandle() : handle_(Traits::NullHandle()) {} + + explicit GenericScopedHandle(Handle handle) : handle_(Traits::NullHandle()) { + Set(handle); + } + + GenericScopedHandle(GenericScopedHandle&& other) + : handle_(Traits::NullHandle()) { + Set(other.Take()); + } + + ~GenericScopedHandle() { Close(); } + + bool IsValid() const { return Traits::IsHandleValid(handle_); } + + GenericScopedHandle& operator=(GenericScopedHandle&& other) { + DCHECK_NE(this, &other); + Set(other.Take()); + return *this; + } + + void Set(Handle handle) { + if (handle_ != handle) { + // Preserve old LastError to avoid bug 528394. + auto last_error = ::GetLastError(); + Close(); + + if (Traits::IsHandleValid(handle)) { + handle_ = handle; + Verifier::StartTracking(handle, this, BASE_WIN_GET_CALLER, + GetProgramCounter()); + } + ::SetLastError(last_error); + } + } + + Handle Get() const { return handle_; } + + // Transfers ownership away from this object. + Handle Take() WARN_UNUSED_RESULT { + Handle temp = handle_; + handle_ = Traits::NullHandle(); + if (Traits::IsHandleValid(temp)) { + Verifier::StopTracking(temp, this, BASE_WIN_GET_CALLER, + GetProgramCounter()); + } + return temp; + } + + // Explicitly closes the owned handle. + void Close() { + if (Traits::IsHandleValid(handle_)) { + Verifier::StopTracking(handle_, this, BASE_WIN_GET_CALLER, + GetProgramCounter()); + + Traits::CloseHandle(handle_); + handle_ = Traits::NullHandle(); + } + } + + private: + FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, ActiveVerifierWrongOwner); + FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, ActiveVerifierUntrackedHandle); + Handle handle_; + + DISALLOW_COPY_AND_ASSIGN(GenericScopedHandle); +}; + +#undef BASE_WIN_GET_CALLER + +// The traits class for Win32 handles that can be closed via CloseHandle() API. +class HandleTraits { + public: + using Handle = HANDLE; + + // Closes the handle. + static bool BASE_EXPORT CloseHandle(HANDLE handle); + + // Returns true if the handle value is valid. + static bool IsHandleValid(HANDLE handle) { + return handle != nullptr && handle != INVALID_HANDLE_VALUE; + } + + // Returns NULL handle value. + static HANDLE NullHandle() { return nullptr; } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(HandleTraits); +}; + +// Do-nothing verifier. +class DummyVerifierTraits { + public: + using Handle = HANDLE; + + static void StartTracking(HANDLE handle, + const void* owner, + const void* pc1, + const void* pc2) {} + static void StopTracking(HANDLE handle, + const void* owner, + const void* pc1, + const void* pc2) {} + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DummyVerifierTraits); +}; + +// Performs actual run-time tracking. +class BASE_EXPORT VerifierTraits { + public: + using Handle = HANDLE; + + static void StartTracking(HANDLE handle, + const void* owner, + const void* pc1, + const void* pc2); + static void StopTracking(HANDLE handle, + const void* owner, + const void* pc1, + const void* pc2); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits); +}; + +using ScopedHandle = GenericScopedHandle; + +// This function may be called by the embedder to disable the use of +// VerifierTraits at runtime. It has no effect if DummyVerifierTraits is used +// for ScopedHandle. +BASE_EXPORT void DisableHandleVerifier(); + +// This should be called whenever the OS is closing a handle, if extended +// verification of improper handle closing is desired. If |handle| is being +// tracked by the handle verifier and ScopedHandle is not the one closing it, +// a CHECK is generated. +BASE_EXPORT void OnHandleBeingClosed(HANDLE handle); +} // namespace win +} // namespace base + +#endif // BASE_WIN_SCOPED_HANDLE_H_ diff --git a/security/sandbox/chromium/base/win/scoped_handle_verifier.cc b/security/sandbox/chromium/base/win/scoped_handle_verifier.cc new file mode 100644 index 0000000000..316606c0bf --- /dev/null +++ b/security/sandbox/chromium/base/win/scoped_handle_verifier.cc @@ -0,0 +1,238 @@ +// Copyright 2018 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/win/scoped_handle_verifier.h" + +#include + +#include + +#include + +#include "base/debug/alias.h" +#include "base/debug/stack_trace.h" +#include "base/synchronization/lock_impl.h" +#include "base/win/base_win_buildflags.h" +#include "base/win/current_module.h" + +extern "C" { +__declspec(dllexport) void* GetHandleVerifier(); + +void* GetHandleVerifier() { + return base::win::internal::ScopedHandleVerifier::Get(); +} +} // extern C + +namespace { + +base::win::internal::ScopedHandleVerifier* g_active_verifier = nullptr; +using GetHandleVerifierFn = void* (*)(); +using HandleMap = + std::unordered_map; +using NativeLock = base::internal::LockImpl; + +NativeLock* GetLock() { + static auto* native_lock = new NativeLock(); + return native_lock; +} + +// Simple automatic locking using a native critical section so it supports +// recursive locking. +class AutoNativeLock { + public: + explicit AutoNativeLock(NativeLock& lock) : lock_(lock) { lock_.Lock(); } + + ~AutoNativeLock() { lock_.Unlock(); } + + private: + NativeLock& lock_; + DISALLOW_COPY_AND_ASSIGN(AutoNativeLock); +}; + +} // namespace + +namespace base { +namespace win { +namespace internal { + +ScopedHandleVerifier::ScopedHandleVerifier(bool enabled) + : enabled_(enabled), lock_(GetLock()) {} + +// static +ScopedHandleVerifier* ScopedHandleVerifier::Get() { + if (!g_active_verifier) + ScopedHandleVerifier::InstallVerifier(); + + return g_active_verifier; +} + +bool CloseHandleWrapper(HANDLE handle) { + if (!::CloseHandle(handle)) + // Making this DCHECK on non-Nighly as we are hitting this frequently, + // looks like we are closing handles twice somehow. See bug 1564899. +#if defined(NIGHTLY_BUILD) + CHECK(false); // CloseHandle failed. +#else + DCHECK(false); // CloseHandle failed. +#endif + return true; +} + +// Assigns the g_active_verifier global within the GetLock() lock. +// If |existing_verifier| is non-null then |enabled| is ignored. +void ThreadSafeAssignOrCreateScopedHandleVerifier( + ScopedHandleVerifier* existing_verifier, + bool enabled) { + AutoNativeLock lock(*GetLock()); + // Another thread in this module might be trying to assign the global + // verifier, so check that within the lock here. + if (g_active_verifier) + return; + g_active_verifier = + existing_verifier ? existing_verifier : new ScopedHandleVerifier(enabled); +} + +// static +void ScopedHandleVerifier::InstallVerifier() { +#if BUILDFLAG(SINGLE_MODULE_MODE_HANDLE_VERIFIER) + // Component build has one Active Verifier per module. + ThreadSafeAssignOrCreateScopedHandleVerifier(nullptr, true); +#else + // If you are reading this, wondering why your process seems deadlocked, take + // a look at your DllMain code and remove things that should not be done + // there, like doing whatever gave you that nice windows handle you are trying + // to store in a ScopedHandle. + HMODULE main_module = ::GetModuleHandle(NULL); + GetHandleVerifierFn get_handle_verifier = + reinterpret_cast( + ::GetProcAddress(main_module, "GetHandleVerifier")); + + // This should only happen if running in a DLL is linked with base but the + // hosting EXE is not. In this case, create an ScopedHandleVerifier for the + // current + // module but leave it disabled. + if (!get_handle_verifier) { + ThreadSafeAssignOrCreateScopedHandleVerifier(nullptr, false); + return; + } + + // Check if in the main module. + if (get_handle_verifier == GetHandleVerifier) { + ThreadSafeAssignOrCreateScopedHandleVerifier(nullptr, true); + return; + } + + ScopedHandleVerifier* main_module_verifier = + reinterpret_cast(get_handle_verifier()); + + // Main module should always on-demand create a verifier. + DCHECK(main_module_verifier); + + ThreadSafeAssignOrCreateScopedHandleVerifier(main_module_verifier, false); +#endif +} + +bool ScopedHandleVerifier::CloseHandle(HANDLE handle) { + if (!enabled_) + return CloseHandleWrapper(handle); + + closing_.Set(true); + CloseHandleWrapper(handle); + closing_.Set(false); + + return true; +} + +// static +NativeLock* ScopedHandleVerifier::GetLock() { + return ::GetLock(); +} + +void ScopedHandleVerifier::StartTracking(HANDLE handle, + const void* owner, + const void* pc1, + const void* pc2) { + if (!enabled_) + return; + + // Grab the thread id before the lock. + DWORD thread_id = GetCurrentThreadId(); + + AutoNativeLock lock(*lock_); + + ScopedHandleVerifierInfo handle_info = {owner, pc1, pc2, + base::debug::StackTrace(), thread_id}; + std::pair item(handle, handle_info); + std::pair result = map_.insert(item); + if (!result.second) { + ScopedHandleVerifierInfo other = result.first->second; + base::debug::Alias(&other); + auto creation_stack = creation_stack_; + base::debug::Alias(&creation_stack); + CHECK(false); // Attempt to start tracking already tracked handle. + } +} + +void ScopedHandleVerifier::StopTracking(HANDLE handle, + const void* owner, + const void* pc1, + const void* pc2) { + if (!enabled_) + return; + + AutoNativeLock lock(*lock_); + HandleMap::iterator i = map_.find(handle); + if (i == map_.end()) { + auto creation_stack = creation_stack_; + base::debug::Alias(&creation_stack); + CHECK(false); // Attempting to close an untracked handle. + } + + ScopedHandleVerifierInfo other = i->second; + if (other.owner != owner) { + base::debug::Alias(&other); + auto creation_stack = creation_stack_; + base::debug::Alias(&creation_stack); + CHECK(false); // Attempting to close a handle not owned by opener. + } + + map_.erase(i); +} + +void ScopedHandleVerifier::Disable() { + enabled_ = false; +} + +void ScopedHandleVerifier::OnHandleBeingClosed(HANDLE handle) { + if (!enabled_) + return; + + if (closing_.Get()) + return; + + AutoNativeLock lock(*lock_); + HandleMap::iterator i = map_.find(handle); + if (i == map_.end()) + return; + + ScopedHandleVerifierInfo other = i->second; + base::debug::Alias(&other); + auto creation_stack = creation_stack_; + base::debug::Alias(&creation_stack); + CHECK(false); // CloseHandle called on tracked handle. +} + +HMODULE ScopedHandleVerifier::GetModule() const { + return CURRENT_MODULE(); +} + +HMODULE GetHandleVerifierModuleForTesting() { + return g_active_verifier->GetModule(); +} + +} // namespace internal +} // namespace win +} // namespace base diff --git a/security/sandbox/chromium/base/win/scoped_handle_verifier.h b/security/sandbox/chromium/base/win/scoped_handle_verifier.h new file mode 100644 index 0000000000..596e2c47eb --- /dev/null +++ b/security/sandbox/chromium/base/win/scoped_handle_verifier.h @@ -0,0 +1,88 @@ +// Copyright 2018 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. + +#ifndef BASE_WIN_SCOPED_HANDLE_VERIFIER_H_ +#define BASE_WIN_SCOPED_HANDLE_VERIFIER_H_ + +#include "base/win/windows_types.h" + +#include + +#include "base/base_export.h" +#include "base/debug/stack_trace.h" +#include "base/hash/hash.h" +#include "base/synchronization/lock_impl.h" +#include "base/threading/thread_local.h" + +namespace base { +namespace win { +namespace internal { + +struct HandleHash { + size_t operator()(const HANDLE& handle) const { + return base::FastHash(as_bytes(make_span(&handle, 1))); + } +}; + +struct ScopedHandleVerifierInfo { + const void* owner; + const void* pc1; + const void* pc2; + base::debug::StackTrace stack; + DWORD thread_id; +}; + +// Implements the actual object that is verifying handles for this process. +// The active instance is shared across the module boundary but there is no +// way to delete this object from the wrong side of it (or any side, actually). +// We need [[clang::lto_visibility_public]] because instances of this class are +// passed across module boundaries. This means different modules must have +// compatible definitions of the class even when whole program optimization is +// enabled - which is what this attribute accomplishes. The pragma stops MSVC +// from emitting an unrecognized attribute warning. +#pragma warning(push) +#pragma warning(disable : 5030) +class [[clang::lto_visibility_public]] ScopedHandleVerifier { +#pragma warning(pop) + public: + explicit ScopedHandleVerifier(bool enabled); + + // Retrieves the current verifier. + static ScopedHandleVerifier* Get(); + + // The methods required by HandleTraits. They are virtual because we need to + // forward the call execution to another module, instead of letting the + // compiler call the version that is linked in the current module. + virtual bool CloseHandle(HANDLE handle); + virtual void StartTracking(HANDLE handle, const void* owner, const void* pc1, + const void* pc2); + virtual void StopTracking(HANDLE handle, const void* owner, const void* pc1, + const void* pc2); + virtual void Disable(); + virtual void OnHandleBeingClosed(HANDLE handle); + virtual HMODULE GetModule() const; + + private: + ~ScopedHandleVerifier(); // Not implemented. + + static base::internal::LockImpl* GetLock(); + static void InstallVerifier(); + + base::debug::StackTrace creation_stack_; + bool enabled_; + base::ThreadLocalBoolean closing_; + base::internal::LockImpl* lock_; + std::unordered_map map_; + DISALLOW_COPY_AND_ASSIGN(ScopedHandleVerifier); +}; + +// This testing function returns the module that the ActiveVerifier concrete +// implementation was instantiated in. +BASE_EXPORT HMODULE GetHandleVerifierModuleForTesting(); + +} // namespace internal +} // namespace win +} // namespace base + +#endif // BASE_WIN_SCOPED_HANDLE_VERIFIER_H_ diff --git a/security/sandbox/chromium/base/win/scoped_process_information.cc b/security/sandbox/chromium/base/win/scoped_process_information.cc new file mode 100644 index 0000000000..d3024bb5e9 --- /dev/null +++ b/security/sandbox/chromium/base/win/scoped_process_information.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2012 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/win/scoped_process_information.h" + +#include "base/logging.h" +#include "base/win/scoped_handle.h" + +namespace base { +namespace win { + +namespace { + +// Duplicates source into target, returning true upon success. |target| is +// guaranteed to be untouched in case of failure. Succeeds with no side-effects +// if source is NULL. +bool CheckAndDuplicateHandle(HANDLE source, ScopedHandle* target) { + if (!source) + return true; + + HANDLE temp = nullptr; + if (!::DuplicateHandle(::GetCurrentProcess(), source, ::GetCurrentProcess(), + &temp, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + DWORD last_error = ::GetLastError(); + DPLOG(ERROR) << "Failed to duplicate a handle " << last_error; + ::SetLastError(last_error); + return false; + } + target->Set(temp); + return true; +} + +} // namespace + +ScopedProcessInformation::ScopedProcessInformation() = default; + +ScopedProcessInformation::ScopedProcessInformation( + const PROCESS_INFORMATION& process_info) { + Set(process_info); +} + +ScopedProcessInformation::~ScopedProcessInformation() { + Close(); +} + +bool ScopedProcessInformation::IsValid() const { + return process_id_ || process_handle_.Get() || thread_id_ || + thread_handle_.Get(); +} + +void ScopedProcessInformation::Close() { + process_handle_.Close(); + thread_handle_.Close(); + process_id_ = 0; + thread_id_ = 0; +} + +void ScopedProcessInformation::Set(const PROCESS_INFORMATION& process_info) { + if (IsValid()) + Close(); + + process_handle_.Set(process_info.hProcess); + thread_handle_.Set(process_info.hThread); + process_id_ = process_info.dwProcessId; + thread_id_ = process_info.dwThreadId; +} + +bool ScopedProcessInformation::DuplicateFrom( + const ScopedProcessInformation& other) { + DCHECK(!IsValid()) << "target ScopedProcessInformation must be NULL"; + DCHECK(other.IsValid()) << "source ScopedProcessInformation must be valid"; + + if (CheckAndDuplicateHandle(other.process_handle(), &process_handle_) && + CheckAndDuplicateHandle(other.thread_handle(), &thread_handle_)) { + process_id_ = other.process_id(); + thread_id_ = other.thread_id(); + return true; + } + + return false; +} + +PROCESS_INFORMATION ScopedProcessInformation::Take() { + PROCESS_INFORMATION process_information = {}; + process_information.hProcess = process_handle_.Take(); + process_information.hThread = thread_handle_.Take(); + process_information.dwProcessId = process_id(); + process_information.dwThreadId = thread_id(); + process_id_ = 0; + thread_id_ = 0; + + return process_information; +} + +HANDLE ScopedProcessInformation::TakeProcessHandle() { + process_id_ = 0; + return process_handle_.Take(); +} + +HANDLE ScopedProcessInformation::TakeThreadHandle() { + thread_id_ = 0; + return thread_handle_.Take(); +} + +} // namespace win +} // namespace base diff --git a/security/sandbox/chromium/base/win/scoped_process_information.h b/security/sandbox/chromium/base/win/scoped_process_information.h new file mode 100644 index 0000000000..3b85d1bfab --- /dev/null +++ b/security/sandbox/chromium/base/win/scoped_process_information.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012 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. + +#ifndef BASE_WIN_SCOPED_PROCESS_INFORMATION_H_ +#define BASE_WIN_SCOPED_PROCESS_INFORMATION_H_ + +#include + +#include "base/base_export.h" +#include "base/macros.h" +#include "base/win/scoped_handle.h" + +namespace base { +namespace win { + +// Manages the closing of process and thread handles from PROCESS_INFORMATION +// structures. Allows clients to take ownership of either handle independently. +class BASE_EXPORT ScopedProcessInformation { + public: + ScopedProcessInformation(); + explicit ScopedProcessInformation(const PROCESS_INFORMATION& process_info); + ~ScopedProcessInformation(); + + // Returns true iff this instance is holding a thread and/or process handle. + bool IsValid() const; + + // Closes the held thread and process handles, if any. + void Close(); + + // Populates this instance with the provided |process_info|. + void Set(const PROCESS_INFORMATION& process_info); + + // Populates this instance with duplicate handles and the thread/process IDs + // from |other|. Returns false in case of failure, in which case this instance + // will be completely unpopulated. + bool DuplicateFrom(const ScopedProcessInformation& other); + + // Transfers ownership of the held PROCESS_INFORMATION, if any, away from this + // instance. + PROCESS_INFORMATION Take(); + + // Transfers ownership of the held process handle, if any, away from this + // instance. Note that the related process_id will also be cleared. + HANDLE TakeProcessHandle(); + + // Transfers ownership of the held thread handle, if any, away from this + // instance. Note that the related thread_id will also be cleared. + HANDLE TakeThreadHandle(); + + // Returns the held process handle, if any, while retaining ownership. + HANDLE process_handle() const { return process_handle_.Get(); } + + // Returns the held thread handle, if any, while retaining ownership. + HANDLE thread_handle() const { return thread_handle_.Get(); } + + // Returns the held process id, if any. + DWORD process_id() const { return process_id_; } + + // Returns the held thread id, if any. + DWORD thread_id() const { return thread_id_; } + + private: + ScopedHandle process_handle_; + ScopedHandle thread_handle_; + DWORD process_id_ = 0; + DWORD thread_id_ = 0; + + DISALLOW_COPY_AND_ASSIGN(ScopedProcessInformation); +}; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_SCOPED_PROCESS_INFORMATION_H_ diff --git a/security/sandbox/chromium/base/win/startup_information.cc b/security/sandbox/chromium/base/win/startup_information.cc new file mode 100644 index 0000000000..a78508dcad --- /dev/null +++ b/security/sandbox/chromium/base/win/startup_information.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2012 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/win/startup_information.h" + +#include "base/logging.h" + +namespace base { +namespace win { + +StartupInformation::StartupInformation() : startup_info_() { + startup_info_.StartupInfo.cb = sizeof(startup_info_); +} + +StartupInformation::~StartupInformation() { + if (startup_info_.lpAttributeList) { + ::DeleteProcThreadAttributeList(startup_info_.lpAttributeList); + } +} + +bool StartupInformation::InitializeProcThreadAttributeList( + DWORD attribute_count) { + if (startup_info_.StartupInfo.cb != sizeof(startup_info_) || + startup_info_.lpAttributeList) { + return false; + } + + SIZE_T size = 0; + ::InitializeProcThreadAttributeList(nullptr, attribute_count, 0, &size); + if (size == 0) + return false; + + auto attribute_list = std::make_unique(size); + auto* attribute_list_ptr = + reinterpret_cast(attribute_list.get()); + if (!::InitializeProcThreadAttributeList(attribute_list_ptr, attribute_count, + 0, &size)) { + return false; + } + + attribute_list_ = std::move(attribute_list); + startup_info_.lpAttributeList = attribute_list_ptr; + + return true; +} + +bool StartupInformation::UpdateProcThreadAttribute(DWORD_PTR attribute, + void* value, + size_t size) { + if (!startup_info_.lpAttributeList) + return false; + return !!::UpdateProcThreadAttribute(startup_info_.lpAttributeList, 0, + attribute, value, size, nullptr, + nullptr); +} + +} // namespace win +} // namespace base diff --git a/security/sandbox/chromium/base/win/startup_information.h b/security/sandbox/chromium/base/win/startup_information.h new file mode 100644 index 0000000000..7ef6965dd5 --- /dev/null +++ b/security/sandbox/chromium/base/win/startup_information.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012 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. + +#ifndef BASE_WIN_STARTUP_INFORMATION_H_ +#define BASE_WIN_STARTUP_INFORMATION_H_ + +#include + +#include + +#include + +#include "base/base_export.h" +#include "base/macros.h" + +namespace base { +namespace win { + +// Manages the lifetime of additional attributes in STARTUPINFOEX. +class BASE_EXPORT StartupInformation { + public: + StartupInformation(); + + ~StartupInformation(); + + // Initialize the attribute list for the specified number of entries. + bool InitializeProcThreadAttributeList(DWORD attribute_count); + + // Sets one entry in the initialized attribute list. + // |value| needs to live at least as long as the StartupInformation object + // this is called on. + bool UpdateProcThreadAttribute(DWORD_PTR attribute, void* value, size_t size); + + LPSTARTUPINFOW startup_info() { return &startup_info_.StartupInfo; } + LPSTARTUPINFOW startup_info() const { + return const_cast(&startup_info_.StartupInfo); + } + + bool has_extended_startup_info() const { + return !!startup_info_.lpAttributeList; + } + + private: + std::unique_ptr attribute_list_; + STARTUPINFOEXW startup_info_; + DISALLOW_COPY_AND_ASSIGN(StartupInformation); +}; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_STARTUP_INFORMATION_H_ diff --git a/security/sandbox/chromium/base/win/static_constants.cc b/security/sandbox/chromium/base/win/static_constants.cc new file mode 100644 index 0000000000..f9894a22bd --- /dev/null +++ b/security/sandbox/chromium/base/win/static_constants.cc @@ -0,0 +1,13 @@ +// Copyright 2019 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/win/static_constants.h" + +namespace base { +namespace win { + +const char kApplicationVerifierDllName[] = "verifier.dll"; + +} // namespace win +} // namespace base diff --git a/security/sandbox/chromium/base/win/static_constants.h b/security/sandbox/chromium/base/win/static_constants.h new file mode 100644 index 0000000000..98a631f7cf --- /dev/null +++ b/security/sandbox/chromium/base/win/static_constants.h @@ -0,0 +1,21 @@ +// Copyright 2019 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. + +// Defines constants needed before imports (like base.dll) are fully resolved. +// For example, constants defined here can be used by interceptions (i.e. hooks) +// in the sandbox, which run before imports are resolved, and can therefore only +// reference static variables. + +#ifndef BASE_WIN_STATIC_CONSTANTS_H_ +#define BASE_WIN_STATIC_CONSTANTS_H_ + +namespace base { +namespace win { + +extern const char kApplicationVerifierDllName[]; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_STATIC_CONSTANTS_H_ diff --git a/security/sandbox/chromium/base/win/windows_types.h b/security/sandbox/chromium/base/win/windows_types.h new file mode 100644 index 0000000000..9be05f3c25 --- /dev/null +++ b/security/sandbox/chromium/base/win/windows_types.h @@ -0,0 +1,278 @@ +// Copyright (c) 2014 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. + +// This file contains defines and typedefs that allow popular Windows types to +// be used without the overhead of including windows.h. + +#ifndef BASE_WIN_WINDOWS_TYPES_H +#define BASE_WIN_WINDOWS_TYPES_H + +// Needed for function prototypes. +#if defined(__MINGW32__) +// MinGW doesn't have this file yet, but we only need this define. +// Bug 1552706 tracks removing this and the one below. +#define _Releases_exclusive_lock_(lock) +// MinGW doesn't appear to have this in specstrings.h either. +#define _Post_equals_last_error_ +#else +#include +#endif +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// typedef and define the most commonly used Windows integer types. + +typedef unsigned long DWORD; +typedef long LONG; +typedef __int64 LONGLONG; +typedef unsigned __int64 ULONGLONG; + +#define VOID void +typedef char CHAR; +typedef short SHORT; +typedef long LONG; +typedef int INT; +typedef unsigned int UINT; +typedef unsigned int* PUINT; +typedef void* LPVOID; +typedef void* PVOID; +typedef void* HANDLE; +typedef int BOOL; +typedef unsigned char BYTE; +typedef BYTE BOOLEAN; +typedef DWORD ULONG; +typedef unsigned short WORD; +typedef WORD UWORD; +typedef WORD ATOM; + +#if defined(_WIN64) +typedef __int64 INT_PTR, *PINT_PTR; +typedef unsigned __int64 UINT_PTR, *PUINT_PTR; + +typedef __int64 LONG_PTR, *PLONG_PTR; +typedef unsigned __int64 ULONG_PTR, *PULONG_PTR; +#else +typedef __w64 int INT_PTR, *PINT_PTR; +typedef __w64 unsigned int UINT_PTR, *PUINT_PTR; + +typedef __w64 long LONG_PTR, *PLONG_PTR; +typedef __w64 unsigned long ULONG_PTR, *PULONG_PTR; +#endif + +typedef UINT_PTR WPARAM; +typedef LONG_PTR LPARAM; +typedef LONG_PTR LRESULT; +#define LRESULT LONG_PTR +typedef _Return_type_success_(return >= 0) long HRESULT; + +typedef ULONG_PTR SIZE_T, *PSIZE_T; +typedef LONG_PTR SSIZE_T, *PSSIZE_T; + +typedef DWORD ACCESS_MASK; +typedef ACCESS_MASK REGSAM; + +// As defined in guiddef.h. +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID& +#endif + +// Forward declare Windows compatible handles. + +#define CHROME_DECLARE_HANDLE(name) \ + struct name##__; \ + typedef struct name##__* name +CHROME_DECLARE_HANDLE(HDESK); +CHROME_DECLARE_HANDLE(HGLRC); +CHROME_DECLARE_HANDLE(HICON); +CHROME_DECLARE_HANDLE(HINSTANCE); +CHROME_DECLARE_HANDLE(HKEY); +CHROME_DECLARE_HANDLE(HKL); +CHROME_DECLARE_HANDLE(HMENU); +CHROME_DECLARE_HANDLE(HWINSTA); +CHROME_DECLARE_HANDLE(HWND); +#undef CHROME_DECLARE_HANDLE + +typedef LPVOID HINTERNET; +typedef HINSTANCE HMODULE; +typedef PVOID LSA_HANDLE; + +// Forward declare some Windows struct/typedef sets. + +typedef struct _OVERLAPPED OVERLAPPED; +typedef struct tagMSG MSG, *PMSG, *NPMSG, *LPMSG; + +typedef struct _RTL_SRWLOCK RTL_SRWLOCK; +typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; + +typedef struct _GUID GUID; +typedef GUID CLSID; + +typedef struct tagLOGFONTW LOGFONTW, *PLOGFONTW, *NPLOGFONTW, *LPLOGFONTW; +typedef LOGFONTW LOGFONT; + +typedef struct _FILETIME FILETIME; + +typedef struct tagMENUITEMINFOW MENUITEMINFOW, MENUITEMINFO; + +typedef struct tagNMHDR NMHDR; + +typedef PVOID PSID; + +// Declare Chrome versions of some Windows structures. These are needed for +// when we need a concrete type but don't want to pull in Windows.h. We can't +// declare the Windows types so we declare our types and cast to the Windows +// types in a few places. + +struct CHROME_SRWLOCK { + PVOID Ptr; +}; + +struct CHROME_CONDITION_VARIABLE { + PVOID Ptr; +}; + +// Define some commonly used Windows constants. Note that the layout of these +// macros - including internal spacing - must be 100% consistent with windows.h. + +// clang-format off + +#ifndef INVALID_HANDLE_VALUE +// Work around there being two slightly different definitions in the SDK. +#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) +#endif +#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) +#define HTNOWHERE 0 +#define MAX_PATH 260 +#define CS_GLOBALCLASS 0x4000 + +#define ERROR_SUCCESS 0L +#define ERROR_FILE_NOT_FOUND 2L +#define ERROR_ACCESS_DENIED 5L +#define ERROR_INVALID_HANDLE 6L +#define ERROR_SHARING_VIOLATION 32L +#define ERROR_LOCK_VIOLATION 33L +#define REG_BINARY ( 3ul ) + +#define STATUS_PENDING ((DWORD )0x00000103L) +#define STILL_ACTIVE STATUS_PENDING +#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) +#define FAILED(hr) (((HRESULT)(hr)) < 0) + +#define HKEY_CLASSES_ROOT (( HKEY ) (ULONG_PTR)((LONG)0x80000000) ) +#define HKEY_LOCAL_MACHINE (( HKEY ) (ULONG_PTR)((LONG)0x80000002) ) +#define HKEY_CURRENT_USER (( HKEY ) (ULONG_PTR)((LONG)0x80000001) ) +#define KEY_QUERY_VALUE (0x0001) +#define KEY_SET_VALUE (0x0002) +#define KEY_CREATE_SUB_KEY (0x0004) +#define KEY_ENUMERATE_SUB_KEYS (0x0008) +#define KEY_NOTIFY (0x0010) +#define KEY_CREATE_LINK (0x0020) +#define KEY_WOW64_32KEY (0x0200) +#define KEY_WOW64_64KEY (0x0100) +#define KEY_WOW64_RES (0x0300) + +#define READ_CONTROL (0x00020000L) +#define SYNCHRONIZE (0x00100000L) + +#define STANDARD_RIGHTS_READ (READ_CONTROL) +#define STANDARD_RIGHTS_WRITE (READ_CONTROL) +#define STANDARD_RIGHTS_ALL (0x001F0000L) + +#define KEY_READ ((STANDARD_RIGHTS_READ |\ + KEY_QUERY_VALUE |\ + KEY_ENUMERATE_SUB_KEYS |\ + KEY_NOTIFY) \ + & \ + (~SYNCHRONIZE)) + + +#define KEY_WRITE ((STANDARD_RIGHTS_WRITE |\ + KEY_SET_VALUE |\ + KEY_CREATE_SUB_KEY) \ + & \ + (~SYNCHRONIZE)) + +#define KEY_ALL_ACCESS ((STANDARD_RIGHTS_ALL |\ + KEY_QUERY_VALUE |\ + KEY_SET_VALUE |\ + KEY_CREATE_SUB_KEY |\ + KEY_ENUMERATE_SUB_KEYS |\ + KEY_NOTIFY |\ + KEY_CREATE_LINK) \ + & \ + (~SYNCHRONIZE)) + +// clang-format on + +// Define some macros needed when prototyping Windows functions. + +#define DECLSPEC_IMPORT __declspec(dllimport) +#define WINBASEAPI DECLSPEC_IMPORT +#define WINUSERAPI DECLSPEC_IMPORT +#define WINAPI __stdcall +#define CALLBACK __stdcall + +// Needed for optimal lock performance. +WINBASEAPI _Releases_exclusive_lock_(*SRWLock) VOID WINAPI + ReleaseSRWLockExclusive(_Inout_ PSRWLOCK SRWLock); + +// Needed to support protobuf's GetMessage macro magic. +WINUSERAPI BOOL WINAPI GetMessageW(_Out_ LPMSG lpMsg, + _In_opt_ HWND hWnd, + _In_ UINT wMsgFilterMin, + _In_ UINT wMsgFilterMax); + +// Needed for thread_local_storage.h +WINBASEAPI LPVOID WINAPI TlsGetValue(_In_ DWORD dwTlsIndex); + +// Needed for scoped_handle.h +WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI + GetLastError(VOID); + +WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode); + +#ifdef __cplusplus +} +#endif + +// These macros are all defined by windows.h and are also used as the names of +// functions in the Chromium code base. Add to this list as needed whenever +// there is a Windows macro which causes a function call to be renamed. This +// ensures that the same renaming will happen everywhere. Includes of this file +// can be added wherever needed to ensure this consistent renaming. + +#define CopyFile CopyFileW +#define CreateDirectory CreateDirectoryW +#define CreateEvent CreateEventW +#define CreateFile CreateFileW +#define CreateService CreateServiceW +#define DeleteFile DeleteFileW +#define DispatchMessage DispatchMessageW +#define DrawText DrawTextW +#define FindFirstFile FindFirstFileW +#define FindNextFile FindNextFileW +#define GetComputerName GetComputerNameW +#define GetCurrentDirectory GetCurrentDirectoryW +#define GetCurrentTime() GetTickCount() +#define GetFileAttributes GetFileAttributesW +#define GetMessage GetMessageW +#define GetUserName GetUserNameW +#define LoadIcon LoadIconW +#define LoadImage LoadImageW +#define PostMessage PostMessageW +#define RemoveDirectory RemoveDirectoryW +#define ReplaceFile ReplaceFileW +#define ReportEvent ReportEventW +#define SendMessage SendMessageW +#define SendMessageCallback SendMessageCallbackW +#define SetCurrentDirectory SetCurrentDirectoryW +#define StartService StartServiceW +#define UpdateResource UpdateResourceW + +#endif // BASE_WIN_WINDOWS_TYPES_H diff --git a/security/sandbox/chromium/base/win/windows_version.cc b/security/sandbox/chromium/base/win/windows_version.cc new file mode 100644 index 0000000000..ef96f8796f --- /dev/null +++ b/security/sandbox/chromium/base/win/windows_version.cc @@ -0,0 +1,313 @@ +// Copyright (c) 2012 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/win/windows_version.h" + +#include + +#include +#include +#include + +#include "base/file_version_info_win.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/win/registry.h" + +#if !defined(__clang__) && _MSC_FULL_VER < 191125507 +#error VS 2017 Update 3.2 or higher is required +#endif + +#if !defined(NTDDI_WIN10_RS4) +#error Windows 10.0.17134.0 SDK or higher required. +#endif + +namespace base { +namespace win { + +namespace { + +// The values under the CurrentVersion registry hive are mirrored under +// the corresponding Wow6432 hive. +constexpr wchar_t kRegKeyWindowsNTCurrentVersion[] = + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; + +// Returns the "UBR" (Windows 10 patch number) and "ReleaseId" (Windows 10 +// release number) from the registry. "UBR" is an undocumented value and will be +// 0 if the value was not found. "ReleaseId" will be an empty string if the +// value is not found. +std::pair GetVersionData() { + DWORD ubr = 0; + std::wstring release_id; + RegKey key; + + if (key.Open(HKEY_LOCAL_MACHINE, kRegKeyWindowsNTCurrentVersion, + KEY_QUERY_VALUE) == ERROR_SUCCESS) { + key.ReadValueDW(L"UBR", &ubr); + key.ReadValue(L"ReleaseId", &release_id); + } + + return std::make_pair(static_cast(ubr), WideToUTF8(release_id)); +} + +const _SYSTEM_INFO& GetSystemInfoStorage() { + static const NoDestructor<_SYSTEM_INFO> system_info([] { + _SYSTEM_INFO info = {}; + ::GetNativeSystemInfo(&info); + return info; + }()); + return *system_info; +} + +} // namespace + +// static +OSInfo** OSInfo::GetInstanceStorage() { + // Note: we don't use the Singleton class because it depends on AtExitManager, + // and it's convenient for other modules to use this class without it. + static OSInfo* info = []() { + _OSVERSIONINFOEXW version_info = {sizeof(version_info)}; + ::GetVersionEx(reinterpret_cast<_OSVERSIONINFOW*>(&version_info)); + + DWORD os_type = 0; + ::GetProductInfo(version_info.dwMajorVersion, version_info.dwMinorVersion, + 0, 0, &os_type); + + return new OSInfo(version_info, GetSystemInfoStorage(), os_type); + }(); + + return &info; +} + +// static +OSInfo* OSInfo::GetInstance() { + return *GetInstanceStorage(); +} + +// static +OSInfo::WindowsArchitecture OSInfo::GetArchitecture() { + switch (GetSystemInfoStorage().wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_INTEL: + return X86_ARCHITECTURE; + case PROCESSOR_ARCHITECTURE_AMD64: + return X64_ARCHITECTURE; + case PROCESSOR_ARCHITECTURE_IA64: + return IA64_ARCHITECTURE; + case PROCESSOR_ARCHITECTURE_ARM64: + return ARM64_ARCHITECTURE; + default: + return OTHER_ARCHITECTURE; + } +} + +OSInfo::OSInfo(const _OSVERSIONINFOEXW& version_info, + const _SYSTEM_INFO& system_info, + int os_type) + : version_(Version::PRE_XP), + wow64_status_(GetWOW64StatusForProcess(GetCurrentProcess())) { + version_number_.major = version_info.dwMajorVersion; + version_number_.minor = version_info.dwMinorVersion; + version_number_.build = version_info.dwBuildNumber; + std::tie(version_number_.patch, release_id_) = GetVersionData(); + version_ = MajorMinorBuildToVersion( + version_number_.major, version_number_.minor, version_number_.build); + service_pack_.major = version_info.wServicePackMajor; + service_pack_.minor = version_info.wServicePackMinor; + service_pack_str_ = WideToUTF8(version_info.szCSDVersion); + + processors_ = system_info.dwNumberOfProcessors; + allocation_granularity_ = system_info.dwAllocationGranularity; + + if (version_info.dwMajorVersion == 6 || version_info.dwMajorVersion == 10) { + // Only present on Vista+. + switch (os_type) { + case PRODUCT_CLUSTER_SERVER: + case PRODUCT_DATACENTER_SERVER: + case PRODUCT_DATACENTER_SERVER_CORE: + case PRODUCT_ENTERPRISE_SERVER: + case PRODUCT_ENTERPRISE_SERVER_CORE: + case PRODUCT_ENTERPRISE_SERVER_IA64: + case PRODUCT_SMALLBUSINESS_SERVER: + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + case PRODUCT_STANDARD_SERVER: + case PRODUCT_STANDARD_SERVER_CORE: + case PRODUCT_WEB_SERVER: + version_type_ = SUITE_SERVER; + break; + case PRODUCT_PROFESSIONAL: + case PRODUCT_ULTIMATE: + version_type_ = SUITE_PROFESSIONAL; + break; + case PRODUCT_ENTERPRISE: + case PRODUCT_ENTERPRISE_E: + case PRODUCT_ENTERPRISE_EVALUATION: + case PRODUCT_ENTERPRISE_N: + case PRODUCT_ENTERPRISE_N_EVALUATION: + case PRODUCT_ENTERPRISE_S: + case PRODUCT_ENTERPRISE_S_EVALUATION: + case PRODUCT_ENTERPRISE_S_N: + case PRODUCT_ENTERPRISE_S_N_EVALUATION: + case PRODUCT_BUSINESS: + case PRODUCT_BUSINESS_N: + version_type_ = SUITE_ENTERPRISE; + break; + case PRODUCT_EDUCATION: + case PRODUCT_EDUCATION_N: + version_type_ = SUITE_EDUCATION; + break; + case PRODUCT_HOME_BASIC: + case PRODUCT_HOME_PREMIUM: + case PRODUCT_STARTER: + default: + version_type_ = SUITE_HOME; + break; + } + } else if (version_info.dwMajorVersion == 5 && + version_info.dwMinorVersion == 2) { + if (version_info.wProductType == VER_NT_WORKSTATION && + system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + version_type_ = SUITE_PROFESSIONAL; + } else if (version_info.wSuiteMask & VER_SUITE_WH_SERVER) { + version_type_ = SUITE_HOME; + } else { + version_type_ = SUITE_SERVER; + } + } else if (version_info.dwMajorVersion == 5 && + version_info.dwMinorVersion == 1) { + if (version_info.wSuiteMask & VER_SUITE_PERSONAL) + version_type_ = SUITE_HOME; + else + version_type_ = SUITE_PROFESSIONAL; + } else { + // Windows is pre XP so we don't care but pick a safe default. + version_type_ = SUITE_HOME; + } +} + +OSInfo::~OSInfo() = default; + +Version OSInfo::Kernel32Version() const { + static const Version kernel32_version = + MajorMinorBuildToVersion(Kernel32BaseVersion().components()[0], + Kernel32BaseVersion().components()[1], + Kernel32BaseVersion().components()[2]); + return kernel32_version; +} + +Version OSInfo::UcrtVersion() const { + auto ucrt_version_info = FileVersionInfoWin::CreateFileVersionInfoWin( + FilePath(FILE_PATH_LITERAL("ucrtbase.dll"))); + if (ucrt_version_info) { + auto ucrt_components = ucrt_version_info->GetFileVersion().components(); + if (ucrt_components.size() == 4) { + return MajorMinorBuildToVersion(ucrt_components[0], ucrt_components[1], + ucrt_components[2]); + } + } + return Version(); +} + +// Retrieve a version from kernel32. This is useful because when running in +// compatibility mode for a down-level version of the OS, the file version of +// kernel32 will still be the "real" version. +base::Version OSInfo::Kernel32BaseVersion() const { + static const NoDestructor version([] { + std::unique_ptr file_version_info = + FileVersionInfoWin::CreateFileVersionInfoWin( + FilePath(FILE_PATH_LITERAL("kernel32.dll"))); + if (!file_version_info) { + // crbug.com/912061: on some systems it seems kernel32.dll might be + // corrupted or not in a state to get version info. In this case try + // kernelbase.dll as a fallback. + file_version_info = FileVersionInfoWin::CreateFileVersionInfoWin( + FilePath(FILE_PATH_LITERAL("kernelbase.dll"))); + } + CHECK(file_version_info); + return file_version_info->GetFileVersion(); + }()); + return *version; +} + +std::string OSInfo::processor_model_name() { + if (processor_model_name_.empty()) { + const wchar_t kProcessorNameString[] = + L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; + RegKey key(HKEY_LOCAL_MACHINE, kProcessorNameString, KEY_READ); + std::wstring value; + key.ReadValue(L"ProcessorNameString", &value); + processor_model_name_ = WideToUTF8(value); + } + return processor_model_name_; +} + +// static +OSInfo::WOW64Status OSInfo::GetWOW64StatusForProcess(HANDLE process_handle) { + BOOL is_wow64 = FALSE; + if (!::IsWow64Process(process_handle, &is_wow64)) + return WOW64_UNKNOWN; + return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED; +} + +// With the exception of Server 2003, server variants are treated the same as +// the corresponding workstation release. +// static +Version OSInfo::MajorMinorBuildToVersion(int major, int minor, int build) { + if (major == 10) { + if (build >= 19041) + return Version::WIN10_20H1; + if (build >= 18362) + return Version::WIN10_19H1; + if (build >= 17763) + return Version::WIN10_RS5; + if (build >= 17134) + return Version::WIN10_RS4; + if (build >= 16299) + return Version::WIN10_RS3; + if (build >= 15063) + return Version::WIN10_RS2; + if (build >= 14393) + return Version::WIN10_RS1; + if (build >= 10586) + return Version::WIN10_TH2; + return Version::WIN10; + } + + if (major > 6) { + // Hitting this likely means that it's time for a >10 block above. + NOTREACHED() << major << "." << minor << "." << build; + return Version::WIN_LAST; + } + + if (major == 6) { + switch (minor) { + case 0: + return Version::VISTA; + case 1: + return Version::WIN7; + case 2: + return Version::WIN8; + default: + DCHECK_EQ(minor, 3); + return Version::WIN8_1; + } + } + + if (major == 5 && minor != 0) { + // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003. + return minor == 1 ? Version::XP : Version::SERVER_2003; + } + + // Win 2000 or older. + return Version::PRE_XP; +} + +Version GetVersion() { + return OSInfo::GetInstance()->version(); +} + +} // namespace win +} // namespace base diff --git a/security/sandbox/chromium/base/win/windows_version.h b/security/sandbox/chromium/base/win/windows_version.h new file mode 100644 index 0000000000..08d6e274ef --- /dev/null +++ b/security/sandbox/chromium/base/win/windows_version.h @@ -0,0 +1,187 @@ +// Copyright (c) 2012 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. + +#ifndef BASE_WIN_WINDOWS_VERSION_H_ +#define BASE_WIN_WINDOWS_VERSION_H_ + +#include + +#include + +#include "base/base_export.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/version.h" + +using HANDLE = void*; +struct _OSVERSIONINFOEXW; +struct _SYSTEM_INFO; + +namespace base { +namespace test { +class ScopedOSInfoOverride; +} // namespace test +} // namespace base + +namespace base { +namespace win { + +// The running version of Windows. This is declared outside OSInfo for +// syntactic sugar reasons; see the declaration of GetVersion() below. +// NOTE: Keep these in order so callers can do things like +// "if (base::win::GetVersion() >= base::win::Version::VISTA) ...". +// +// This enum is used in metrics histograms, so they shouldn't be reordered or +// removed. New values can be added before Version::WIN_LAST. +enum class Version { + PRE_XP = 0, // Not supported. + XP = 1, + SERVER_2003 = 2, // Also includes XP Pro x64 and Server 2003 R2. + VISTA = 3, // Also includes Windows Server 2008. + WIN7 = 4, // Also includes Windows Server 2008 R2. + WIN8 = 5, // Also includes Windows Server 2012. + WIN8_1 = 6, // Also includes Windows Server 2012 R2. + WIN10 = 7, // Threshold 1: Version 1507, Build 10240. + WIN10_TH2 = 8, // Threshold 2: Version 1511, Build 10586. + WIN10_RS1 = 9, // Redstone 1: Version 1607, Build 14393. + WIN10_RS2 = 10, // Redstone 2: Version 1703, Build 15063. + WIN10_RS3 = 11, // Redstone 3: Version 1709, Build 16299. + WIN10_RS4 = 12, // Redstone 4: Version 1803, Build 17134. + WIN10_RS5 = 13, // Redstone 5: Version 1809, Build 17763. + WIN10_19H1 = 14, // 19H1: Version 1903, Build 18362. + WIN10_20H1 = 15, // 20H1: Version 2004, Build 19041. + // On edit, update tools\metrics\histograms\enums.xml "WindowsVersion" and + // "GpuBlacklistFeatureTestResultsWindows2". + WIN_LAST, // Indicates error condition. +}; + +// A rough bucketing of the available types of versions of Windows. This is used +// to distinguish enterprise enabled versions from home versions and potentially +// server versions. Keep these values in the same order, since they are used as +// is for metrics histogram ids. +enum VersionType { + SUITE_HOME = 0, + SUITE_PROFESSIONAL, + SUITE_SERVER, + SUITE_ENTERPRISE, + SUITE_EDUCATION, + SUITE_LAST, +}; + +// A singleton that can be used to query various pieces of information about the +// OS and process state. Note that this doesn't use the base Singleton class, so +// it can be used without an AtExitManager. +class BASE_EXPORT OSInfo { + public: + struct VersionNumber { + int major; + int minor; + int build; + int patch; + }; + + struct ServicePack { + int major; + int minor; + }; + + // The processor architecture this copy of Windows natively uses. For + // example, given an x64-capable processor, we have three possibilities: + // 32-bit Chrome running on 32-bit Windows: X86_ARCHITECTURE + // 32-bit Chrome running on 64-bit Windows via WOW64: X64_ARCHITECTURE + // 64-bit Chrome running on 64-bit Windows: X64_ARCHITECTURE + enum WindowsArchitecture { + X86_ARCHITECTURE, + X64_ARCHITECTURE, + IA64_ARCHITECTURE, + ARM64_ARCHITECTURE, + OTHER_ARCHITECTURE, + }; + + // Whether a process is running under WOW64 (the wrapper that allows 32-bit + // processes to run on 64-bit versions of Windows). This will return + // WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit + // Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g. + // the process does not have sufficient access rights to determine this. + enum WOW64Status { + WOW64_DISABLED, + WOW64_ENABLED, + WOW64_UNKNOWN, + }; + + static OSInfo* GetInstance(); + + // Separate from the rest of OSInfo so it can be used during early process + // initialization. + static WindowsArchitecture GetArchitecture(); + + // Like wow64_status(), but for the supplied handle instead of the current + // process. This doesn't touch member state, so you can bypass the singleton. + static WOW64Status GetWOW64StatusForProcess(HANDLE process_handle); + + const Version& version() const { return version_; } + Version Kernel32Version() const; + Version UcrtVersion() const; + base::Version Kernel32BaseVersion() const; + // The next two functions return arrays of values, [major, minor(, build)]. + const VersionNumber& version_number() const { return version_number_; } + const VersionType& version_type() const { return version_type_; } + const ServicePack& service_pack() const { return service_pack_; } + const std::string& service_pack_str() const { return service_pack_str_; } + const int& processors() const { return processors_; } + const size_t& allocation_granularity() const { + return allocation_granularity_; + } + const WOW64Status& wow64_status() const { return wow64_status_; } + std::string processor_model_name(); + const std::string& release_id() const { return release_id_; } + + private: + friend class base::test::ScopedOSInfoOverride; + FRIEND_TEST_ALL_PREFIXES(OSInfo, MajorMinorBuildToVersion); + static OSInfo** GetInstanceStorage(); + + OSInfo(const _OSVERSIONINFOEXW& version_info, + const _SYSTEM_INFO& system_info, + int os_type); + ~OSInfo(); + + // Returns a Version value for a given OS version tuple. + static Version MajorMinorBuildToVersion(int major, int minor, int build); + + Version version_; + VersionNumber version_number_; + VersionType version_type_; + ServicePack service_pack_; + + // Represents the version of the OS associated to a release of + // Windows 10. Each version may have different releases (such as patch + // updates). This is the identifier of the release. + // Example: + // Windows 10 Version 1809 (OS build 17763) has multiple releases + // (i.e. build 17763.1, build 17763.195, build 17763.379, ...). + // See https://docs.microsoft.com/en-us/windows/windows-10/release-information + // for more information. + std::string release_id_; + + // A string, such as "Service Pack 3", that indicates the latest Service Pack + // installed on the system. If no Service Pack has been installed, the string + // is empty. + std::string service_pack_str_; + int processors_; + size_t allocation_granularity_; + WOW64Status wow64_status_; + std::string processor_model_name_; + + DISALLOW_COPY_AND_ASSIGN(OSInfo); +}; + +// Because this is by far the most commonly-requested value from the above +// singleton, we add a global-scope accessor here as syntactic sugar. +BASE_EXPORT Version GetVersion(); + +} // namespace win +} // namespace base + +#endif // BASE_WIN_WINDOWS_VERSION_H_ -- cgit v1.2.3