Backed out changeset 5c7de47bcacb (bug 1927094) for causing Bug 1928689. a=backout
[gecko.git] / toolkit / xre / dllservices / DynamicBlocklist.h
bloba8db60d4ea2d201a88109aec2396e107a65e42d6
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/. */
7 #ifndef mozilla_DynamicBlocklist_h
8 #define mozilla_DynamicBlocklist_h
10 #include <winternl.h>
12 #include "nsWindowsHelpers.h"
13 #include "mozilla/CheckedInt.h"
14 #include "mozilla/NativeNt.h"
15 #include "mozilla/UniquePtr.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/Vector.h"
19 #if defined(MOZILLA_INTERNAL_API)
20 # include "mozilla/dom/Promise.h"
21 # include "nsIOutputStream.h"
22 # include "nsTHashSet.h"
23 #endif // defined(MOZILLA_INTERNAL_API)
25 #include "mozilla/WindowsDllBlocklistInfo.h"
27 #if !defined(MOZILLA_INTERNAL_API) && defined(ENABLE_TESTS)
28 # include "mozilla/CmdLineAndEnvUtils.h"
29 # define BLOCKLIST_INSERT_TEST_ENTRY
30 #endif // !defined(MOZILLA_INTERNAL_API) && defined(ENABLE_TESTS)
32 namespace mozilla {
33 using DllBlockInfo = DllBlockInfoT<UNICODE_STRING>;
35 struct DynamicBlockListBase {
36 static constexpr uint32_t kSignature = 'FFBL'; // Firefox Blocklist
37 static constexpr uint32_t kCurrentVersion = 1;
39 struct FileHeader {
40 uint32_t mSignature;
41 uint32_t mFileVersion;
42 uint32_t mPayloadSize;
45 // Define this class in a header so that TestCrossProcessWin
46 // can include and test it.
47 class DynamicBlockList final : public DynamicBlockListBase {
48 uint32_t mPayloadSize;
49 UniquePtr<uint8_t[]> mPayload;
51 #ifdef ENABLE_TESTS
52 // These two definitions are needed for the DynamicBlocklistWriter to avoid
53 // writing this test entry out to the blocklist file, so compile these in
54 // even if MOZILLA_INTERNAL_API is defined.
55 public:
56 static constexpr wchar_t kTestDll[] = L"TestDllBlocklist_UserBlocked.dll";
57 // kTestDll is null-terminated, but we don't want that for the blocklist
58 // file
59 static constexpr size_t kTestDllBytes = sizeof(kTestDll) - sizeof(wchar_t);
61 private:
62 # ifdef BLOCKLIST_INSERT_TEST_ENTRY
63 // Set up a test entry in the blocklist, used for testing purposes.
64 void AssignTestEntry(DllBlockInfo* testEntry, uint32_t nameOffset) {
65 testEntry->mName.Length = kTestDllBytes;
66 testEntry->mName.MaximumLength = kTestDllBytes;
67 testEntry->mName.Buffer = reinterpret_cast<PWSTR>(nameOffset);
68 testEntry->mMaxVersion = DllBlockInfo::ALL_VERSIONS;
69 testEntry->mFlags = DllBlockInfoFlags::FLAGS_DEFAULT;
72 void CreateListWithTestEntry() {
73 mPayloadSize = sizeof(DllBlockInfo) * 2 + kTestDllBytes;
74 mPayload = MakeUnique<uint8_t[]>(mPayloadSize);
75 DllBlockInfo* entry = reinterpret_cast<DllBlockInfo*>(mPayload.get());
76 AssignTestEntry(entry, sizeof(DllBlockInfo) * 2);
77 memcpy(mPayload.get() + sizeof(DllBlockInfo) * 2, kTestDll, kTestDllBytes);
78 ++entry;
79 entry->mName.Length = 0;
80 entry->mName.MaximumLength = 0;
82 # endif // BLOCKLIST_INSERT_TEST_ENTRY
83 #endif // ENABLE_TESTS
85 bool LoadFile(const wchar_t* aPath) {
86 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
87 const bool shouldInsertBlocklistTestEntry =
88 MOZ_UNLIKELY(mozilla::EnvHasValue("MOZ_DISABLE_NONLOCAL_CONNECTIONS") ||
89 mozilla::EnvHasValue("MOZ_RUN_GTEST"));
90 #endif
92 nsAutoHandle file(
93 ::CreateFileW(aPath, GENERIC_READ,
94 FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
95 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
96 if (!file) {
97 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
98 if (shouldInsertBlocklistTestEntry) {
99 // If the blocklist file doesn't exist, we still want to include a test
100 // entry for testing purposes.
101 CreateListWithTestEntry();
102 return true;
104 #endif
105 return false;
108 DWORD bytesRead = 0;
109 FileHeader header;
110 BOOL ok =
111 ::ReadFile(file.get(), &header, sizeof(header), &bytesRead, nullptr);
112 if (!ok) {
113 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
114 if (shouldInsertBlocklistTestEntry) {
115 // If we have a problem reading the blocklist file, we still want to
116 // include a test entry for testing purposes.
117 CreateListWithTestEntry();
118 return true;
120 #endif
121 return false;
123 if (bytesRead != sizeof(header)) {
124 return false;
127 if (header.mSignature != kSignature ||
128 header.mFileVersion != kCurrentVersion) {
129 return false;
132 uint32_t destinationPayloadSize = header.mPayloadSize;
133 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
134 if (shouldInsertBlocklistTestEntry) {
135 // Write an extra entry for testing purposes
136 destinationPayloadSize += sizeof(DllBlockInfo) + kTestDllBytes;
138 #endif
139 UniquePtr<uint8_t[]> payload =
140 MakeUnique<uint8_t[]>(destinationPayloadSize);
141 ok = ::ReadFile(file.get(), payload.get(), header.mPayloadSize, &bytesRead,
142 nullptr);
143 if (!ok || bytesRead != header.mPayloadSize) {
144 return false;
147 uint32_t sizeOfPayloadToIterateOver = header.mPayloadSize;
148 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
149 bool haveWrittenTestEntry = false;
150 UNICODE_STRING testUnicodeString;
151 // Cast the const-ness away since we're only going to use
152 // this to compare against strings.
153 testUnicodeString.Buffer = const_cast<PWSTR>(kTestDll);
154 testUnicodeString.Length = kTestDllBytes;
155 testUnicodeString.MaximumLength = kTestDllBytes;
156 #endif
157 for (uint32_t offset = 0; offset < sizeOfPayloadToIterateOver;
158 offset += sizeof(DllBlockInfo)) {
159 DllBlockInfo* entry =
160 reinterpret_cast<DllBlockInfo*>(payload.get() + offset);
161 if (!entry->mName.Length) {
162 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
163 if (shouldInsertBlocklistTestEntry && !haveWrittenTestEntry) {
164 // Shift everything forward
165 memmove(payload.get() + offset + sizeof(DllBlockInfo),
166 payload.get() + offset, header.mPayloadSize - offset);
167 AssignTestEntry(entry, destinationPayloadSize - kTestDllBytes);
168 haveWrittenTestEntry = true;
170 #endif
171 break;
174 size_t stringOffset = reinterpret_cast<size_t>(entry->mName.Buffer);
175 if (stringOffset + entry->mName.Length > header.mPayloadSize) {
176 entry->mName.Length = 0;
177 break;
179 entry->mName.MaximumLength = entry->mName.Length;
180 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
181 if (shouldInsertBlocklistTestEntry && !haveWrittenTestEntry) {
182 UNICODE_STRING currentUnicodeString;
183 currentUnicodeString.Buffer =
184 reinterpret_cast<PWSTR>(payload.get() + stringOffset);
185 currentUnicodeString.Length = entry->mName.Length;
186 currentUnicodeString.MaximumLength = entry->mName.Length;
187 if (RtlCompareUnicodeString(&currentUnicodeString, &testUnicodeString,
188 TRUE) > 0) {
189 // Shift everything forward
190 memmove(payload.get() + offset + sizeof(DllBlockInfo),
191 payload.get() + offset, header.mPayloadSize - offset);
192 AssignTestEntry(entry, destinationPayloadSize - kTestDllBytes);
193 haveWrittenTestEntry = true;
194 // Now we have expanded the area of valid memory, so there is more
195 // allowable space to iterate over.
196 sizeOfPayloadToIterateOver = destinationPayloadSize;
197 offset += sizeof(DllBlockInfo);
198 ++entry;
200 // The start of the string section will be moving ahead (or has already
201 // moved ahead) to make room for the test entry
202 entry->mName.Buffer +=
203 sizeof(DllBlockInfo) / sizeof(entry->mName.Buffer[0]);
205 #endif
207 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
208 if (shouldInsertBlocklistTestEntry) {
209 memcpy(payload.get() + destinationPayloadSize - kTestDllBytes, kTestDll,
210 kTestDllBytes);
212 #endif
214 mPayloadSize = destinationPayloadSize;
215 mPayload = std::move(payload);
216 return true;
219 public:
220 DynamicBlockList() : mPayloadSize(0) {}
221 explicit DynamicBlockList(const wchar_t* aPath) : mPayloadSize(0) {
222 LoadFile(aPath);
225 DynamicBlockList(DynamicBlockList&&) = default;
226 DynamicBlockList& operator=(DynamicBlockList&&) = default;
227 DynamicBlockList(const DynamicBlockList&) = delete;
228 DynamicBlockList& operator=(const DynamicBlockList&) = delete;
230 constexpr uint32_t GetPayloadSize() const { return mPayloadSize; }
232 // Return the number of bytes copied
233 size_t CopyTo(void* aBuffer, size_t aBufferLength) const {
234 if (mPayloadSize > aBufferLength) {
235 return 0;
237 memcpy(aBuffer, mPayload.get(), mPayloadSize);
238 return mPayloadSize;
242 #if defined(MOZILLA_INTERNAL_API) && defined(MOZ_LAUNCHER_PROCESS)
244 class DynamicBlocklistWriter final : public DynamicBlockListBase {
245 template <typename T>
246 static nsresult WriteValue(nsIOutputStream* aOutputStream, const T& aValue) {
247 uint32_t written;
248 return aOutputStream->Write(reinterpret_cast<const char*>(&aValue),
249 sizeof(T), &written);
252 template <typename T>
253 static nsresult WriteBuffer(nsIOutputStream* aOutputStream, const T* aBuffer,
254 uint32_t aBufferSize) {
255 uint32_t written;
256 return aOutputStream->Write(reinterpret_cast<const char*>(aBuffer),
257 aBufferSize, &written);
260 RefPtr<dom::Promise> mPromise;
261 Vector<DllBlockInfo> mArray;
262 // All strings are packed in this buffer without null characters
263 UniquePtr<uint8_t[]> mStringBuffer;
265 size_t mArraySize;
266 size_t mStringBufferSize;
268 nsresult WriteToFile(const nsAString& aName) const;
270 public:
271 DynamicBlocklistWriter(
272 RefPtr<dom::Promise> aPromise,
273 const nsTHashSet<nsStringCaseInsensitiveHashKey>& aBlocklist);
274 ~DynamicBlocklistWriter() = default;
276 DynamicBlocklistWriter(DynamicBlocklistWriter&&) = default;
277 DynamicBlocklistWriter& operator=(DynamicBlocklistWriter&&) = default;
278 DynamicBlocklistWriter(const DynamicBlocklistWriter&) = delete;
279 DynamicBlocklistWriter& operator=(const DynamicBlocklistWriter&) = delete;
281 bool IsReady() const { return mStringBuffer.get(); }
283 void Run();
284 void Cancel();
287 #endif // defined(MOZILLA_INTERNAL_API) && defined(MOZ_LAUNCHER_PROCESS)
289 } // namespace mozilla
291 #endif // mozilla_DynamicBlocklist_h