diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /browser/app/winlauncher/freestanding/SharedSection.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/app/winlauncher/freestanding/SharedSection.cpp')
-rw-r--r-- | browser/app/winlauncher/freestanding/SharedSection.cpp | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/browser/app/winlauncher/freestanding/SharedSection.cpp b/browser/app/winlauncher/freestanding/SharedSection.cpp new file mode 100644 index 0000000000..66891ac80f --- /dev/null +++ b/browser/app/winlauncher/freestanding/SharedSection.cpp @@ -0,0 +1,267 @@ +/* -*- 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/. */ + +#include "SharedSection.h" + +#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 { + +SharedSection gSharedSection; + +bool Kernel32ExportsSolver::IsInitialized() const { + return mState == State::Initialized || IsResolved(); +} + +bool Kernel32ExportsSolver::IsResolved() const { + return mState == State::Resolved; +} + +// Why don't we use ::GetProcAddress? +// If the export table of kernel32.dll is tampered in the current process, +// we cannot transfer an RVA because the function pointed by the RVA may not +// exist in a target process. +// We can use ::GetProcAddress with additional check to detect tampering, but +// FindExportAddressTableEntry fits perfectly here because it returns nullptr +// if the target entry is outside the image, which means it's tampered or +// forwarded to another DLL. +#define INIT_FUNCTION(exports, name) \ + do { \ + auto rvaToFunction = exports.FindExportAddressTableEntry(#name); \ + if (!rvaToFunction) { \ + return; \ + } \ + m##name = reinterpret_cast<decltype(m##name)>(*rvaToFunction); \ + } while (0) + +#define RESOLVE_FUNCTION(base, name) \ + m##name = reinterpret_cast<decltype(m##name)>( \ + base + reinterpret_cast<uintptr_t>(m##name)) + +void Kernel32ExportsSolver::Init() { + if (mState == State::Initialized || mState == State::Resolved) { + return; + } + + interceptor::MMPolicyInProcess policy; + auto k32Exports = nt::PEExportSection<interceptor::MMPolicyInProcess>::Get( + ::GetModuleHandleW(L"kernel32.dll"), policy); + if (!k32Exports) { + return; + } + + // 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); + + mState = State::Initialized; +} + +void Kernel32ExportsSolver::ResolveInternal() { + if (mState == State::Resolved) { + return; + } + + MOZ_RELEASE_ASSERT(mState == State::Initialized); + + UNICODE_STRING k32Name; + ::RtlInitUnicodeString(&k32Name, L"kernel32.dll"); + + // We cannot use GetModuleHandleW because this code can be called + // before IAT is resolved. + auto k32Module = nt::GetModuleHandleFromLeafName(k32Name); + MOZ_RELEASE_ASSERT(k32Module.isOk()); + + uintptr_t k32Base = + nt::PEHeaders::HModuleToBaseAddr<uintptr_t>(k32Module.unwrap()); + + RESOLVE_FUNCTION(k32Base, FlushInstructionCache); + RESOLVE_FUNCTION(k32Base, GetModuleHandleW); + RESOLVE_FUNCTION(k32Base, GetSystemInfo); + RESOLVE_FUNCTION(k32Base, VirtualProtect); + + mState = State::Resolved; +} + +/* static */ +ULONG NTAPI Kernel32ExportsSolver::ResolveOnce(PRTL_RUN_ONCE aRunOnce, + PVOID aParameter, PVOID*) { + reinterpret_cast<Kernel32ExportsSolver*>(aParameter)->ResolveInternal(); + return TRUE; +} + +void Kernel32ExportsSolver::Resolve(RTL_RUN_ONCE& aRunOnce) { + ::RtlRunOnceExecuteOnce(&aRunOnce, &ResolveOnce, this, nullptr); +} + +HANDLE SharedSection::sSectionHandle = nullptr; +void* SharedSection::sWriteCopyView = nullptr; + +void SharedSection::Reset(HANDLE aNewSecionObject) { + if (sWriteCopyView) { + nt::AutoMappedView view(sWriteCopyView); + sWriteCopyView = nullptr; + } + + if (sSectionHandle != aNewSecionObject) { + if (sSectionHandle) { + ::CloseHandle(sSectionHandle); + } + sSectionHandle = aNewSecionObject; + } +} + +void SharedSection::ConvertToReadOnly() { + if (!sSectionHandle) { + return; + } + + HANDLE readonlyHandle; + if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle, + nt::kCurrentProcess, &readonlyHandle, GENERIC_READ, + FALSE, 0)) { + return; + } + + Reset(readonlyHandle); +} + +LauncherVoidResult SharedSection::Init(const nt::PEHeaders& aPEHeaders) { + static_assert( + kSharedViewSize >= sizeof(Layout), + "kSharedViewSize is too small to represent SharedSection::Layout."); + + HANDLE section = + ::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, + kSharedViewSize, nullptr); + if (!section) { + return LAUNCHER_ERROR_FROM_LAST(); + } + Reset(section); + + // The initial contents of the pages in a file mapping object backed by + // the operating system paging file are 0 (zero). No need to zero it out + // ourselves. + // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw + nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE); + if (!writableView) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + 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); + } + + return Ok(); +} + +LauncherResult<SharedSection::Layout*> SharedSection::GetView() { + if (!sWriteCopyView) { + nt::AutoMappedView view(sSectionHandle, PAGE_WRITECOPY); + if (!view) { + return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error()); + } + sWriteCopyView = view.release(); + } + return reinterpret_cast<SharedSection::Layout*>(sWriteCopyView); +} + +LauncherVoidResult SharedSection::TransferHandle( + nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess, + HANDLE* aDestinationAddress) { + HANDLE remoteHandle; + if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle, + aTransferMgr.RemoteProcess(), &remoteHandle, + aDesiredAccess, FALSE, 0)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + return aTransferMgr.Transfer(aDestinationAddress, &remoteHandle, + 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() { + // 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; + } + + // 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 resultView.inspect()->mModulePathArray; +#else + return nullptr; +#endif +} + +} // namespace freestanding +} // namespace mozilla |