Backed out changeset 48a0fcaf46b8 (bug 1684532) for causing gtest perma failure. CLOSED TREE

This commit is contained in:
smolnar 2021-01-13 11:49:37 +02:00
parent 2d91e59e16
commit 0b57365558
11 changed files with 187 additions and 337 deletions

View File

@ -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

View File

@ -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);

View File

@ -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,
[&sectionFileName, 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);

View File

@ -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) {

View File

@ -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

View File

@ -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
}

View File

@ -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);
};

View File

@ -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;

View File

@ -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() {

View File

@ -614,7 +614,6 @@ class MMPolicyInProcessEarlyStage : public MMPolicyInProcessPrimitive {
public:
struct Kernel32Exports {
decltype(&::FlushInstructionCache) mFlushInstructionCache;
decltype(&::GetModuleHandleW) mGetModuleHandleW;
decltype(&::GetSystemInfo) mGetSystemInfo;
decltype(&::VirtualProtect) mVirtualProtect;
};

View File

@ -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;