mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-15 03:00:30 +00:00
Bug 1788004 - Implement a hook-based fallback for BCryptGenRandom to mitigate Rust panics. r=cmartin
BCryptGenRandom can be broken, but the Rust stdlib and the getrandom crate rely on it, and this is a source of crashes which are Rust panics. This happens the most on Windows 7 after bcryptprimitives.dll fails to load (see bug 1788004). To mitigate these crashes, we hook BCryptGenRandom if we detect that it is broken, and install a fallback based on RtlGenRandom. We only protect calls that use BCRYPT_USE_SYSTEM_PREFERRED_RNG; so code that relies on using BCryptOpenAlgorithmProvider and doesn't have its own fallback can still fail. We will hopefully remove this hook when the Rust stdlib and the getrandom crate both have their own RtlGenRandom-based fallback. Differential Revision: https://phabricator.services.mozilla.com/D170662
This commit is contained in:
parent
6a8f812670
commit
45cce5b7c0
@ -32,6 +32,7 @@
|
||||
# include "LauncherProcessWin.h"
|
||||
# include "mozilla/GeckoArgs.h"
|
||||
# include "mozilla/mscom/ProcessRuntime.h"
|
||||
# include "mozilla/WindowsBCryptInitialization.h"
|
||||
# include "mozilla/WindowsDllBlocklist.h"
|
||||
# include "mozilla/WindowsDpiInitialization.h"
|
||||
# include "mozilla/WindowsProcessMitigations.h"
|
||||
@ -401,6 +402,12 @@ int main(int argc, char* argv[], char* envp[]) {
|
||||
(void)result; // Ignore errors since some tools block DPI calls
|
||||
}
|
||||
|
||||
// BCrypt initialization for the main process. This code runs too early to
|
||||
// crash in a reportable way, so we ignore the result even in debug. If this
|
||||
// fails, let's continue and crash later when we encounter a fatal
|
||||
// BCryptGenRandom failure, if any.
|
||||
mozilla::WindowsBCryptInitialization();
|
||||
|
||||
// Once the browser process hits the main function, we no longer need
|
||||
// a writable section handle because all dependent modules have been
|
||||
// loaded.
|
||||
|
@ -91,7 +91,8 @@ MFBT_API bool GenerateRandomBytesFromOS(void* aBuffer, size_t aLength) {
|
||||
MOZ_ASSERT(aLength > 0);
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
||||
// Note: This function is used as a fallback for BCryptGenRandom in
|
||||
// WindowsBCryptInitialization(). Do not use BCryptGenRandom here!
|
||||
return !!RtlGenRandom(aBuffer, aLength);
|
||||
|
||||
#elif defined(USE_ARC4RANDOM) // defined(XP_WIN)
|
||||
|
53
mozglue/misc/WindowsBCryptInitialization.cpp
Normal file
53
mozglue/misc/WindowsBCryptInitialization.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/WindowsBCryptInitialization.h"
|
||||
|
||||
#include "mozilla/RandomNum.h"
|
||||
#include "nsWindowsDllInterceptor.h"
|
||||
|
||||
#include <bcrypt.h>
|
||||
#pragma comment(lib, "bcrypt.lib")
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static mozilla::WindowsDllInterceptor BCryptIntercept;
|
||||
static mozilla::WindowsDllInterceptor::FuncHookType<
|
||||
decltype(&::BCryptGenRandom)>
|
||||
stub_BCryptGenRandom;
|
||||
|
||||
NTSTATUS WINAPI patched_BCryptGenRandom(BCRYPT_ALG_HANDLE aAlgorithm,
|
||||
PUCHAR aBuffer, ULONG aSize,
|
||||
ULONG aFlags) {
|
||||
// If we are using the hook, we know that BCRYPT_USE_SYSTEM_PREFERRED_RNG is
|
||||
// broken, so let's use the fallback directly in that case.
|
||||
if (!aAlgorithm && (aFlags & BCRYPT_USE_SYSTEM_PREFERRED_RNG) && aBuffer &&
|
||||
aSize && mozilla::GenerateRandomBytesFromOS(aBuffer, aSize)) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
return stub_BCryptGenRandom(aAlgorithm, aBuffer, aSize, aFlags);
|
||||
}
|
||||
|
||||
bool WindowsBCryptInitialization() {
|
||||
UCHAR buffer[32];
|
||||
NTSTATUS status = ::BCryptGenRandom(nullptr, buffer, sizeof(buffer),
|
||||
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
if (NT_SUCCESS(status)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BCryptIntercept.Init(L"bcrypt.dll");
|
||||
if (!stub_BCryptGenRandom.Set(BCryptIntercept, "BCryptGenRandom",
|
||||
patched_BCryptGenRandom)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
status = ::BCryptGenRandom(nullptr, buffer, sizeof(buffer),
|
||||
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
return NT_SUCCESS(status);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
31
mozglue/misc/WindowsBCryptInitialization.h
Normal file
31
mozglue/misc/WindowsBCryptInitialization.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* -*- 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_WindowsBCryptInitialization_h
|
||||
#define mozilla_WindowsBCryptInitialization_h
|
||||
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// This functions ensures that calling BCryptGenRandom will work later:
|
||||
// - It triggers a first call to BCryptGenRandom() to pre-load
|
||||
// bcryptPrimitives.dll while the current thread still has an unrestricted
|
||||
// impersonation token. We need to perform that operation in sandboxed
|
||||
// processes to warmup the BCryptGenRandom() call that is used by others,
|
||||
// especially Rust. See bug 1746524, bug 1751094, bug 1751177.
|
||||
// - If that first call fails, we detect it and hook BCryptGenRandom to
|
||||
// install a fallback based on RtlGenRandom for calls that use flag
|
||||
// BCRYPT_USE_SYSTEM_PREFERRED_RNG. We need this because BCryptGenRandom
|
||||
// failures are currently fatal and on some machines BCryptGenRandom is
|
||||
// broken (usually Windows 7). We hope to remove this hook in the future
|
||||
// once the Rust stdlib and the getrandom crate both have their own
|
||||
// RtlGenRandom-based fallback. See bug 1788004.
|
||||
MFBT_API bool WindowsBCryptInitialization();
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_WindowsBCryptInitialization_h
|
@ -77,6 +77,7 @@ if CONFIG["OS_ARCH"] == "WINNT":
|
||||
"ImportDir.h",
|
||||
"MozProcessMitigationDynamicCodePolicy.h",
|
||||
"NativeNt.h",
|
||||
"WindowsBCryptInitialization.h",
|
||||
"WindowsDpiInitialization.h",
|
||||
"WindowsEnumProcessModules.h",
|
||||
"WindowsMapRemoteView.h",
|
||||
@ -90,6 +91,7 @@ if CONFIG["OS_ARCH"] == "WINNT":
|
||||
SOURCES += [
|
||||
"GetKnownFolderPath.cpp",
|
||||
"TimeStamp_windows.cpp",
|
||||
"WindowsBCryptInitialization.cpp",
|
||||
"WindowsDllMain.cpp",
|
||||
"WindowsDpiInitialization.cpp",
|
||||
"WindowsMapRemoteView.cpp",
|
||||
|
@ -1180,6 +1180,14 @@ class WindowsDllDetourPatcher final
|
||||
} else if (*origBytes >= 0xb8 && *origBytes <= 0xbf) {
|
||||
// mov r32, imm32
|
||||
COPY_CODES(5);
|
||||
} else if (*origBytes == 0x8b && (origBytes[1] & kMaskMod) == kModReg) {
|
||||
// 8B /r: mov r32, r/m32
|
||||
COPY_CODES(2);
|
||||
} else if (*origBytes == 0xf7 &&
|
||||
(origBytes[1] & (kMaskMod | kMaskReg)) ==
|
||||
(kModReg | (0 << kRegFieldShift))) {
|
||||
// F7 /0 id: test r/m32, imm32
|
||||
COPY_CODES(6);
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
|
||||
return;
|
||||
|
@ -14,6 +14,9 @@
|
||||
#include <winternl.h>
|
||||
#include <processthreadsapi.h>
|
||||
|
||||
#include <bcrypt.h>
|
||||
#pragma comment(lib, "bcrypt.lib")
|
||||
|
||||
#include "AssemblyPayloads.h"
|
||||
#include "mozilla/DynamicallyLinkedFunctionPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
@ -1380,6 +1383,8 @@ extern "C" int wmain(int argc, wchar_t* argv[]) {
|
||||
TEST_HOOK("user32.dll", InSendMessageEx, Equals, ISMEX_NOSEND) &&
|
||||
TEST_HOOK("user32.dll", SendMessageTimeoutW, Equals, 0) &&
|
||||
TEST_HOOK("user32.dll", SetCursorPos, NotEquals, FALSE) &&
|
||||
TEST_HOOK("bcrypt.dll", BCryptGenRandom, Equals,
|
||||
static_cast<NTSTATUS>(STATUS_INVALID_HANDLE)) &&
|
||||
#if !defined(_M_ARM64)
|
||||
TEST_HOOK("imm32.dll", ImmGetContext, Equals, nullptr) &&
|
||||
#endif // !defined(_M_ARM64)
|
||||
|
@ -25,6 +25,7 @@
|
||||
# endif
|
||||
# include "mozilla/ScopeExit.h"
|
||||
# include "mozilla/WinDllServices.h"
|
||||
# include "mozilla/WindowsBCryptInitialization.h"
|
||||
# include "WinUtils.h"
|
||||
# ifdef ACCESSIBILITY
|
||||
# include "mozilla/GeckoArgs.h"
|
||||
@ -534,24 +535,22 @@ nsresult XRE_InitChildProcess(int aArgc, char* aArgv[],
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(MOZ_SANDBOX) && defined(XP_WIN)
|
||||
#if defined(XP_WIN)
|
||||
# if defined(MOZ_SANDBOX)
|
||||
if (aChildData->sandboxBrokerServices) {
|
||||
SandboxBroker::Initialize(aChildData->sandboxBrokerServices);
|
||||
SandboxBroker::GeckoDependentInitialize();
|
||||
}
|
||||
# endif // defined(MOZ_SANDBOX)
|
||||
|
||||
// Call BCryptGenRandom() to pre-load bcryptPrimitives.dll while the current
|
||||
// thread still has an unrestricted impersonation token. We need to perform
|
||||
// that operation to warmup the BCryptGenRandom() call that is used by
|
||||
// others, especially rust. See bug 1746524, bug 1751094, bug 1751177
|
||||
UCHAR buffer[32];
|
||||
NTSTATUS status = BCryptGenRandom(NULL, // hAlgorithm
|
||||
buffer, // pbBuffer
|
||||
sizeof(buffer), // cbBuffer
|
||||
BCRYPT_USE_SYSTEM_PREFERRED_RNG // dwFlags
|
||||
);
|
||||
MOZ_RELEASE_ASSERT(status == STATUS_SUCCESS);
|
||||
#endif // defined(MOZ_SANDBOX) && defined(XP_WIN)
|
||||
{
|
||||
// BCrypt initialization for child processes. If this fails, let's continue
|
||||
// and crash later when we encounter a fatal BCryptGenRandom failure (if
|
||||
// any), unless we are in a debug build.
|
||||
DebugOnly<bool> result = mozilla::WindowsBCryptInitialization();
|
||||
MOZ_ASSERT(result);
|
||||
}
|
||||
#endif // defined(XP_WIN)
|
||||
|
||||
{
|
||||
// This is a lexical scope for the MessageLoop below. We want it
|
||||
|
Loading…
Reference in New Issue
Block a user