From 19a5f37464a01290a2dc3a4f66cac9a109bcc637 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 27 Feb 2025 15:30:02 +0000 Subject: [PATCH] Bug 1950112: Use win32k lockdown and ACG for GMP process for non-widevine CDMs. r=yjuglaret,win-reviewers Differential Revision: https://phabricator.services.mozilla.com/D239356 --- ipc/glue/GeckoChildProcessHost.cpp | 22 ++++++--------- modules/libpref/init/StaticPrefList.yaml | 13 ++++++--- .../win/src/sandboxbroker/sandboxBroker.cpp | 27 ++++++++++++++---- .../win/src/sandboxbroker/sandboxBroker.h | 6 ++-- toolkit/xre/nsAppRunner.cpp | 7 +++++ widget/windows/nsAppShell.cpp | 28 +++++++++++-------- 6 files changed, 67 insertions(+), 36 deletions(-) diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index aa3f58360ffd47..7872a19ce0ebe7 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -1526,11 +1526,6 @@ Result WindowsProcessLauncher::DoSetup() { FilePath exePath; BinPathType pathType = GetPathToBinary(exePath, mProcessType); -# if defined(MOZ_SANDBOX) || defined(_ARM64_) - const bool isGMP = mProcessType == GeckoProcessType_GMPlugin; - const bool isWidevine = isGMP && Contains(mChildArgs, "gmp-widevinecdm"); -# endif // defined(MOZ_SANDBOX) || defined(_ARM64_) - mCmdLine.emplace(exePath.ToWStringHack()); if (pathType == BinPathType::Self) { @@ -1574,14 +1569,15 @@ Result WindowsProcessLauncher::DoSetup() { break; case GeckoProcessType_GMPlugin: if (!PR_GetEnv("MOZ_DISABLE_GMP_SANDBOX")) { - // The Widevine CDM on Windows can only load at USER_RESTRICTED, - // not at USER_LOCKDOWN. So look in the command line arguments - // to see if we're loading the path to the Widevine CDM, and if - // so use sandbox level USER_RESTRICTED instead of USER_LOCKDOWN. - auto level = - isWidevine ? SandboxBroker::Restricted : SandboxBroker::LockDown; - if (NS_WARN_IF( - !mResults.mSandboxBroker->SetSecurityLevelForGMPlugin(level))) { + auto gmpSandboxKind = GMPSandboxKind::Default; + if (Contains(mChildArgs, "gmp-widevinecdm")) { + gmpSandboxKind = GMPSandboxKind::Widevine; + } else if (Contains(mChildArgs, "gmp-clearkey")) { + gmpSandboxKind = GMPSandboxKind::Clearkey; + } + + if (NS_WARN_IF(!mResults.mSandboxBroker->SetSecurityLevelForGMPlugin( + gmpSandboxKind))) { return Err(LaunchError("SetSecurityLevelForGMPlugin")); } mUseSandbox = true; diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 983e581a60b703..4bbb8c1850fbbb 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -16104,14 +16104,19 @@ # true means win32k system calls are not permitted. - name: security.sandbox.content.win32k-disable type: RelaxedAtomicBool - value: true + value: @IS_NIGHTLY_BUILD@ mirror: always - # Note: win32k is currently _not_ disabled for GMP due to intermittent test - # failures, where the GMP process fails very early. See bug 1449348. + # Whether win32k is disabled for compatible plugins. - name: security.sandbox.gmp.win32k-disable type: RelaxedAtomicBool - value: false + value: @IS_NIGHTLY_BUILD@ + mirror: always + + # Whether ACG is enabled (dynamic code blocked) for compatible plugins. +- name: security.sandbox.gmp.acg.enabled + type: RelaxedAtomicBool + value: true mirror: always # Whether win32k is disabled for socket processes. diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp index 79d79c23e31c5e..f7ef6cc151db9c 100644 --- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp @@ -1750,8 +1750,8 @@ bool SandboxBroker::SetSecurityLevelForUtilityProcess( } } -bool SandboxBroker::SetSecurityLevelForGMPlugin(SandboxLevel aLevel, - bool aIsRemoteLaunch) { +bool SandboxBroker::SetSecurityLevelForGMPlugin( + GMPSandboxKind aGMPSandboxKind) { if (!mPolicy) { return false; } @@ -1761,8 +1761,10 @@ bool SandboxBroker::SetSecurityLevelForGMPlugin(SandboxLevel aLevel, SANDBOX_ENSURE_SUCCESS( result, "SetJobLevel should never fail with these arguments, what happened?"); - auto level = (aLevel == Restricted) ? sandbox::USER_RESTRICTED - : sandbox::USER_LOCKDOWN; + + // The Widevine CDM on Windows can only load at USER_RESTRICTED + auto level = (aGMPSandboxKind == Widevine) ? sandbox::USER_RESTRICTED + : sandbox::USER_LOCKDOWN; result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, level); SANDBOX_ENSURE_SUCCESS( result, @@ -1803,13 +1805,28 @@ bool SandboxBroker::SetSecurityLevelForGMPlugin(SandboxLevel aLevel, result = mPolicy->SetProcessMitigations(mitigations); SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations."); - if (StaticPrefs::security_sandbox_gmp_win32k_disable()) { + // win32k is currently not disabled for clearkey due to WMF decoding or + // widevine due to intermittent test failures, where the GMP process fails + // very early. See bug 1449348. + if (StaticPrefs::security_sandbox_gmp_win32k_disable() && + aGMPSandboxKind != Widevine && aGMPSandboxKind != Clearkey) { result = AddWin32kLockdownPolicy(mPolicy, true); SANDBOX_ENSURE_SUCCESS(result, "Failed to add the win32k lockdown policy"); } mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS | sandbox::MITIGATION_DLL_SEARCH_ORDER; + if (StaticPrefs::security_sandbox_gmp_acg_enabled()) { + auto acgMitigation = sandbox::MITIGATION_DYNAMIC_CODE_DISABLE; + if (aGMPSandboxKind == Widevine) { + // We can't guarantee that widevine won't use dynamic code. + acgMitigation = 0; + } else if (aGMPSandboxKind == Clearkey) { + // Clearkey uses system decoding libraries. + acgMitigation = DynamicCodeFlagForSystemMediaLibraries(); + } + mitigations |= acgMitigation; + } result = mPolicy->SetDelayedProcessMitigations(mitigations); SANDBOX_ENSURE_SUCCESS(result, diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h index 6b362071a33fd8..adcd1c23e9a805 100644 --- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h @@ -26,6 +26,8 @@ class TargetPolicy; namespace mozilla { +enum GMPSandboxKind { Default, Widevine, Clearkey }; + class SandboxBroker { public: SandboxBroker(); @@ -56,9 +58,7 @@ class SandboxBroker { bool SetSecurityLevelForRDDProcess(); bool SetSecurityLevelForSocketProcess(); - enum SandboxLevel { LockDown, Restricted }; - bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel, - bool aIsRemoteLaunch = false); + bool SetSecurityLevelForGMPlugin(GMPSandboxKind aGMPSandboxKind); bool SetSecurityLevelForUtilityProcess(mozilla::ipc::SandboxingKind aSandbox); // File system permissions diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 2018ae3c3d33bf..bdd95b8939f1c9 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -6272,6 +6272,13 @@ bool XRE_IsE10sParentProcess() { #undef GECKO_PROCESS_TYPE bool XRE_UseNativeEventProcessing() { +#if defined(XP_WIN) + // If win32k is locked down we can't use native event processing. + if (IsWin32kLockedDown()) { + return false; + } +#endif + switch (XRE_GetProcessType()) { #if defined(XP_MACOSX) || defined(XP_WIN) case GeckoProcessType_RDD: diff --git a/widget/windows/nsAppShell.cpp b/widget/windows/nsAppShell.cpp index 912ff0f2378132..f558bfb311bb58 100644 --- a/widget/windows/nsAppShell.cpp +++ b/widget/windows/nsAppShell.cpp @@ -619,17 +619,23 @@ nsresult nsAppShell::Init() { if (nsresult rv = this->InitEventWindow(); NS_FAILED(rv)) { return rv; } - } else if (XRE_IsContentProcess() && !IsWin32kLockedDown()) { - // We're not generally processing native events, but still using GDI and we - // still have some internal windows, e.g. from calling CoInitializeEx. - // So we use a class that will do a single event pump where previously we - // might have processed multiple events to make sure any occasional messages - // to these windows are processed. This also allows any internal Windows - // messages to be processed to ensure the GDI data remains fresh. - nsCOMPtr threadInt = - do_QueryInterface(NS_GetCurrentThread()); - if (threadInt) { - threadInt->SetObserver(new SingleNativeEventPump()); + } else { + // Load winmm.dll because it is still needed by our event loop and might not + // get loaded before we lower the sandbox. + ::LoadLibraryW(L"winmm.dll"); + + if (XRE_IsContentProcess() && !IsWin32kLockedDown()) { + // We're not generally processing native events, but still using GDI and + // we still have some internal windows, e.g. from calling CoInitializeEx. + // So we use a class that will do a single event pump where previously we + // might have processed multiple events to make sure any occasional + // messages to these windows are processed. This also allows any internal + // Windows messages to be processed to ensure the GDI data remains fresh. + nsCOMPtr threadInt = + do_QueryInterface(NS_GetCurrentThread()); + if (threadInt) { + threadInt->SetObserver(new SingleNativeEventPump()); + } } }