Bug 1676913 - Restrict a caller of GetDependentModulePaths to xul.dll. r=mhowell

Bug 1659438 introduced an exported function `GetDependentModulePaths` in firefox.exe
so that our sandboxBroker can easily access the shared section whose handle is owned
by firefox.exe.

This patch disallows `GetDependentModulePaths` to be called from someone other than
xul.dll in order to harden the attack to tamper our shared section.  This cannot
prevent all possible attacks, but it's better than nothing.

Differential Revision: https://phabricator.services.mozilla.com/D97377
This commit is contained in:
Toshihito Kikuchi 2020-11-18 01:03:02 +00:00
parent 455ee2ab18
commit d98a83f672
4 changed files with 78 additions and 46 deletions

View File

@ -6,34 +6,15 @@
#include "mozilla/LoaderAPIInterfaces.h"
#include "freestanding/CheckForCaller.h"
#include "freestanding/LoaderPrivateAPI.h"
#if defined(_MSC_VER)
# include <intrin.h>
# pragma intrinsic(_ReturnAddress)
# define RETURN_ADDRESS() _ReturnAddress()
#elif defined(__GNUC__) || defined(__clang__)
# define RETURN_ADDRESS() \
__builtin_extract_return_addr(__builtin_return_address(0))
#endif
static bool CheckForMozglue(void* aReturnAddress) {
HMODULE callingModule;
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(aReturnAddress),
&callingModule)) {
return false;
}
return callingModule && callingModule == ::GetModuleHandleW(L"mozglue.dll");
}
namespace mozilla {
extern "C" MOZ_EXPORT nt::LoaderAPI* GetNtLoaderAPI(
nt::LoaderObserver* aNewObserver) {
const bool isCallerMozglue = CheckForMozglue(RETURN_ADDRESS());
const bool isCallerMozglue =
CheckForAddress(RETURN_ADDRESS(), L"mozglue.dll");
MOZ_ASSERT(isCallerMozglue);
if (!isCallerMozglue) {
return nullptr;

View File

@ -0,0 +1,36 @@
/* -*- 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_CheckForCaller_h
#define mozilla_freestanding_CheckForCaller_h
namespace mozilla {
#if defined(_MSC_VER)
# include <intrin.h>
# pragma intrinsic(_ReturnAddress)
# define RETURN_ADDRESS() _ReturnAddress()
#elif defined(__GNUC__) || defined(__clang__)
# define RETURN_ADDRESS() \
__builtin_extract_return_addr(__builtin_return_address(0))
#endif
template <int N>
bool CheckForAddress(void* aReturnAddress, const wchar_t (&aName)[N]) {
HMODULE callingModule;
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(aReturnAddress),
&callingModule)) {
return false;
}
return callingModule && callingModule == ::GetModuleHandleW(aName);
}
} // namespace mozilla
#endif // mozilla_freestanding_CheckForCaller_h

View File

@ -6,6 +6,8 @@
#include "SharedSection.h"
#include "CheckForCaller.h"
namespace mozilla {
namespace freestanding {
@ -214,6 +216,15 @@ LauncherVoidResult SharedSection::TransferHandle(
}
extern "C" MOZ_EXPORT uint32_t GetDependentModulePaths(uint32_t** aOutArray) {
const bool isCallerXul = CheckForAddress(RETURN_ADDRESS(), L"xul.dll");
MOZ_ASSERT(isCallerXul);
if (!isCallerXul) {
if (aOutArray) {
*aOutArray = nullptr;
}
return 0;
}
LauncherResult<SharedSection::Layout*> resultView = gSharedSection.GetView();
if (resultView.isErr()) {
if (aOutArray) {

View File

@ -68,30 +68,10 @@ static bool VerifySharedSection(SharedSection& aSharedSection) {
VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetSystemInfo);
VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, VirtualProtect);
Span<uint32_t> modulePaths = []() -> Span<uint32_t> {
auto getDependentModulePaths =
reinterpret_cast<uint32_t (*)(uint32_t**)>(::GetProcAddress(
::GetModuleHandleW(nullptr), "GetDependentModulePaths"));
if (!getDependentModulePaths) {
printf(
"TEST-FAILED | TestCrossProcessWin | "
"Failed to get a pointer to GetDependentModulePaths - %08lx.\n",
::GetLastError());
return nullptr;
}
uint32_t* modulePathArray;
uint32_t modulePathArrayLen = getDependentModulePaths(&modulePathArray);
return Span(modulePathArray, modulePathArrayLen);
}();
if (modulePaths.IsEmpty()) {
return false;
}
const uint8_t* arrayBase =
reinterpret_cast<const uint8_t*>(modulePaths.data());
for (const uint32_t& offset : modulePaths) {
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(
@ -152,6 +132,30 @@ class ChildProcess final {
return 1;
}
auto getDependentModulePaths =
reinterpret_cast<uint32_t (*)(uint32_t**)>(::GetProcAddress(
::GetModuleHandleW(nullptr), "GetDependentModulePaths"));
if (!getDependentModulePaths) {
printf(
"TEST-FAILED | TestCrossProcessWin | "
"Failed to get a pointer to GetDependentModulePaths - %08lx.\n",
::GetLastError());
return 1;
}
#if !defined(DEBUG)
// GetDependentModulePaths does not allow a caller other than xul.dll.
// Skip on Debug build because it hits MOZ_ASSERT.
uint32_t* modulePathArray;
if (getDependentModulePaths(&modulePathArray) || modulePathArray) {
printf(
"TEST-FAILED | TestCrossProcessWin | "
"GetDependentModulePaths should return zero if the caller is "
"not xul.dll.\n");
return 1;
}
#endif // !defined(DEBUG)
if (!VerifySharedSection(gSharedSection)) {
return 1;
}