Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#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
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