SharedSection.h (9400B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_freestanding_SharedSection_h 8 #define mozilla_freestanding_SharedSection_h 9 10 #include "mozilla/DynamicBlocklist.h" 11 #include "mozilla/glue/SharedSection.h" 12 #include "mozilla/NativeNt.h" 13 #include "mozilla/interceptor/MMPolicies.h" 14 15 // clang-format off 16 #define MOZ_LITERAL_UNICODE_STRING(s) \ 17 { \ 18 /* Length of the string in bytes, less the null terminator */ \ 19 sizeof(s) - sizeof(wchar_t), \ 20 /* Length of the string in bytes, including the null terminator */ \ 21 sizeof(s), \ 22 /* Pointer to the buffer */ \ 23 const_cast<wchar_t*>(s) \ 24 } 25 // clang-format on 26 27 namespace mozilla { 28 namespace freestanding { 29 class SharedSectionTestHelper; 30 31 struct DllBlockInfoComparator { 32 explicit DllBlockInfoComparator(const UNICODE_STRING& aTarget) 33 : mTarget(&aTarget) {} 34 35 int operator()(const DllBlockInfo& aVal) const { 36 return static_cast<int>( 37 ::RtlCompareUnicodeString(mTarget, &aVal.mName, TRUE)); 38 } 39 40 PCUNICODE_STRING mTarget; 41 }; 42 43 // This class calculates RVAs of kernel32's functions and transfers them 44 // to a target process, where the transferred RVAs are resolved into 45 // function addresses so that the target process can use them after 46 // kernel32.dll is loaded and before IAT is resolved. 47 struct MOZ_TRIVIAL_CTOR_DTOR Kernel32ExportsSolver final 48 : interceptor::MMPolicyInProcessEarlyStage::Kernel32Exports { 49 void Init(); 50 bool Resolve(); 51 }; 52 53 // This class manages a section which is created in the launcher process and 54 // mapped in the browser process and the sandboxed processes. The section's 55 // layout is represented as SharedSection::Layout. 56 // 57 // (1) Kernel32's functions required for MMPolicyInProcessEarlyStage 58 // Formatted as Kernel32ExportsSolver. 59 // 60 // (2) Various flags and offsets 61 // 62 // (3) Entries in the dynamic blocklist, in DllBlockInfo format. There 63 // are mNumBlockEntries of these, followed by one that has mName.Length 64 // of 0. Note that the strings that contain 65 // the names of the entries in the blocklist are stored concatenated 66 // after the last entry. The mName pointers in each DllBlockInfo point 67 // to these strings correctly in Resolve(), so clients don't need 68 // to do anything special to read these strings. 69 // 70 // (4) Array of NT paths of the executable's dependent modules 71 // Formatted as a null-delimited wide-character string set ending with 72 // an empty string. These entries start at offset 73 // mDependentModulePathArrayStart (in bytes) from the beginning 74 // of the structure 75 // 76 // +--------------------------------------------------------------+ 77 // | (1) | FlushInstructionCache | 78 // | | GetModuleHandleW | 79 // | | GetSystemInfo | 80 // | | VirtualProtect | 81 // | | State [kUninitialized|kInitialized|kResolved] | 82 // +--------------------------------------------------------------+ 83 // | (2) | (flags and offsets) | 84 // +--------------------------------------------------------------+ 85 // | (3) | <DllBlockInfo for first entry in dynamic blocklist> | 86 // | | <DllBlockInfo for second entry in dynamic blocklist> | 87 // | | ... | 88 // | | <DllBlockInfo for last entry in dynamic blocklist> | 89 // | | <DllBlockInfo with mName.Length of 0> | 90 // | | L"string1.dllstring2.dll...stringlast.dll" | 91 // +--------------------------------------------------------------+ 92 // | (4) | L"NT path 1" | 93 // | | L"NT path 2" | 94 // | | ... | 95 // | | L"" | 96 // +--------------------------------------------------------------+ 97 class MOZ_TRIVIAL_CTOR_DTOR SharedSection final : public nt::SharedSection { 98 struct Layout final { 99 enum class State { 100 kUninitialized, 101 kInitialized, 102 kLoadedDynamicBlocklistEntries, 103 kResolved, 104 } mState; 105 106 Kernel32ExportsSolver mK32Exports; 107 // 1 if the blocklist is disabled, 0 otherwise. 108 // If the blocklist is disabled, the entries are still loaded to make it 109 // easy for the user to remove any they don't want, but none of the DLLs 110 // here are actually blocked. 111 // Stored as a uint32_t for alignment reasons. 112 uint32_t mBlocklistIsDisabled; 113 // The offset, in bytes, from the beginning of the Layout structure to the 114 // first dependent module entry. 115 // When the Layout object is created, this value is 0, indicating that no 116 // dependent modules have been added and it is safe to add DllBlockInfo 117 // entries. 118 // After this value is set to something non-0, no more DllBlockInfo entries 119 // can be added. 120 uint32_t mDependentModulePathArrayStart; 121 // The number of blocklist entries. 122 uint32_t mNumBlockEntries; 123 DllBlockInfo mFirstBlockEntry[1]; 124 125 Span<DllBlockInfo> GetModulePathArray() { 126 return Span<DllBlockInfo>( 127 mFirstBlockEntry, 128 (kSharedViewSize - (reinterpret_cast<uintptr_t>(mFirstBlockEntry) - 129 reinterpret_cast<uintptr_t>(this))) / 130 sizeof(DllBlockInfo)); 131 } 132 // Can be used to make sure we don't step past the end of the shared memory 133 // section. 134 static constexpr uint32_t GetMaxNumBlockEntries() { 135 return (kSharedViewSize - (offsetof(Layout, mFirstBlockEntry))) / 136 sizeof(DllBlockInfo); 137 } 138 Layout() = delete; // disallow instantiation 139 bool Resolve(); 140 bool IsDisabled() const; 141 const DllBlockInfo* SearchBlocklist(const UNICODE_STRING& aLeafName) const; 142 Span<wchar_t> GetDependentModules(); 143 }; 144 145 // As we define a global variable of this class and use it in our blocklist 146 // which is excuted in a process's early stage. If we have a complex dtor, 147 // the static initializer tries to register that dtor with onexit() of 148 // ucrtbase.dll which is not loaded yet, resulting in crash. Thus, we have 149 // a raw handle and a pointer as a static variable and manually release them 150 // by calling Reset() where possible. 151 static HANDLE sSectionHandle; 152 static Layout* sWriteCopyView; 153 static RTL_RUN_ONCE sEnsureOnce; 154 155 // The sLock lock guarantees that while it is held, sSectionHandle will not 156 // change nor get closed, sEnsureOnce will not get reinitialized, and 157 // sWriteCopyView will not change nor get unmapped once initialized. We take 158 // sLock on paths that could run concurrently with ConvertToReadOnly(). This 159 // method is only called on the main process, and very early, so the only 160 // real risk here should be threads started by third-party products reaching 161 // our patched_NtMapViewOfSection (see bug 1850969). 162 static nt::SRWLock sLock; 163 164 static ULONG NTAPI EnsureWriteCopyViewOnce(PRTL_RUN_ONCE, PVOID, PVOID*); 165 static Layout* EnsureWriteCopyView(bool requireKernel32Exports = false); 166 167 static constexpr size_t kSharedViewSize = 0x1000; 168 169 // For test use only 170 friend class SharedSectionTestHelper; 171 172 public: 173 // Replace |sSectionHandle| with a given handle. 174 static void Reset(HANDLE aNewSectionObject = sSectionHandle); 175 176 static inline nt::AutoSharedLock AutoNoReset() { 177 return nt::AutoSharedLock{sLock}; 178 } 179 180 // Replace |sSectionHandle| with a new readonly handle. 181 static void ConvertToReadOnly(); 182 183 // Create a new writable section and initialize the Kernel32ExportsSolver 184 // part. 185 static LauncherVoidResult Init(); 186 187 // Append a new string to the |sSectionHandle| 188 static LauncherVoidResult AddDependentModule(PCUNICODE_STRING aNtPath); 189 static LauncherVoidResult SetBlocklist(const DynamicBlockList& aBlocklist, 190 bool isDisabled); 191 192 // Map |sSectionHandle| to a copy-on-write page and return a writable pointer 193 // to each structure, or null if Layout failed to resolve exports. 194 Kernel32ExportsSolver* GetKernel32Exports(); 195 Maybe<Vector<const wchar_t*>> GetDependentModules() final override; 196 Span<const DllBlockInfo> GetDynamicBlocklist() final override; 197 198 static bool IsDisabled(); 199 static const DllBlockInfo* SearchBlocklist(const UNICODE_STRING& aLeafName); 200 201 // Transfer |sSectionHandle| to a process associated with |aTransferMgr|. 202 static LauncherVoidResult TransferHandle( 203 nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess, 204 HANDLE* aDestinationAddress = &sSectionHandle); 205 }; 206 207 extern SharedSection gSharedSection; 208 209 } // namespace freestanding 210 } // namespace mozilla 211 212 #endif // mozilla_freestanding_SharedSection_h