diff options
Diffstat (limited to 'browser/app/winlauncher/freestanding/SharedSection.h')
-rw-r--r-- | browser/app/winlauncher/freestanding/SharedSection.h | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/browser/app/winlauncher/freestanding/SharedSection.h b/browser/app/winlauncher/freestanding/SharedSection.h new file mode 100644 index 0000000000..54e89a2325 --- /dev/null +++ b/browser/app/winlauncher/freestanding/SharedSection.h @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_freestanding_SharedSection_h +#define mozilla_freestanding_SharedSection_h + +#include "mozilla/DynamicBlocklist.h" +#include "mozilla/glue/SharedSection.h" +#include "mozilla/NativeNt.h" +#include "mozilla/interceptor/MMPolicies.h" + +// clang-format off +#define MOZ_LITERAL_UNICODE_STRING(s) \ + { \ + /* Length of the string in bytes, less the null terminator */ \ + sizeof(s) - sizeof(wchar_t), \ + /* Length of the string in bytes, including the null terminator */ \ + sizeof(s), \ + /* Pointer to the buffer */ \ + const_cast<wchar_t*>(s) \ + } +// clang-format on + +namespace mozilla { +namespace freestanding { +class SharedSectionTestHelper; + +struct DllBlockInfoComparator { + explicit DllBlockInfoComparator(const UNICODE_STRING& aTarget) + : mTarget(&aTarget) {} + + int operator()(const DllBlockInfo& aVal) const { + return static_cast<int>( + ::RtlCompareUnicodeString(mTarget, &aVal.mName, TRUE)); + } + + PCUNICODE_STRING mTarget; +}; + +// This class calculates RVAs of kernel32's functions and transfers them +// to a target process, where the transferred RVAs are resolved into +// function addresses so that the target process can use them after +// kernel32.dll is loaded and before IAT is resolved. +struct MOZ_TRIVIAL_CTOR_DTOR Kernel32ExportsSolver final + : interceptor::MMPolicyInProcessEarlyStage::Kernel32Exports { + void Init(); + bool Resolve(); +}; + +// This class manages a section which is created in the launcher process and +// mapped in the browser process and the sandboxed processes. The section's +// layout is represented as SharedSection::Layout. +// +// (1) Kernel32's functions required for MMPolicyInProcessEarlyStage +// Formatted as Kernel32ExportsSolver. +// +// (2) Various flags and offsets +// +// (3) Entries in the dynamic blocklist, in DllBlockInfo format. There +// are mNumBlockEntries of these, followed by one that has mName.Length +// of 0. Note that the strings that contain +// the names of the entries in the blocklist are stored concatenated +// after the last entry. The mName pointers in each DllBlockInfo point +// to these strings correctly in Resolve(), so clients don't need +// to do anything special to read these strings. +// +// (4) Array of NT paths of the executable's dependent modules +// Formatted as a null-delimited wide-character string set ending with +// an empty string. These entries start at offset +// mDependentModulePathArrayStart (in bytes) from the beginning +// of the structure +// +// +--------------------------------------------------------------+ +// | (1) | FlushInstructionCache | +// | | GetModuleHandleW | +// | | GetSystemInfo | +// | | VirtualProtect | +// | | State [kUninitialized|kInitialized|kResolved] | +// +--------------------------------------------------------------+ +// | (2) | (flags and offsets) | +// +--------------------------------------------------------------+ +// | (3) | <DllBlockInfo for first entry in dynamic blocklist> | +// | | <DllBlockInfo for second entry in dynamic blocklist> | +// | | ... | +// | | <DllBlockInfo for last entry in dynamic blocklist> | +// | | <DllBlockInfo with mName.Length of 0> | +// | | L"string1.dllstring2.dll...stringlast.dll" | +// +--------------------------------------------------------------+ +// | (4) | L"NT path 1" | +// | | L"NT path 2" | +// | | ... | +// | | L"" | +// +--------------------------------------------------------------+ +class MOZ_TRIVIAL_CTOR_DTOR SharedSection final : public nt::SharedSection { + struct Layout final { + enum class State { + kUninitialized, + kInitialized, + kLoadedDynamicBlocklistEntries, + kResolved, + } mState; + + Kernel32ExportsSolver mK32Exports; + // 1 if the blocklist is disabled, 0 otherwise. + // If the blocklist is disabled, the entries are still loaded to make it + // easy for the user to remove any they don't want, but none of the DLLs + // here are actually blocked. + // Stored as a uint32_t for alignment reasons. + uint32_t mBlocklistIsDisabled; + // The offset, in bytes, from the beginning of the Layout structure to the + // first dependent module entry. + // When the Layout object is created, this value is 0, indicating that no + // dependent modules have been added and it is safe to add DllBlockInfo + // entries. + // After this value is set to something non-0, no more DllBlockInfo entries + // can be added. + uint32_t mDependentModulePathArrayStart; + // The number of blocklist entries. + uint32_t mNumBlockEntries; + DllBlockInfo mFirstBlockEntry[1]; + + Span<DllBlockInfo> GetModulePathArray() { + return Span<DllBlockInfo>( + mFirstBlockEntry, + (kSharedViewSize - (reinterpret_cast<uintptr_t>(mFirstBlockEntry) - + reinterpret_cast<uintptr_t>(this))) / + sizeof(DllBlockInfo)); + } + // Can be used to make sure we don't step past the end of the shared memory + // section. + static constexpr uint32_t GetMaxNumBlockEntries() { + return (kSharedViewSize - (offsetof(Layout, mFirstBlockEntry))) / + sizeof(DllBlockInfo); + } + Layout() = delete; // disallow instantiation + bool Resolve(); + bool IsDisabled() const; + const DllBlockInfo* SearchBlocklist(const UNICODE_STRING& aLeafName) const; + Span<wchar_t> GetDependentModules(); + }; + + // As we define a global variable of this class and use it in our blocklist + // which is excuted in a process's early stage. If we have a complex dtor, + // the static initializer tries to register that dtor with onexit() of + // ucrtbase.dll which is not loaded yet, resulting in crash. Thus, we have + // a raw handle and a pointer as a static variable and manually release them + // by calling Reset() where possible. + static HANDLE sSectionHandle; + static Layout* sWriteCopyView; + static RTL_RUN_ONCE sEnsureOnce; + + // The sLock lock guarantees that while it is held, sSectionHandle will not + // change nor get closed, sEnsureOnce will not get reinitialized, and + // sWriteCopyView will not change nor get unmapped once initialized. We take + // sLock on paths that could run concurrently with ConvertToReadOnly(). This + // method is only called on the main process, and very early, so the only + // real risk here should be threads started by third-party products reaching + // our patched_NtMapViewOfSection (see bug 1850969). + static nt::SRWLock sLock; + + static ULONG NTAPI EnsureWriteCopyViewOnce(PRTL_RUN_ONCE, PVOID, PVOID*); + static Layout* EnsureWriteCopyView(bool requireKernel32Exports = false); + + static constexpr size_t kSharedViewSize = 0x1000; + + // For test use only + friend class SharedSectionTestHelper; + + public: + // Replace |sSectionHandle| with a given handle. + static void Reset(HANDLE aNewSectionObject = sSectionHandle); + + static inline nt::AutoSharedLock AutoNoReset() { + return nt::AutoSharedLock{sLock}; + } + + // Replace |sSectionHandle| with a new readonly handle. + static void ConvertToReadOnly(); + + // Create a new writable section and initialize the Kernel32ExportsSolver + // part. + static LauncherVoidResult Init(); + + // Append a new string to the |sSectionHandle| + static LauncherVoidResult AddDependentModule(PCUNICODE_STRING aNtPath); + static LauncherVoidResult SetBlocklist(const DynamicBlockList& aBlocklist, + bool isDisabled); + + // Map |sSectionHandle| to a copy-on-write page and return a writable pointer + // to each structure, or null if Layout failed to resolve exports. + Kernel32ExportsSolver* GetKernel32Exports(); + Maybe<Vector<const wchar_t*>> GetDependentModules() final override; + Span<const DllBlockInfo> GetDynamicBlocklist() final override; + + static bool IsDisabled(); + static const DllBlockInfo* SearchBlocklist(const UNICODE_STRING& aLeafName); + + // Transfer |sSectionHandle| to a process associated with |aTransferMgr|. + static LauncherVoidResult TransferHandle( + nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess, + HANDLE* aDestinationAddress = &sSectionHandle); +}; + +extern SharedSection gSharedSection; + +} // namespace freestanding +} // namespace mozilla + +#endif // mozilla_freestanding_SharedSection_h |