1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
/* -*- 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;
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);
// 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();
Span<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
|