212 lines
9.2 KiB
C++
212 lines
9.2 KiB
C++
/* -*- 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
|