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
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)
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;
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
;
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.
56 static constexpr wchar_t kTestDll
[] = L
"TestDllBlocklist_UserBlocked.dll";
57 // kTestDll is null-terminated, but we don't want that for the blocklist
59 static constexpr size_t kTestDllBytes
= sizeof(kTestDll
) - sizeof(wchar_t);
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
);
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"));
93 ::CreateFileW(aPath
, GENERIC_READ
,
94 FILE_SHARE_READ
| FILE_SHARE_DELETE
| FILE_SHARE_WRITE
,
95 nullptr, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, nullptr));
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();
111 ::ReadFile(file
.get(), &header
, sizeof(header
), &bytesRead
, nullptr);
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();
123 if (bytesRead
!= sizeof(header
)) {
127 if (header
.mSignature
!= kSignature
||
128 header
.mFileVersion
!= kCurrentVersion
) {
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
;
139 UniquePtr
<uint8_t[]> payload
=
140 MakeUnique
<uint8_t[]>(destinationPayloadSize
);
141 ok
= ::ReadFile(file
.get(), payload
.get(), header
.mPayloadSize
, &bytesRead
,
143 if (!ok
|| bytesRead
!= header
.mPayloadSize
) {
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
;
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;
174 size_t stringOffset
= reinterpret_cast<size_t>(entry
->mName
.Buffer
);
175 if (stringOffset
+ entry
->mName
.Length
> header
.mPayloadSize
) {
176 entry
->mName
.Length
= 0;
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(¤tUnicodeString
, &testUnicodeString
,
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
);
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]);
207 #ifdef BLOCKLIST_INSERT_TEST_ENTRY
208 if (shouldInsertBlocklistTestEntry
) {
209 memcpy(payload
.get() + destinationPayloadSize
- kTestDllBytes
, kTestDll
,
214 mPayloadSize
= destinationPayloadSize
;
215 mPayload
= std::move(payload
);
220 DynamicBlockList() : mPayloadSize(0) {}
221 explicit DynamicBlockList(const wchar_t* aPath
) : mPayloadSize(0) {
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
) {
237 memcpy(aBuffer
, mPayload
.get(), 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
) {
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
) {
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
;
266 size_t mStringBufferSize
;
268 nsresult
WriteToFile(const nsAString
& aName
) const;
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(); }
287 #endif // defined(MOZILLA_INTERNAL_API) && defined(MOZ_LAUNCHER_PROCESS)
289 } // namespace mozilla
291 #endif // mozilla_DynamicBlocklist_h