tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 2dc50a92afaf41baf1ebf4fc342af04d59b051f6
parent 935e3b3498345cd6d81660023f799c9fe52981e8
Author: Yannis Juglaret <yjuglaret@mozilla.com>
Date:   Mon, 15 Dec 2025 17:47:17 +0000

Bug 1998650 - Only favor system DLLs if we can guarantee compatibility. r=bobowen,win-reviewers,gstoll

For compatibility reasons, we cannot rely on system-wide Visual C++
runtime DLLs unless they are at least in the version we ship. This patch
fixes this concern by letting the launcher process decide whether or not
we should favor system DLLs based on their version. In case the launcher
process is disabled, the browser process will decide instead.

Differential Revision: https://phabricator.services.mozilla.com/D275822

Diffstat:
Mbrowser/app/winlauncher/LauncherProcessWin.cpp | 115++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mbrowser/app/winlauncher/moz.build | 1+
Mmozglue/misc/WindowsProcessMitigations.cpp | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mmozglue/misc/WindowsProcessMitigations.h | 3+++
Msecurity/sandbox/win/src/sandboxbroker/sandboxBroker.cpp | 35++++++++++++++++++++++++-----------
5 files changed, 164 insertions(+), 41 deletions(-)

diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp @@ -10,7 +10,6 @@ #include "mozilla/CmdLineAndEnvUtils.h" #include "mozilla/DebugOnly.h" -#include "mozilla/DynamicallyLinkedFunctionPtr.h" #include "mozilla/glue/Debug.h" #include "mozilla/GeckoArgs.h" #include "mozilla/Maybe.h" @@ -18,12 +17,14 @@ #include "mozilla/SafeMode.h" #include "mozilla/UniquePtr.h" #include "mozilla/WindowsConsole.h" +#include "mozilla/WindowsProcessMitigations.h" #include "mozilla/WindowsVersion.h" #include "mozilla/WinHeaderOnlyUtils.h" #include "nsWindowsHelpers.h" #include <windows.h> #include <processthreadsapi.h> +#include <shlwapi.h> #include "DllBlocklistInit.h" #include "ErrorHandler.h" @@ -110,16 +111,82 @@ static nsReturnRef<HANDLE> CreateJobAndAssignProcess(HANDLE aProcess) { return job.out(); } -#if !defined( \ - PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON) -# define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON \ - (0x00000001ULL << 60) -#endif // !defined(PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON) +enum class VCRuntimeDLLDir : bool { + Application, + System, +}; +static bool GetMSVCP140VersionInfo(VCRuntimeDLLDir aDir, + uint64_t& aOutVersion) { + wchar_t dllPath[MAX_PATH]; + if (aDir == VCRuntimeDLLDir::Application) { + DWORD size = ::GetModuleFileNameW(nullptr, dllPath, MAX_PATH); + if (!size || + (size == MAX_PATH && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) || + !::PathRemoveFileSpecW(dllPath)) { + return false; + } + } else { + MOZ_ASSERT(aDir == VCRuntimeDLLDir::System); + UINT size = ::GetSystemDirectoryW(dllPath, MAX_PATH); + if (!size || size >= MAX_PATH) { + return false; + } + } + + if (!::PathAppendW(dllPath, L"msvcp140.dll")) { + return false; + } + HMODULE crt = + ::LoadLibraryExW(dllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE); + if (!crt) { + return false; + } + + mozilla::nt::PEHeaders headers{crt}; + bool result = headers.GetVersionInfo(aOutVersion); + ::FreeLibrary(crt); + return result; +} + +/** + * Choose whether we want to favor loading DLLs from the system directory over + * the application directory. This choice automatically propagates to all child + * processes. In particular, it determines whether child processes will load + * Visual C++ runtime DLLs from the system or the application directory at + * startup. + * + * Whenever possible, we want all processes to favor loading DLLs from the + * system directory. But if old Visual C++ runtime DLLs are installed + * system-wide, then we must favor loading from the application directory + * instead to ensure compatibility, at least during startup. So in this case we + * only apply the delayed variant of the mitigation and only in sandboxed + * processes, which is the best compromise (see SandboxBroker::LaunchApp). + * + * This function is called from the launcher process *and* the browser process. + * This is because if the launcher process is disabled, we still want the + * browser process to go through this code so that it enforces the correct + * choice for itself and for child processes. + */ +static void EnablePreferLoadFromSystem32IfCompatible() { + // We may already have the mitigation if we are the browser process and we + // inherited it from the launcher process. + if (!mozilla::IsPreferLoadFromSystem32Available() || + mozilla::IsPreferLoadFromSystem32Enabled()) { + return; + } -#if !defined(PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF) -# define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF \ - (0x00000002ULL << 40) -#endif // !defined(PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF) + // Only bail out if (1) there is a conflict because the two DLLs exist *and* + // (2) the version of the system DLL is problematic. + uint64_t systemDirVersion = 0, appDirVersion = 0; + if (GetMSVCP140VersionInfo(VCRuntimeDLLDir::System, systemDirVersion) && + GetMSVCP140VersionInfo(VCRuntimeDLLDir::Application, appDirVersion) && + systemDirVersion < appDirVersion) { + return; + } + + mozilla::DebugOnly<bool> setOk = mozilla::EnablePreferLoadFromSystem32(); + MOZ_ASSERT(setOk); +} /** * Any mitigation policies that should be set on the browser process should go @@ -127,10 +194,11 @@ static nsReturnRef<HANDLE> CreateJobAndAssignProcess(HANDLE aProcess) { */ static void SetMitigationPolicies(mozilla::ProcThreadAttributes& aAttrs, const bool aIsSafeMode) { - if (mozilla::IsWin10AnniversaryUpdateOrLater()) { - aAttrs.AddMitigationPolicy( - PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON); - } + // Note: Do *not* handle IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON here. For this + // mitigation we rely on EnablePreferLoadFromSystem32IfCompatible(). + // The launcher process or the browser process will choose whether we + // want to apply the mitigation or not, and child processes will + // automatically inherit that choice. #if defined(_M_ARM64) // Disable CFG on older versions of ARM64 Windows to avoid a crash in COM. @@ -277,6 +345,9 @@ Maybe<int> LauncherMain(int& argc, wchar_t* argv[]) { return Nothing(); } + // Called from the launcher process *and* the browser process. + EnablePreferLoadFromSystem32IfCompatible(); + #if defined(MOZ_LAUNCHER_PROCESS) LauncherRegistryInfo regInfo; Maybe<bool> runAsLauncher = RunAsLauncherProcess(regInfo, argc, argv); @@ -300,22 +371,6 @@ Maybe<int> LauncherMain(int& argc, wchar_t* argv[]) { return Nothing(); } - // Make sure that the launcher process itself has image load policies set - if (IsWin10AnniversaryUpdateOrLater()) { - static const StaticDynamicallyLinkedFunctionPtr< - decltype(&SetProcessMitigationPolicy)> - pSetProcessMitigationPolicy(L"kernel32.dll", - "SetProcessMitigationPolicy"); - if (pSetProcessMitigationPolicy) { - PROCESS_MITIGATION_IMAGE_LOAD_POLICY imgLoadPol = {}; - imgLoadPol.PreferSystem32Images = 1; - - DebugOnly<BOOL> setOk = pSetProcessMitigationPolicy( - ProcessImageLoadPolicy, &imgLoadPol, sizeof(imgLoadPol)); - MOZ_ASSERT(setOk); - } - } - #if defined(MOZ_SANDBOX) // Ensure the relevant mitigations are enforced. mozilla::sandboxing::ApplyParentProcessMitigations(); diff --git a/browser/app/winlauncher/moz.build b/browser/app/winlauncher/moz.build @@ -24,6 +24,7 @@ OS_LIBS += [ "oleaut32", "ole32", "rpcrt4", + "shlwapi", "version", ] diff --git a/mozglue/misc/WindowsProcessMitigations.cpp b/mozglue/misc/WindowsProcessMitigations.cpp @@ -9,6 +9,7 @@ #include <processthreadsapi.h> #include "mozilla/DynamicallyLinkedFunctionPtr.h" +#include "mozilla/WindowsVersion.h" static_assert(sizeof(PROCESS_MITIGATION_DYNAMIC_CODE_POLICY) == 4); @@ -97,4 +98,54 @@ MFBT_API bool IsUserShadowStackEnabled() { return polInfo.EnableUserShadowStack; } +MFBT_API bool IsPreferLoadFromSystem32Available() { + return mozilla::IsWin10AnniversaryUpdateOrLater(); +} + +MFBT_API bool IsPreferLoadFromSystem32Enabled() { + auto pGetProcessMitigationPolicy = FetchGetProcessMitigationPolicyFunc(); + if (!pGetProcessMitigationPolicy) { + return false; + } + + PROCESS_MITIGATION_IMAGE_LOAD_POLICY imgLoadPol{}; + if (!pGetProcessMitigationPolicy(::GetCurrentProcess(), + ProcessImageLoadPolicy, &imgLoadPol, + sizeof(imgLoadPol))) { + return false; + } + + return imgLoadPol.PreferSystem32Images; +} + +MFBT_API bool EnablePreferLoadFromSystem32() { + auto pGetProcessMitigationPolicy = FetchGetProcessMitigationPolicyFunc(); + if (!pGetProcessMitigationPolicy) { + return false; + } + + static const mozilla::StaticDynamicallyLinkedFunctionPtr< + decltype(&::SetProcessMitigationPolicy)> + pSetProcessMitigationPolicy(L"kernel32.dll", + "SetProcessMitigationPolicy"); + if (!pSetProcessMitigationPolicy) { + return false; + } + + PROCESS_MITIGATION_IMAGE_LOAD_POLICY imgLoadPol{}; + if (!pGetProcessMitigationPolicy(::GetCurrentProcess(), + ProcessImageLoadPolicy, &imgLoadPol, + sizeof(imgLoadPol))) { + return false; + } + + if (imgLoadPol.PreferSystem32Images) { + return true; + } + + imgLoadPol.PreferSystem32Images = 1; + return pSetProcessMitigationPolicy(ProcessImageLoadPolicy, &imgLoadPol, + sizeof(imgLoadPol)); +} + } // namespace mozilla diff --git a/mozglue/misc/WindowsProcessMitigations.h b/mozglue/misc/WindowsProcessMitigations.h @@ -16,6 +16,9 @@ MFBT_API void SetWin32kLockedDownInPolicy(); MFBT_API bool IsDynamicCodeDisabled(); MFBT_API bool IsEafPlusEnabled(); MFBT_API bool IsUserShadowStackEnabled(); +MFBT_API bool IsPreferLoadFromSystem32Available(); +MFBT_API bool IsPreferLoadFromSystem32Enabled(); +MFBT_API bool EnablePreferLoadFromSystem32(); } // namespace mozilla diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp @@ -471,19 +471,36 @@ Result<Ok, mozilla::ipc::LaunchError> SandboxBroker::LaunchApp( "Setting the reduced set of flags should always succeed"); } + sandbox::MitigationFlags delayedMitigations = + config->GetDelayedProcessMitigations(); + + // Only prefer loading from the system directory as a delayed mitigation, and + // always enable this delayed mitigation. This means that: + // - if the launcher or browser process chose to apply the mitigation, child + // processes will have it enabled at startup automatically anyway; + // - even if the launcher or browser process chose not to apply the + // mitigation, at least sandboxed child processes will run with the + // mitigation once the sandbox starts (by this time, they will already + // have loaded the Visual C++ runtime DLLs, so these are no longer a + // concern; also, although some sandboxed child processes can start new + // processes, they never start new *Firefox* processes). + // Refer to EnablePreferLoadFromSystem32IfCompatible for more details. + MOZ_ASSERT(!(config->GetProcessMitigations() & + sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32)); + delayedMitigations |= sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32; + // Bug 1936749: MpDetours.dll injection is incompatible with ACG. constexpr sandbox::MitigationFlags kDynamicCodeFlags = sandbox::MITIGATION_DYNAMIC_CODE_DISABLE | sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT; - sandbox::MitigationFlags delayedMitigations = - config->GetDelayedProcessMitigations(); if ((delayedMitigations & kDynamicCodeFlags) && ::GetModuleHandleW(L"MpDetours.dll")) { delayedMitigations &= ~kDynamicCodeFlags; - SANDBOX_SUCCEED_OR_CRASH( - config->SetDelayedProcessMitigations(delayedMitigations)); } + SANDBOX_SUCCEED_OR_CRASH( + config->SetDelayedProcessMitigations(delayedMitigations)); + EnsureAppLockerAccess(config); // If logging enabled, set up the policy. @@ -1108,8 +1125,7 @@ void SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel, sandbox::MITIGATION_DEP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE | sandbox::MITIGATION_KTM_COMPONENT | sandbox::MITIGATION_FSCTL_DISABLED | sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE | - sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL | - sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32; + sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL; #if defined(_M_ARM64) // Disable CFG on older versions of ARM64 Windows to avoid a crash in COM. @@ -1430,8 +1446,7 @@ bool SandboxBroker::SetSecurityLevelForRDDProcess() { sandbox::MITIGATION_NONSYSTEM_FONT_DISABLE | sandbox::MITIGATION_KTM_COMPONENT | sandbox::MITIGATION_FSCTL_DISABLED | sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE | - sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL | - sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32; + sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL; if (StaticPrefs::security_sandbox_rdd_shadow_stack_enabled()) { mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE; @@ -1514,8 +1529,7 @@ bool SandboxBroker::SetSecurityLevelForSocketProcess() { sandbox::MITIGATION_NONSYSTEM_FONT_DISABLE | sandbox::MITIGATION_KTM_COMPONENT | sandbox::MITIGATION_FSCTL_DISABLED | sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE | - sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL | - sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32; + sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL; if (StaticPrefs::security_sandbox_socket_shadow_stack_enabled()) { mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE; @@ -1587,7 +1601,6 @@ struct UtilitySandboxProps { sandbox::MITIGATION_KTM_COMPONENT | sandbox::MITIGATION_FSCTL_DISABLED | sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE | sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL | - sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32 | sandbox::MITIGATION_CET_COMPAT_MODE; sandbox::MitigationFlags mExcludedInitialMitigations = 0;