mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Backed out changeset 48a0fcaf46b8 (bug 1684532) for causing gtest perma failure. CLOSED TREE
This commit is contained in:
parent
2d91e59e16
commit
0b57365558
@ -25,7 +25,6 @@
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
# include "freestanding/SharedSection.h"
|
||||
# include "LauncherProcessWin.h"
|
||||
# include "mozilla/WindowsDllBlocklist.h"
|
||||
|
||||
@ -321,11 +320,6 @@ int main(int argc, char* argv[], char* envp[]) {
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// Once the browser process hits the main function, we no longer need
|
||||
// a writable section handle because all dependent modules have been
|
||||
// loaded.
|
||||
mozilla::freestanding::gSharedSection.ConvertToReadOnly();
|
||||
|
||||
mozilla::CreateAndStorePreXULSkeletonUI(GetModuleHandle(nullptr), argc, argv);
|
||||
#endif
|
||||
|
||||
|
@ -44,6 +44,12 @@ LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher(
|
||||
static LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPInternal(
|
||||
const wchar_t* aFullImagePath, nt::CrossExecTransferManager& aTransferMgr,
|
||||
const IMAGE_THUNK_DATA* aCachedNtdllThunk) {
|
||||
LauncherVoidResult transferResult =
|
||||
freestanding::gSharedSection.TransferHandle(aTransferMgr);
|
||||
if (transferResult.isErr()) {
|
||||
return transferResult.propagateErr();
|
||||
}
|
||||
|
||||
CrossProcessDllInterceptor intcpt(aTransferMgr.RemoteProcess());
|
||||
intcpt.Init(L"ntdll.dll");
|
||||
|
||||
@ -163,14 +169,6 @@ LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP(
|
||||
return RestoreImportDirectory(aFullImagePath, transferMgr);
|
||||
}
|
||||
|
||||
// Transfer a readonly handle to the child processes because all information
|
||||
// are already written to the section by the launcher and main process.
|
||||
LauncherVoidResult transferResult =
|
||||
freestanding::gSharedSection.TransferHandle(transferMgr, GENERIC_READ);
|
||||
if (transferResult.isErr()) {
|
||||
return transferResult.propagateErr();
|
||||
}
|
||||
|
||||
return InitializeDllBlocklistOOPInternal(aFullImagePath, transferMgr,
|
||||
aCachedNtdllThunk);
|
||||
}
|
||||
@ -191,15 +189,6 @@ LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher(
|
||||
return result.propagateErr();
|
||||
}
|
||||
|
||||
// Transfer a writable handle to the main process because it needs to append
|
||||
// dependent module paths to the section.
|
||||
LauncherVoidResult transferResult =
|
||||
freestanding::gSharedSection.TransferHandle(transferMgr,
|
||||
GENERIC_READ | GENERIC_WRITE);
|
||||
if (transferResult.isErr()) {
|
||||
return transferResult.propagateErr();
|
||||
}
|
||||
|
||||
auto clearInstance = MakeScopeExit([]() {
|
||||
// After transfer, the launcher process does not need the object anymore.
|
||||
freestanding::gSharedSection.Reset(nullptr);
|
||||
|
@ -241,49 +241,6 @@ struct DllBlockInfoComparator {
|
||||
};
|
||||
|
||||
static BOOL WINAPI NoOp_DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; }
|
||||
static RTL_RUN_ONCE gK32ExportsResolveOnce = RTL_RUN_ONCE_INIT;
|
||||
|
||||
// This helper function checks whether a given module is included
|
||||
// in the executable's Import Table. Because an injected module's
|
||||
// DllMain may revert the Import Table to the original state, we parse
|
||||
// the Import Table every time a module is loaded without creating a cache.
|
||||
static bool IsDependentModule(
|
||||
const UNICODE_STRING& aModuleLeafName,
|
||||
mozilla::freestanding::Kernel32ExportsSolver& aK32Exports) {
|
||||
// We enable automatic DLL blocking only in Nightly for now because it caused
|
||||
// a compat issue (bug 1682304).
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
aK32Exports.Resolve(gK32ExportsResolveOnce);
|
||||
if (!aK32Exports.IsResolved()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::nt::PEHeaders exeHeaders(aK32Exports.mGetModuleHandleW(nullptr));
|
||||
if (!exeHeaders || !exeHeaders.IsImportDirectoryTampered()) {
|
||||
// If no tampering is detected, no need to enumerate the Import Table.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isDependent = false;
|
||||
exeHeaders.EnumImportChunks(
|
||||
[&isDependent, &aModuleLeafName, &exeHeaders](const char* aDepModule) {
|
||||
// If |aDepModule| is within the PE image, it's not an injected module
|
||||
// but a legitimate dependent module.
|
||||
if (isDependent || exeHeaders.IsWithinImage(aDepModule)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UNICODE_STRING depModuleLeafName;
|
||||
mozilla::nt::AllocatedUnicodeString depModuleName(aDepModule);
|
||||
mozilla::nt::GetLeafName(&depModuleLeafName, depModuleName);
|
||||
isDependent = (::RtlCompareUnicodeString(
|
||||
&aModuleLeafName, &depModuleLeafName, TRUE) == 0);
|
||||
});
|
||||
return isDependent;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Allowing a module to be loaded but detour the entrypoint to NoOp_DllMain
|
||||
// so that the module has no chance to interact with our code. We need this
|
||||
@ -292,7 +249,8 @@ static bool IsDependentModule(
|
||||
static bool RedirectToNoOpEntryPoint(
|
||||
const mozilla::nt::PEHeaders& aModule,
|
||||
mozilla::freestanding::Kernel32ExportsSolver& aK32Exports) {
|
||||
aK32Exports.Resolve(gK32ExportsResolveOnce);
|
||||
static RTL_RUN_ONCE sRunOnce = RTL_RUN_ONCE_INIT;
|
||||
aK32Exports.Resolve(sRunOnce);
|
||||
if (!aK32Exports.IsResolved()) {
|
||||
return false;
|
||||
}
|
||||
@ -402,27 +360,31 @@ NTSTATUS NTAPI patched_NtMapViewOfSection(
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
bool isDependent = false;
|
||||
auto resultView = mozilla::freestanding::gSharedSection.GetView();
|
||||
if (resultView.isOk()) {
|
||||
uint32_t arrayLen = resultView.inspect()->mModulePathArrayLength;
|
||||
const uint32_t* array = resultView.inspect()->mModulePathArray;
|
||||
size_t match;
|
||||
isDependent = mozilla::BinarySearchIf(
|
||||
array, 0, arrayLen,
|
||||
[§ionFileName, arrayBase = reinterpret_cast<const uint8_t*>(array)](
|
||||
const uint32_t& aOffset) {
|
||||
UNICODE_STRING str;
|
||||
::RtlInitUnicodeString(
|
||||
&str, reinterpret_cast<const wchar_t*>(arrayBase + aOffset));
|
||||
return static_cast<int>(
|
||||
::RtlCompareUnicodeString(sectionFileName, &str, TRUE));
|
||||
},
|
||||
&match);
|
||||
}
|
||||
|
||||
// Find the leaf name
|
||||
UNICODE_STRING leafOnStack;
|
||||
nt::GetLeafName(&leafOnStack, sectionFileName);
|
||||
|
||||
bool isDependent = false;
|
||||
auto resultView = freestanding::gSharedSection.GetView();
|
||||
// Small optimization: Since loading a dependent module does not involve
|
||||
// LdrLoadDll, we know isDependent is false if we hold a top frame.
|
||||
if (resultView.isOk() && !ModuleLoadFrame::ExistsTopFrame()) {
|
||||
isDependent =
|
||||
IsDependentModule(leafOnStack, resultView.inspect()->mK32Exports);
|
||||
}
|
||||
|
||||
BlockAction blockAction;
|
||||
if (isDependent) {
|
||||
// Add an NT path to the shared section so that a sandbox process can
|
||||
// use it to bypass CIG. In a sandbox process, this addition fails
|
||||
// because we cannot map the section to a writable region, but it's
|
||||
// ignorable because the paths have been added by the browser process.
|
||||
Unused << freestanding::gSharedSection.AddDepenentModule(sectionFileName);
|
||||
|
||||
// For a dependent module, try redirection instead of blocking it.
|
||||
// If we fail, we reluctantly allow the module for free.
|
||||
mozilla::nt::PEHeaders headers(*aBaseAddress);
|
||||
|
@ -85,9 +85,6 @@ void ModuleLoadFrame::NotifySectionMap(
|
||||
topFrame->OnSectionMap(std::move(aSectionName), aMapBaseAddr, aMapNtStatus);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool ModuleLoadFrame::ExistsTopFrame() { return !!sTopFrame.get(); }
|
||||
|
||||
void ModuleLoadFrame::OnSectionMap(nt::AllocatedUnicodeString&& aSectionName,
|
||||
const void* aMapBaseAddr,
|
||||
NTSTATUS aMapNtStatus) {
|
||||
|
@ -37,7 +37,6 @@ class MOZ_RAII ModuleLoadFrame final {
|
||||
*/
|
||||
static void NotifySectionMap(nt::AllocatedUnicodeString&& aSectionName,
|
||||
const void* aMapBaseAddr, NTSTATUS aMapNtStatus);
|
||||
static bool ExistsTopFrame();
|
||||
|
||||
/**
|
||||
* Called by the LdrLoadDll hook to indicate the status of the load and for
|
||||
|
@ -8,43 +8,6 @@
|
||||
|
||||
#include "CheckForCaller.h"
|
||||
|
||||
namespace {
|
||||
|
||||
bool AddString(void* aBuffer, size_t aBufferSize, const UNICODE_STRING& aStr) {
|
||||
size_t offset = 0;
|
||||
while (offset < aBufferSize) {
|
||||
UNICODE_STRING uniStr;
|
||||
::RtlInitUnicodeString(&uniStr,
|
||||
reinterpret_cast<wchar_t*>(
|
||||
reinterpret_cast<uintptr_t>(aBuffer) + offset));
|
||||
|
||||
if (uniStr.Length == 0) {
|
||||
// Reached to the array's last item.
|
||||
break;
|
||||
}
|
||||
|
||||
if (::RtlCompareUnicodeString(&uniStr, &aStr, TRUE) == 0) {
|
||||
// Already included in the array.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Go to the next string.
|
||||
offset += uniStr.MaximumLength;
|
||||
}
|
||||
|
||||
// Ensure enough space including the last empty string at the end.
|
||||
if (offset + aStr.MaximumLength + sizeof(wchar_t) > aBufferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto newStr = reinterpret_cast<uint8_t*>(aBuffer) + offset;
|
||||
memcpy(newStr, aStr.Buffer, aStr.Length);
|
||||
memset(newStr + aStr.Length, 0, sizeof(wchar_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace freestanding {
|
||||
|
||||
@ -93,7 +56,6 @@ void Kernel32ExportsSolver::Init() {
|
||||
|
||||
// Please make sure these functions are not forwarded to another DLL.
|
||||
INIT_FUNCTION(k32Exports, FlushInstructionCache);
|
||||
INIT_FUNCTION(k32Exports, GetModuleHandleW);
|
||||
INIT_FUNCTION(k32Exports, GetSystemInfo);
|
||||
INIT_FUNCTION(k32Exports, VirtualProtect);
|
||||
|
||||
@ -119,7 +81,6 @@ void Kernel32ExportsSolver::ResolveInternal() {
|
||||
nt::PEHeaders::HModuleToBaseAddr<uintptr_t>(k32Module.unwrap());
|
||||
|
||||
RESOLVE_FUNCTION(k32Base, FlushInstructionCache);
|
||||
RESOLVE_FUNCTION(k32Base, GetModuleHandleW);
|
||||
RESOLVE_FUNCTION(k32Base, GetSystemInfo);
|
||||
RESOLVE_FUNCTION(k32Base, VirtualProtect);
|
||||
|
||||
@ -146,37 +107,74 @@ void SharedSection::Reset(HANDLE aNewSecionObject) {
|
||||
sWriteCopyView = nullptr;
|
||||
}
|
||||
|
||||
if (sSectionHandle != aNewSecionObject) {
|
||||
if (sSectionHandle) {
|
||||
::CloseHandle(sSectionHandle);
|
||||
}
|
||||
sSectionHandle = aNewSecionObject;
|
||||
if (sSectionHandle) {
|
||||
::CloseHandle(sSectionHandle);
|
||||
}
|
||||
sSectionHandle = aNewSecionObject;
|
||||
}
|
||||
|
||||
void SharedSection::ConvertToReadOnly() {
|
||||
if (!sSectionHandle) {
|
||||
return;
|
||||
static void PackOffsetVector(const Vector<nt::MemorySectionNameOnHeap>& aSource,
|
||||
SharedSection::Layout& aDestination,
|
||||
size_t aStringBufferOffset) {
|
||||
aDestination.mModulePathArrayLength = aSource.length();
|
||||
|
||||
uint32_t* curItem = aDestination.mModulePathArray;
|
||||
uint8_t* const arrayBase = reinterpret_cast<uint8_t*>(curItem);
|
||||
for (const auto& it : aSource) {
|
||||
// Fill the current offset value
|
||||
*(curItem++) = aStringBufferOffset;
|
||||
|
||||
// Fill a string and a null character
|
||||
uint32_t lenInBytes = it.AsUnicodeString()->Length;
|
||||
memcpy(arrayBase + aStringBufferOffset, it.AsUnicodeString()->Buffer,
|
||||
lenInBytes);
|
||||
memset(arrayBase + aStringBufferOffset + lenInBytes, 0, sizeof(WCHAR));
|
||||
|
||||
// Advance the offset
|
||||
aStringBufferOffset += (lenInBytes + sizeof(WCHAR));
|
||||
}
|
||||
|
||||
HANDLE readonlyHandle;
|
||||
if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle,
|
||||
nt::kCurrentProcess, &readonlyHandle, GENERIC_READ,
|
||||
FALSE, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reset(readonlyHandle);
|
||||
// Sort the offset array so that we can binary-search it
|
||||
std::sort(aDestination.mModulePathArray,
|
||||
aDestination.mModulePathArray + aSource.length(),
|
||||
[arrayBase](uint32_t a, uint32_t b) {
|
||||
auto s1 = reinterpret_cast<const wchar_t*>(arrayBase + a);
|
||||
auto s2 = reinterpret_cast<const wchar_t*>(arrayBase + b);
|
||||
return wcsicmp(s1, s2) < 0;
|
||||
});
|
||||
}
|
||||
|
||||
LauncherVoidResult SharedSection::Init(const nt::PEHeaders& aPEHeaders) {
|
||||
static_assert(
|
||||
kSharedViewSize >= sizeof(Layout),
|
||||
"kSharedViewSize is too small to represent SharedSection::Layout.");
|
||||
size_t stringBufferSize = 0;
|
||||
Vector<nt::MemorySectionNameOnHeap> modules;
|
||||
|
||||
HANDLE section =
|
||||
::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
|
||||
kSharedViewSize, nullptr);
|
||||
// We enable automatic DLL blocking only in Nightly for now because it caused
|
||||
// a compat issue (bug 1682304).
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
aPEHeaders.EnumImportChunks(
|
||||
[&stringBufferSize, &modules, &aPEHeaders](const char* aModule) {
|
||||
# if defined(DONT_SKIP_DEFAULT_DEPENDENT_MODULES)
|
||||
Unused << aPEHeaders;
|
||||
# else
|
||||
if (aPEHeaders.IsWithinImage(aModule)) {
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
HMODULE module = ::GetModuleHandleA(aModule);
|
||||
nt::MemorySectionNameOnHeap ntPath =
|
||||
nt::MemorySectionNameOnHeap::GetBackingFilePath(nt::kCurrentProcess,
|
||||
module);
|
||||
stringBufferSize += (ntPath.AsUnicodeString()->Length + sizeof(WCHAR));
|
||||
Unused << modules.emplaceBack(std::move(ntPath));
|
||||
});
|
||||
#endif
|
||||
|
||||
size_t arraySize = modules.length() * sizeof(Layout::mModulePathArray[0]);
|
||||
size_t totalSize =
|
||||
sizeof(Kernel32ExportsSolver) + arraySize + stringBufferSize;
|
||||
|
||||
HANDLE section = ::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
|
||||
PAGE_READWRITE, 0, totalSize, nullptr);
|
||||
if (!section) {
|
||||
return LAUNCHER_ERROR_FROM_LAST();
|
||||
}
|
||||
@ -193,21 +191,7 @@ LauncherVoidResult SharedSection::Init(const nt::PEHeaders& aPEHeaders) {
|
||||
|
||||
SharedSection::Layout* view = writableView.as<SharedSection::Layout>();
|
||||
view->mK32Exports.Init();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
LauncherVoidResult SharedSection::AddDepenentModule(PCUNICODE_STRING aNtPath) {
|
||||
nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE);
|
||||
if (!writableView) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error());
|
||||
}
|
||||
|
||||
SharedSection::Layout* view = writableView.as<SharedSection::Layout>();
|
||||
if (!AddString(view->mModulePathArray,
|
||||
kSharedViewSize - sizeof(Kernel32ExportsSolver), *aNtPath)) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
PackOffsetVector(modules, *view, arraySize);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
@ -216,7 +200,7 @@ LauncherResult<SharedSection::Layout*> SharedSection::GetView() {
|
||||
if (!sWriteCopyView) {
|
||||
nt::AutoMappedView view(sSectionHandle, PAGE_WRITECOPY);
|
||||
if (!view) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error());
|
||||
return LAUNCHER_ERROR_FROM_LAST();
|
||||
}
|
||||
sWriteCopyView = view.release();
|
||||
}
|
||||
@ -224,12 +208,11 @@ LauncherResult<SharedSection::Layout*> SharedSection::GetView() {
|
||||
}
|
||||
|
||||
LauncherVoidResult SharedSection::TransferHandle(
|
||||
nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess,
|
||||
HANDLE* aDestinationAddress) {
|
||||
nt::CrossExecTransferManager& aTransferMgr, HANDLE* aDestinationAddress) {
|
||||
HANDLE remoteHandle;
|
||||
if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle,
|
||||
aTransferMgr.RemoteProcess(), &remoteHandle,
|
||||
aDesiredAccess, FALSE, 0)) {
|
||||
GENERIC_READ, FALSE, 0)) {
|
||||
return LAUNCHER_ERROR_FROM_LAST();
|
||||
}
|
||||
|
||||
@ -237,29 +220,33 @@ LauncherVoidResult SharedSection::TransferHandle(
|
||||
sizeof(remoteHandle));
|
||||
}
|
||||
|
||||
// This exported function is invoked by SandboxBroker of xul.dll
|
||||
// in order to add dependent modules to the CIG exception list.
|
||||
extern "C" MOZ_EXPORT const wchar_t* GetDependentModulePaths() {
|
||||
extern "C" MOZ_EXPORT uint32_t GetDependentModulePaths(uint32_t** aOutArray) {
|
||||
if (aOutArray) {
|
||||
*aOutArray = nullptr;
|
||||
}
|
||||
|
||||
// We enable pre-spawn CIG only in Nightly for now because it caused
|
||||
// a compat issue (bug 1682304).
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
const bool isCallerXul = CheckForAddress(RETURN_ADDRESS(), L"xul.dll");
|
||||
MOZ_ASSERT(isCallerXul);
|
||||
if (!isCallerXul) {
|
||||
return nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remap a write-copy section to take the latest update in mModulePathArray.
|
||||
gSharedSection.Reset();
|
||||
|
||||
LauncherResult<SharedSection::Layout*> resultView = gSharedSection.GetView();
|
||||
if (resultView.isErr()) {
|
||||
return nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return resultView.inspect()->mModulePathArray;
|
||||
if (aOutArray) {
|
||||
// Return a valid address even if the array length is zero
|
||||
// to distinguish it from an error case.
|
||||
*aOutArray = resultView.inspect()->mModulePathArray;
|
||||
}
|
||||
return resultView.inspect()->mModulePathArrayLength;
|
||||
#else
|
||||
return nullptr;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -45,27 +45,31 @@ class MOZ_TRIVIAL_CTOR_DTOR Kernel32ExportsSolver final
|
||||
};
|
||||
|
||||
// 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.
|
||||
// mapped in the browser process and the sandboxed processes as a copy-on-write
|
||||
// region. The section's layout is represented as SharedSection::Layout.
|
||||
//
|
||||
// (1) Kernel32's functions required for MMPolicyInProcessEarlyStage
|
||||
// Formatted as Kernel32ExportsSolver.
|
||||
//
|
||||
// (2) Array of NT paths of the executable's dependent modules
|
||||
// Formatted as a null-delimited wide-character string set ending with
|
||||
// an empty string.
|
||||
// Array item is an offset to a string buffer of a module's
|
||||
// NT path, relative to the beginning of the array.
|
||||
// Array is case-insensitive sorted.
|
||||
//
|
||||
// +--------------------------------------------------------------+
|
||||
// | (1) | FlushInstructionCache |
|
||||
// | | GetModuleHandleW |
|
||||
// | | GetSystemInfo |
|
||||
// | | VirtualProtect |
|
||||
// | | State [Uninitialized|Initialized|Resolved] |
|
||||
// +--------------------------------------------------------------+
|
||||
// | (2) | L"NT path 1" |
|
||||
// | | L"NT path 2" |
|
||||
// | (2) | The length of the offset array |
|
||||
// | | Offset1 to String1 |
|
||||
// | | * Offset is relative to the beginning of the array |
|
||||
// | | * String is an NT path in wchar_t |
|
||||
// | | Offset2 to String2 |
|
||||
// | | ... |
|
||||
// | | L"" |
|
||||
// | | OffsetN to StringN |
|
||||
// | | String1, 2, ..., N (null delimited strings) |
|
||||
// +--------------------------------------------------------------+
|
||||
class MOZ_TRIVIAL_CTOR_DTOR SharedSection final {
|
||||
// As we define a global variable of this class and use it in our blocklist
|
||||
@ -77,35 +81,21 @@ class MOZ_TRIVIAL_CTOR_DTOR SharedSection final {
|
||||
static HANDLE sSectionHandle;
|
||||
static void* sWriteCopyView;
|
||||
|
||||
static constexpr size_t kSharedViewSize = 0x1000;
|
||||
|
||||
public:
|
||||
struct Layout final {
|
||||
Kernel32ExportsSolver mK32Exports;
|
||||
wchar_t mModulePathArray[1];
|
||||
uint32_t mModulePathArrayLength;
|
||||
uint32_t mModulePathArray[1];
|
||||
|
||||
Layout() = delete; // disallow instantiation
|
||||
};
|
||||
|
||||
// Replace |sSectionHandle| with a given handle.
|
||||
static void Reset(HANDLE aNewSecionObject = sSectionHandle);
|
||||
|
||||
// Replace |sSectionHandle| with a new readonly handle.
|
||||
static void ConvertToReadOnly();
|
||||
|
||||
// Create a new writable section and initialize the Kernel32ExportsSolver
|
||||
// part.
|
||||
static void Reset(HANDLE aNewSecionObject);
|
||||
static LauncherVoidResult Init(const nt::PEHeaders& aPEHeaders);
|
||||
|
||||
// Append a new string to the |sSectionHandle|
|
||||
static LauncherVoidResult AddDepenentModule(PCUNICODE_STRING aNtPath);
|
||||
|
||||
// Map |sSectionHandle| to a copy-on-write page and return its address.
|
||||
static LauncherResult<Layout*> GetView();
|
||||
|
||||
// Transfer |sSectionHandle| to a process associated with |aTransferMgr|.
|
||||
static LauncherVoidResult TransferHandle(
|
||||
nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess,
|
||||
nt::CrossExecTransferManager& aTransferMgr,
|
||||
HANDLE* aDestinationAddress = &sSectionHandle);
|
||||
};
|
||||
|
||||
|
@ -5,20 +5,28 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#define MOZ_USE_LAUNCHER_ERROR
|
||||
#define DONT_SKIP_DEFAULT_DEPENDENT_MODULES
|
||||
|
||||
#include "freestanding/SharedSection.cpp"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
|
||||
const wchar_t kChildArg[] = L"--child";
|
||||
const char kTestStrings[][6] = {
|
||||
"a b c", "A B C", "a b c", "A B C", "X Y Z",
|
||||
};
|
||||
const wchar_t kTestStringsMerged[] =
|
||||
L"a b c"
|
||||
L"\0"
|
||||
L"X Y Z"
|
||||
L"\0";
|
||||
|
||||
#if !defined(__MINGW32__)
|
||||
// MinGW includes an old winternl.h that defines FILE_BASIC_INFORMATION.
|
||||
typedef struct FILE_BASIC_INFORMATION {
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
ULONG FileAttributes;
|
||||
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
|
||||
#endif // !defined(__MINGW32__)
|
||||
|
||||
extern "C" NTSTATUS NTAPI
|
||||
NtQueryAttributesFile(POBJECT_ATTRIBUTES aObjectAttributes,
|
||||
PFILE_BASIC_INFORMATION aFileInformation);
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::freestanding;
|
||||
@ -57,55 +65,32 @@ static bool VerifySharedSection(SharedSection& aSharedSection) {
|
||||
|
||||
HMODULE k32mod = ::GetModuleHandleW(L"kernel32.dll");
|
||||
VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, FlushInstructionCache);
|
||||
VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetModuleHandleW);
|
||||
VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetSystemInfo);
|
||||
VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, VirtualProtect);
|
||||
|
||||
bool matched = memcmp(view->mModulePathArray, kTestStringsMerged,
|
||||
sizeof(kTestStringsMerged)) == 0;
|
||||
if (!matched) {
|
||||
// Print actual strings on error
|
||||
for (const wchar_t* p = view->mModulePathArray; *p;) {
|
||||
printf("%p: %ls\n", p, p);
|
||||
while (*p) {
|
||||
++p;
|
||||
}
|
||||
++p;
|
||||
const uint8_t* const arrayBase =
|
||||
reinterpret_cast<const uint8_t*>(view->mModulePathArray);
|
||||
for (uint32_t i = 0; i < view->mModulePathArrayLength; ++i) {
|
||||
uint32_t offset = view->mModulePathArray[i];
|
||||
// Use NtQueryAttributesFile to check the validity of an NT path.
|
||||
UNICODE_STRING ntpath;
|
||||
::RtlInitUnicodeString(
|
||||
&ntpath, reinterpret_cast<const wchar_t*>(arrayBase + offset));
|
||||
OBJECT_ATTRIBUTES oa;
|
||||
InitializeObjectAttributes(&oa, &ntpath, OBJ_CASE_INSENSITIVE, nullptr,
|
||||
nullptr);
|
||||
FILE_BASIC_INFORMATION info;
|
||||
NTSTATUS status = ::NtQueryAttributesFile(&oa, &info);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"Invalid path %ls - %08lx.\n",
|
||||
ntpath.Buffer, status);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TestAddString() {
|
||||
wchar_t testBuffer[3] = {0};
|
||||
UNICODE_STRING ustr;
|
||||
|
||||
// This makes |testBuffer| full.
|
||||
::RtlInitUnicodeString(&ustr, L"a");
|
||||
if (!AddString(testBuffer, sizeof(testBuffer), ustr)) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"AddString failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adding a string to a full buffer should fail.
|
||||
::RtlInitUnicodeString(&ustr, L"b");
|
||||
if (AddString(testBuffer, sizeof(testBuffer), ustr)) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"AddString caused OOB memory access.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool matched = memcmp(testBuffer, L"a\0", sizeof(testBuffer)) == 0;
|
||||
if (!matched) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"AddString wrote wrong values.\n");
|
||||
return false;
|
||||
printf("%p: %ls\n", &offset,
|
||||
reinterpret_cast<const wchar_t*>(arrayBase + offset));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -148,7 +133,7 @@ class ChildProcess final {
|
||||
}
|
||||
|
||||
auto getDependentModulePaths =
|
||||
reinterpret_cast<const wchar_t (*)()>(::GetProcAddress(
|
||||
reinterpret_cast<uint32_t (*)(uint32_t**)>(::GetProcAddress(
|
||||
::GetModuleHandleW(nullptr), "GetDependentModulePaths"));
|
||||
if (!getDependentModulePaths) {
|
||||
printf(
|
||||
@ -161,7 +146,8 @@ class ChildProcess final {
|
||||
#if !defined(DEBUG)
|
||||
// GetDependentModulePaths does not allow a caller other than xul.dll.
|
||||
// Skip on Debug build because it hits MOZ_ASSERT.
|
||||
if (getDependentModulePaths()) {
|
||||
uint32_t* modulePathArray;
|
||||
if (getDependentModulePaths(&modulePathArray) || modulePathArray) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"GetDependentModulePaths should return zero if the caller is "
|
||||
@ -174,27 +160,17 @@ class ChildProcess final {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test a scenario to transfer a transferred section as a readonly handle
|
||||
// Test a scenario to transfer a transferred section
|
||||
static HANDLE copiedHandle = nullptr;
|
||||
nt::CrossExecTransferManager tansferToSelf(::GetCurrentProcess());
|
||||
LauncherVoidResult result = gSharedSection.TransferHandle(
|
||||
tansferToSelf, GENERIC_READ, &copiedHandle);
|
||||
LauncherVoidResult result =
|
||||
gSharedSection.TransferHandle(tansferToSelf, &copiedHandle);
|
||||
if (result.isErr()) {
|
||||
PrintLauncherError(result, "SharedSection::TransferHandle(self) failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
gSharedSection.Reset(copiedHandle);
|
||||
|
||||
UNICODE_STRING ustr;
|
||||
::RtlInitUnicodeString(&ustr, L"test");
|
||||
result = gSharedSection.AddDepenentModule(&ustr);
|
||||
if (result.inspectErr() !=
|
||||
WindowsError::FromWin32Error(ERROR_ACCESS_DENIED)) {
|
||||
PrintLauncherError(result, "The readonly section was writable");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!VerifySharedSection(gSharedSection)) {
|
||||
return 1;
|
||||
}
|
||||
@ -277,10 +253,6 @@ int wmain(int argc, wchar_t* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!TestAddString()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
LauncherResult<HMODULE> remoteImageBase =
|
||||
nt::GetProcessExeModule(childProcess);
|
||||
if (remoteImageBase.isErr()) {
|
||||
@ -340,17 +312,7 @@ int wmain(int argc, wchar_t* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (const auto& testString : kTestStrings) {
|
||||
nt::AllocatedUnicodeString ustr(testString);
|
||||
result = gSharedSection.AddDepenentModule(ustr);
|
||||
if (result.isErr()) {
|
||||
PrintLauncherError(result, "SharedSection::AddDepenentModule failed");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
result = gSharedSection.TransferHandle(transferMgr,
|
||||
GENERIC_READ | GENERIC_WRITE);
|
||||
result = gSharedSection.TransferHandle(transferMgr);
|
||||
if (result.isErr()) {
|
||||
PrintLauncherError(result, "SharedSection::TransferHandle failed");
|
||||
return 1;
|
||||
|
@ -97,7 +97,6 @@ VOID NTAPI RtlReleaseSRWLockShared(PSRWLOCK aLock);
|
||||
|
||||
ULONG NTAPI RtlNtStatusToDosError(NTSTATUS aStatus);
|
||||
VOID NTAPI RtlSetLastWin32Error(DWORD aError);
|
||||
DWORD NTAPI RtlGetLastWin32Error();
|
||||
|
||||
NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress,
|
||||
PVOID aBuffer, SIZE_T aNumBytesToRead,
|
||||
@ -162,15 +161,6 @@ class AllocatedUnicodeString final {
|
||||
Duplicate(aSrc);
|
||||
}
|
||||
|
||||
explicit AllocatedUnicodeString(const char* aSrc) {
|
||||
if (!aSrc) {
|
||||
mUnicodeString = {};
|
||||
return;
|
||||
}
|
||||
|
||||
Duplicate(aSrc);
|
||||
}
|
||||
|
||||
AllocatedUnicodeString(const AllocatedUnicodeString& aOther) {
|
||||
Duplicate(&aOther.mUnicodeString);
|
||||
}
|
||||
@ -240,19 +230,6 @@ class AllocatedUnicodeString final {
|
||||
mUnicodeString = {};
|
||||
}
|
||||
}
|
||||
|
||||
void Duplicate(const char* aSrc) {
|
||||
MOZ_ASSERT(aSrc);
|
||||
|
||||
ANSI_STRING ansiStr;
|
||||
RtlInitAnsiString(&ansiStr, aSrc);
|
||||
NTSTATUS ntStatus =
|
||||
::RtlAnsiStringToUnicodeString(&mUnicodeString, &ansiStr, TRUE);
|
||||
MOZ_ASSERT(NT_SUCCESS(ntStatus));
|
||||
if (!NT_SUCCESS(ntStatus)) {
|
||||
mUnicodeString = {};
|
||||
}
|
||||
}
|
||||
#endif // !defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
void Clear() {
|
||||
|
@ -614,7 +614,6 @@ class MMPolicyInProcessEarlyStage : public MMPolicyInProcessPrimitive {
|
||||
public:
|
||||
struct Kernel32Exports {
|
||||
decltype(&::FlushInstructionCache) mFlushInstructionCache;
|
||||
decltype(&::GetModuleHandleW) mGetModuleHandleW;
|
||||
decltype(&::GetSystemInfo) mGetSystemInfo;
|
||||
decltype(&::VirtualProtect) mVirtualProtect;
|
||||
};
|
||||
|
@ -392,18 +392,21 @@ static void AddCachedDirRule(sandbox::TargetPolicy* aPolicy,
|
||||
}
|
||||
}
|
||||
|
||||
// This function caches and returns an array of NT paths of the executable's
|
||||
// dependent modules.
|
||||
// This function caches and returns a Maybe<Span> of offsets to null
|
||||
// terminated wchar_t NT paths of the executable's dependent modules.
|
||||
// The strings themselves are stored in memory directly after the Span.
|
||||
// See also the comment in browser/app/winlauncher/freestanding/SharedSection.h
|
||||
//
|
||||
// If this returns Nothing(), it means the retrieval of the modules failed
|
||||
// (e.g. when the launcher process is disabled), so the process should not
|
||||
// enable pre-spawn CIG.
|
||||
static const Maybe<Vector<const wchar_t*>>& GetPrespawnCigExceptionModules() {
|
||||
static const Maybe<Span<uint32_t>>& GetPrespawnCigExceptionModules() {
|
||||
// sDependentModules points to a shared section created in the launcher
|
||||
// process and the mapped address is static in each process, so we cache
|
||||
// it as a static variable instead of retrieving it every time.
|
||||
static Maybe<Vector<const wchar_t*>> sDependentModules =
|
||||
[]() -> Maybe<Vector<const wchar_t*>> {
|
||||
using GetDependentModulePathsFn = const wchar_t* (*)();
|
||||
static Maybe<Span<uint32_t>> sDependentModules =
|
||||
[]() -> Maybe<Span<uint32_t>> {
|
||||
using GetDependentModulePathsFn = uint32_t (*)(uint32_t**);
|
||||
GetDependentModulePathsFn getDependentModulePaths =
|
||||
reinterpret_cast<GetDependentModulePathsFn>(::GetProcAddress(
|
||||
::GetModuleHandleW(nullptr), "GetDependentModulePaths"));
|
||||
@ -411,30 +414,17 @@ static const Maybe<Vector<const wchar_t*>>& GetPrespawnCigExceptionModules() {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
const wchar_t* arrayBase = getDependentModulePaths();
|
||||
if (!arrayBase) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
// Convert a null-delimited string set to a string vector.
|
||||
Vector<const wchar_t*> paths;
|
||||
for (const wchar_t* p = arrayBase; *p;) {
|
||||
Unused << paths.append(p);
|
||||
while (*p) {
|
||||
++p;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
|
||||
return Some(std::move(paths));
|
||||
uint32_t* modulePathArray = nullptr;
|
||||
uint32_t modulePathArrayLen = getDependentModulePaths(&modulePathArray);
|
||||
return modulePathArray ? Some(Span(modulePathArray, modulePathArrayLen))
|
||||
: Nothing();
|
||||
}();
|
||||
|
||||
return sDependentModules;
|
||||
}
|
||||
|
||||
static sandbox::ResultCode InitSignedPolicyRulesToBypassCig(
|
||||
sandbox::TargetPolicy* aPolicy,
|
||||
const Vector<const wchar_t*>& aExceptionModules) {
|
||||
sandbox::TargetPolicy* aPolicy, const Span<uint32_t>& aExceptionModules) {
|
||||
// Allow modules in the directory containing the executable such as
|
||||
// mozglue.dll, nss3.dll, etc.
|
||||
nsAutoString rulePath(*sBinDir);
|
||||
@ -446,13 +436,17 @@ static sandbox::ResultCode InitSignedPolicyRulesToBypassCig(
|
||||
return result;
|
||||
}
|
||||
|
||||
if (aExceptionModules.empty()) {
|
||||
if (aExceptionModules.IsEmpty()) {
|
||||
return sandbox::SBOX_ALL_OK;
|
||||
}
|
||||
|
||||
for (const wchar_t* path : aExceptionModules) {
|
||||
result = aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SIGNED_BINARY,
|
||||
sandbox::TargetPolicy::SIGNED_ALLOW_LOAD, path);
|
||||
const uint8_t* arrayBase =
|
||||
reinterpret_cast<const uint8_t*>(aExceptionModules.data());
|
||||
for (uint32_t offset : aExceptionModules) {
|
||||
result =
|
||||
aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SIGNED_BINARY,
|
||||
sandbox::TargetPolicy::SIGNED_ALLOW_LOAD,
|
||||
reinterpret_cast<const wchar_t*>(arrayBase + offset));
|
||||
if (result != sandbox::SBOX_ALL_OK) {
|
||||
return result;
|
||||
}
|
||||
@ -967,7 +961,7 @@ bool SandboxBroker::SetSecurityLevelForRDDProcess() {
|
||||
sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
|
||||
sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
|
||||
|
||||
const Maybe<Vector<const wchar_t*>>& exceptionModules =
|
||||
const Maybe<Span<uint32_t>>& exceptionModules =
|
||||
GetPrespawnCigExceptionModules();
|
||||
if (exceptionModules.isSome()) {
|
||||
mitigations |= sandbox::MITIGATION_FORCE_MS_SIGNED_BINS;
|
||||
|
Loading…
Reference in New Issue
Block a user