Bug 1511078: Add LauncherRegistryInfo as a temporary mechanism for runtime disabling of launcher process; r=mhowell

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

--HG--
rename : browser/app/winlauncher/NativeNt.h => mozglue/misc/NativeNt.h
rename : browser/app/winlauncher/test/TestNativeNt.cpp => mozglue/tests/TestNativeNt.cpp
rename : browser/app/winlauncher/LauncherResult.h => toolkit/xre/LauncherResult.h
extra : moz-landing-system : lando
This commit is contained in:
Aaron Klotz 2019-01-15 20:19:46 +00:00
parent 956e93c403
commit 64310051e7
23 changed files with 1346 additions and 48 deletions

View File

@ -4,10 +4,10 @@
* 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 "NativeNt.h"
#include "nsWindowsDllInterceptor.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/NativeNt.h"
#include "mozilla/Types.h"
#include "mozilla/WindowsDllBlocklist.h"
#include "mozilla/WinHeaderOnlyUtils.h"

View File

@ -9,7 +9,7 @@
#include <windows.h>
#include "LauncherResult.h"
#include "mozilla/LauncherResult.h"
namespace mozilla {

View File

@ -6,11 +6,17 @@
#include "ErrorHandler.h"
#include "mozilla/LauncherRegistryInfo.h"
namespace mozilla {
void HandleLauncherError(const LauncherError& aError) {
// This is a placeholder error handler. We'll add telemetry and a fallback
// error log in future revisions.
LauncherRegistryInfo regInfo;
regInfo.DisableDueToFailure();
WindowsError::UniqueString msg = aError.mError.AsString();
if (!msg) {
return;

View File

@ -8,8 +8,7 @@
#define mozilla_ErrorHandler_h
#include "mozilla/Assertions.h"
#include "LauncherResult.h"
#include "mozilla/LauncherResult.h"
namespace mozilla {

View File

@ -8,12 +8,11 @@
#include "mozilla/Assertions.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/LauncherResult.h"
#include "mozilla/mscom/COMApartmentRegion.h"
#include "mozilla/RefPtr.h"
#include "nsWindowsHelpers.h"
#include "LauncherResult.h"
// For _bstr_t and _variant_t
#include <comdef.h>
#include <comutil.h>

View File

@ -8,7 +8,7 @@
#define mozilla_LaunchUnelevated_h
#include "LauncherProcessWin.h"
#include "LauncherResult.h"
#include "mozilla/LauncherResult.h"
#include "mozilla/Maybe.h"
#include "nsWindowsHelpers.h"

View File

@ -13,6 +13,8 @@
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/DynamicallyLinkedFunctionPtr.h"
#include "mozilla/LauncherRegistryInfo.h"
#include "mozilla/LauncherResult.h"
#include "mozilla/Maybe.h"
#include "mozilla/SafeMode.h"
#include "mozilla/Sprintf.h" // For printf_stderr
@ -26,7 +28,6 @@
#include "DllBlocklistWin.h"
#include "ErrorHandler.h"
#include "LauncherResult.h"
#include "LaunchUnelevated.h"
#include "ProcThreadAttributes.h"
@ -38,7 +39,7 @@
* At this point the child process has been created in a suspended state. Any
* additional startup work (eg, blocklist setup) should go here.
*
* @return true if browser startup should proceed, otherwise false.
* @return Ok if browser startup should proceed
*/
static mozilla::LauncherVoidResult PostCreationSetup(HANDLE aChildProcess,
HANDLE aChildMainThread,
@ -144,16 +145,14 @@ static void MaybeBreakForBrowserDebugging() {
::Sleep(pauseLenMs);
}
namespace mozilla {
bool RunAsLauncherProcess(int& argc, wchar_t** argv) {
static bool DoLauncherProcessChecks(int& argc, wchar_t** argv) {
// NB: We run all tests in this function instead of returning early in order
// to ensure that all side effects take place, such as clearing environment
// variables.
bool result = false;
#if defined(MOZ_LAUNCHER_PROCESS)
LauncherResult<bool> isSame = IsSameBinaryAsParentProcess();
mozilla::LauncherResult<bool> isSame = mozilla::IsSameBinaryAsParentProcess();
if (isSame.isOk()) {
result = !isSame.unwrap();
} else {
@ -166,17 +165,47 @@ bool RunAsLauncherProcess(int& argc, wchar_t** argv) {
result = true;
}
result |=
CheckArg(argc, argv, L"launcher", static_cast<const wchar_t**>(nullptr),
CheckArgFlag::RemoveArg) == ARG_FOUND;
result |= mozilla::CheckArg(
argc, argv, L"launcher", static_cast<const wchar_t**>(nullptr),
mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND;
if (!result) {
return result;
}
namespace mozilla {
bool RunAsLauncherProcess(int& argc, wchar_t** argv) {
LauncherRegistryInfo::ProcessType desiredType =
DoLauncherProcessChecks(argc, argv)
? LauncherRegistryInfo::ProcessType::Launcher
: LauncherRegistryInfo::ProcessType::Browser;
// If we're looking at browser, return fast when we're a child process.
if (desiredType == LauncherRegistryInfo::ProcessType::Browser &&
mozilla::CheckArg(argc, argv, L"contentproc",
static_cast<const wchar_t**>(nullptr),
mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND) {
return false;
}
LauncherRegistryInfo regInfo;
LauncherResult<LauncherRegistryInfo::ProcessType> runAsType =
regInfo.Check(desiredType);
if (runAsType.isErr()) {
HandleLauncherError(runAsType);
// If there is an error, we should always fall back to returning false
// for safety's sake.
return false;
}
if (runAsType.unwrap() == LauncherRegistryInfo::ProcessType::Browser) {
// In this case, we will be proceeding to run as the browser.
// We should check MOZ_DEBUG_BROWSER_* env vars.
MaybeBreakForBrowserDebugging();
}
return result;
return runAsType.unwrap() == LauncherRegistryInfo::ProcessType::Launcher;
}
int LauncherMain(int argc, wchar_t* argv[]) {

View File

@ -7,8 +7,8 @@
#ifndef mozilla_SameBinary_h
#define mozilla_SameBinary_h
#include "LauncherResult.h"
#include "NativeNt.h"
#include "mozilla/LauncherResult.h"
#include "mozilla/NativeNt.h"
#include "nsWindowsHelpers.h"
namespace mozilla {

View File

@ -9,6 +9,7 @@ Library('winlauncher')
FORCE_STATIC_LIB = True
UNIFIED_SOURCES += [
'/toolkit/xre/LauncherRegistryInfo.cpp',
'DllBlocklistWin.cpp',
'ErrorHandler.cpp',
'LauncherProcessWin.cpp',
@ -25,6 +26,9 @@ TEST_DIRS += [
'test',
]
for var in ('MOZ_APP_BASENAME', 'MOZ_APP_VENDOR'):
DEFINES[var] = '"%s"' % CONFIG[var]
DisableStlWrapping()
if CONFIG['CC_TYPE'] == 'clang-cl':

View File

@ -4,13 +4,13 @@
* 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 "NativeNt.h"
#include "SameBinary.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/Move.h"
#include "mozilla/NativeNt.h"
#include "mozilla/Unused.h"
#include "mozilla/Vector.h"
#include "mozilla/WinHeaderOnlyUtils.h"

View File

@ -8,7 +8,6 @@ DisableStlWrapping()
GeckoCppUnitTests(
[
'TestNativeNt',
'TestSameBinary',
],
linkage=None

View File

@ -7,11 +7,6 @@
#ifndef mozilla_NativeNt_h
#define mozilla_NativeNt_h
#if defined(MOZILLA_INTERNAL_API)
#error \
"This header is for initial process initialization. You don't want to be including this here."
#endif // defined(MOZILLA_INTERNAL_API)
#include <stdint.h>
#include <windows.h>
#include <winnt.h>
@ -19,8 +14,12 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/LauncherResult.h"
#include "LauncherResult.h"
// The declarations within this #if block are intended to be used for initial
// process initialization ONLY. You probably don't want to be using these in
// normal Gecko code!
#if !defined(MOZILLA_INTERNAL_API)
extern "C" {
@ -80,9 +79,13 @@ VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
} // extern "C"
#endif // !defined(MOZILLA_INTERNAL_API)
namespace mozilla {
namespace nt {
#if !defined(MOZILLA_INTERNAL_API)
struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME {
MemorySectionNameBuf() {
mSectionFileName.Length = 0;
@ -202,6 +205,8 @@ inline void GetLeafName(PUNICODE_STRING aDestString,
aDestString->MaximumLength = aDestString->Length;
}
#endif // !defined(MOZILLA_INTERNAL_API)
inline char EnsureLowerCaseASCII(char aChar) {
if (aChar >= 'A' && aChar <= 'Z') {
aChar -= 'A' - 'a';

View File

@ -38,6 +38,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
]
EXPORTS.mozilla += [
'DynamicallyLinkedFunctionPtr.h',
'NativeNt.h',
'WindowsMapRemoteView.h',
]
EXPORTS.mozilla.interceptor += [

View File

@ -4,7 +4,7 @@
* 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 "NativeNt.h"
#include "mozilla/NativeNt.h"
#include "mozilla/UniquePtr.h"
#include <stdio.h>

View File

@ -15,7 +15,14 @@ CppUnitTests([
])
if CONFIG['OS_ARCH'] == 'WINNT':
GeckoCppUnitTests([
'TestNativeNt',
], linkage=None)
TEST_DIRS += [
'interceptor',
'gtest',
]
OS_LIBS += [
'mincore',
'ntdll',
]

View File

@ -359,11 +359,7 @@ inline UniquePtr<wchar_t[]> MakeCommandLine(int argc, wchar_t** argv,
return s;
}
inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
if (!aArgv) {
return false;
}
inline UniquePtr<wchar_t[]> GetFullBinaryPath() {
DWORD bufLen = MAX_PATH;
mozilla::UniquePtr<wchar_t[]> buf;
DWORD retLen;
@ -372,7 +368,7 @@ inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
buf = mozilla::MakeUnique<wchar_t[]>(bufLen);
retLen = ::GetModuleFileNameW(nullptr, buf.get(), bufLen);
if (!retLen) {
return false;
return nullptr;
}
if (retLen == bufLen && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
@ -388,8 +384,21 @@ inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
// Since we're likely to have a bunch of unused space in buf, let's reallocate
// a string to the actual size of the file name.
auto newArgv_0 = mozilla::MakeUnique<wchar_t[]>(retLen);
if (wcscpy_s(newArgv_0.get(), retLen, buf.get())) {
auto result = mozilla::MakeUnique<wchar_t[]>(retLen);
if (wcscpy_s(result.get(), retLen, buf.get())) {
return nullptr;
}
return result;
}
inline bool SetArgv0ToFullBinaryPath(wchar_t* aArgv[]) {
if (!aArgv) {
return false;
}
UniquePtr<wchar_t[]> newArgv_0(GetFullBinaryPath());
if (!newArgv_0) {
return false;
}

View File

@ -0,0 +1,409 @@
/* -*- 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 "LauncherRegistryInfo.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/NativeNt.h"
#include <string>
#define EXPAND_STRING_MACRO2(t) t
#define EXPAND_STRING_MACRO(t) EXPAND_STRING_MACRO2(t)
namespace mozilla {
const wchar_t LauncherRegistryInfo::kLauncherSubKeyPath[] =
L"SOFTWARE\\" EXPAND_STRING_MACRO(MOZ_APP_VENDOR) L"\\" EXPAND_STRING_MACRO(
MOZ_APP_BASENAME) L"\\Launcher";
const wchar_t LauncherRegistryInfo::kLauncherSuffix[] = L"|Launcher";
const wchar_t LauncherRegistryInfo::kBrowserSuffix[] = L"|Browser";
const wchar_t LauncherRegistryInfo::kImageTimestampSuffix[] = L"|Image";
LauncherResult<LauncherRegistryInfo::Disposition> LauncherRegistryInfo::Open() {
if (!!mRegKey) {
return Disposition::OpenedExisting;
}
DWORD disposition;
HKEY rawKey;
LSTATUS result = ::RegCreateKeyExW(
HKEY_CURRENT_USER, kLauncherSubKeyPath, 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &rawKey, &disposition);
if (result != ERROR_SUCCESS) {
return LAUNCHER_ERROR_FROM_WIN32(result);
}
mRegKey.own(rawKey);
switch (disposition) {
case REG_CREATED_NEW_KEY:
return Disposition::CreatedNew;
case REG_OPENED_EXISTING_KEY:
return Disposition::OpenedExisting;
default:
break;
}
MOZ_ASSERT_UNREACHABLE("Invalid disposition from RegCreateKeyExW");
return LAUNCHER_ERROR_GENERIC();
}
LauncherVoidResult LauncherRegistryInfo::ReflectPrefToRegistry(
const bool aEnable) {
LauncherResult<Disposition> disposition = Open();
if (disposition.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(disposition);
}
// Always delete the launcher timestamp
LauncherResult<bool> clearedLauncherTimestamp =
ClearStartTimestamp(ProcessType::Launcher);
MOZ_ASSERT(clearedLauncherTimestamp.isOk());
if (clearedLauncherTimestamp.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(clearedLauncherTimestamp);
}
if (!aEnable) {
// Set the browser timestamp to 0 to indicate force-disabled
return WriteStartTimestamp(ProcessType::Browser, Some(0ULL));
}
// Otherwise we delete the browser timestamp to start over fresh
LauncherResult<bool> clearedBrowserTimestamp =
ClearStartTimestamp(ProcessType::Browser);
MOZ_ASSERT(clearedBrowserTimestamp.isOk());
if (clearedBrowserTimestamp.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(clearedBrowserTimestamp);
}
return Ok();
}
LauncherResult<LauncherRegistryInfo::ProcessType> LauncherRegistryInfo::Check(
const ProcessType aDesiredType) {
LauncherResult<Disposition> disposition = Open();
if (disposition.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(disposition);
}
LauncherResult<DWORD> ourImageTimestamp = GetCurrentImageTimestamp();
if (ourImageTimestamp.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(ourImageTimestamp);
}
LauncherResult<DWORD> savedImageTimestamp = GetSavedImageTimestamp();
if (savedImageTimestamp.isErr() &&
savedImageTimestamp.unwrapErr() !=
WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) {
return LAUNCHER_ERROR_FROM_RESULT(savedImageTimestamp);
}
// If we don't have a saved timestamp, or we do but it doesn't match with
// our current timestamp, clear previous values.
if (savedImageTimestamp.isErr() ||
savedImageTimestamp.unwrap() != ourImageTimestamp.unwrap()) {
LauncherVoidResult clearResult = ClearStartTimestamps();
if (clearResult.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(clearResult);
}
LauncherVoidResult writeResult =
WriteImageTimestamp(ourImageTimestamp.unwrap());
if (writeResult.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(writeResult);
}
}
ProcessType typeToRunAs = aDesiredType;
if (disposition.unwrap() == Disposition::CreatedNew ||
aDesiredType == ProcessType::Browser) {
// No existing values to check, or we're going to be running as the browser
// process: just write our timestamp and return
LauncherVoidResult wroteTimestamp = WriteStartTimestamp(typeToRunAs);
if (wroteTimestamp.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(wroteTimestamp);
}
return typeToRunAs;
}
LauncherResult<uint64_t> lastLauncherTimestamp =
GetStartTimestamp(ProcessType::Launcher);
bool haveLauncherTs = lastLauncherTimestamp.isOk();
LauncherResult<uint64_t> lastBrowserTimestamp =
GetStartTimestamp(ProcessType::Browser);
bool haveBrowserTs = lastBrowserTimestamp.isOk();
if (haveLauncherTs != haveBrowserTs) {
// If we have a launcher timestamp but no browser timestamp (or vice versa),
// that's bad because it is indicating that the browser can't run with
// the launcher process.
typeToRunAs = ProcessType::Browser;
} else if (haveLauncherTs) {
// if we have both timestamps, we want to ensure that the launcher timestamp
// is earlier than the browser timestamp.
if (aDesiredType == ProcessType::Launcher) {
bool areTimestampsOk =
lastLauncherTimestamp.unwrap() < lastBrowserTimestamp.unwrap();
if (!areTimestampsOk) {
typeToRunAs = ProcessType::Browser;
}
}
} else {
// If we have neither timestamp, then we should try running as suggested
// by |aDesiredType|.
// We shouldn't really have this scenario unless we're going to be running
// as the launcher process.
MOZ_ASSERT(typeToRunAs == ProcessType::Launcher);
// No change to typeToRunAs
}
LauncherVoidResult wroteTimestamp = Ok();
if (typeToRunAs == ProcessType::Browser && aDesiredType != typeToRunAs) {
// We were hoping to run as the launcher, but some failure has caused us
// to run as the browser. Set the browser timestamp to zero as an indicator.
wroteTimestamp = WriteStartTimestamp(typeToRunAs, Some(0ULL));
} else {
wroteTimestamp = WriteStartTimestamp(typeToRunAs);
}
if (wroteTimestamp.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(wroteTimestamp);
}
return typeToRunAs;
}
LauncherVoidResult LauncherRegistryInfo::DisableDueToFailure() {
LauncherResult<Disposition> disposition = Open();
if (disposition.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(disposition);
}
return WriteStartTimestamp(ProcessType::Browser, Some(0ULL));
}
LauncherResult<LauncherRegistryInfo::EnabledState>
LauncherRegistryInfo::IsEnabled() {
LauncherResult<Disposition> disposition = Open();
if (disposition.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(disposition);
}
LauncherResult<uint64_t> launcherTimestamp =
GetStartTimestamp(ProcessType::Launcher);
LauncherResult<uint64_t> browserTimestamp =
GetStartTimestamp(ProcessType::Browser);
// In this function, we'll explictly search for the ForceDisabled and
// Enabled conditions. Everything else is FailDisabled.
bool isBrowserTimestampZero =
browserTimestamp.isOk() && browserTimestamp.unwrap() == 0ULL;
if (isBrowserTimestampZero && launcherTimestamp.isErr()) {
return EnabledState::ForceDisabled;
}
if (launcherTimestamp.isOk() && browserTimestamp.isOk() &&
launcherTimestamp.unwrap() < browserTimestamp.unwrap()) {
return EnabledState::Enabled;
}
if (launcherTimestamp.isErr() && browserTimestamp.isErr()) {
return EnabledState::Enabled;
}
return EnabledState::FailDisabled;
}
LauncherResult<std::wstring> LauncherRegistryInfo::ResolveValueName(
LauncherRegistryInfo::ProcessType aProcessType) {
if (aProcessType == ProcessType::Launcher) {
if (mLauncherValueName.empty()) {
mLauncherValueName.assign(mBinPath);
mLauncherValueName.append(kLauncherSuffix,
ArrayLength(kLauncherSuffix) - 1);
}
return mLauncherValueName;
} else if (aProcessType == ProcessType::Browser) {
if (mBrowserValueName.empty()) {
mBrowserValueName.assign(mBinPath);
mBrowserValueName.append(kBrowserSuffix, ArrayLength(kBrowserSuffix) - 1);
}
return mBrowserValueName;
}
return LAUNCHER_ERROR_GENERIC();
}
std::wstring LauncherRegistryInfo::ResolveImageTimestampValueName() {
if (mImageValueName.empty()) {
mImageValueName.assign(mBinPath);
mImageValueName.append(kImageTimestampSuffix,
ArrayLength(kImageTimestampSuffix) - 1);
}
return mImageValueName;
}
LauncherVoidResult LauncherRegistryInfo::WriteStartTimestamp(
LauncherRegistryInfo::ProcessType aProcessType, Maybe<uint64_t> aValue) {
LauncherResult<std::wstring> name = ResolveValueName(aProcessType);
if (name.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(name);
}
ULARGE_INTEGER timestamp;
if (aValue.isSome()) {
timestamp.QuadPart = aValue.value();
} else {
// We need to use QPC here because millisecond granularity is too coarse
// to properly measure the times involved.
if (!::QueryPerformanceCounter(
reinterpret_cast<LARGE_INTEGER*>(&timestamp))) {
return LAUNCHER_ERROR_FROM_LAST();
}
}
DWORD len = sizeof(timestamp);
LSTATUS result =
::RegSetValueExW(mRegKey.get(), name.unwrap().c_str(), 0, REG_QWORD,
reinterpret_cast<PBYTE>(&timestamp), len);
if (result != ERROR_SUCCESS) {
return LAUNCHER_ERROR_FROM_WIN32(result);
}
return Ok();
}
LauncherResult<DWORD> LauncherRegistryInfo::GetCurrentImageTimestamp() {
nt::PEHeaders headers(::GetModuleHandleW(nullptr));
if (!headers) {
return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
}
DWORD timestamp;
if (!headers.GetTimeStamp(timestamp)) {
return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA);
}
return timestamp;
}
LauncherVoidResult LauncherRegistryInfo::WriteImageTimestamp(DWORD aTimestamp) {
std::wstring imageTimestampValueName = ResolveImageTimestampValueName();
DWORD len = sizeof(aTimestamp);
LSTATUS result =
::RegSetValueExW(mRegKey.get(), imageTimestampValueName.c_str(), 0,
REG_DWORD, reinterpret_cast<PBYTE>(&aTimestamp), len);
if (result != ERROR_SUCCESS) {
return LAUNCHER_ERROR_FROM_WIN32(result);
}
return Ok();
}
LauncherResult<bool> LauncherRegistryInfo::ClearStartTimestamp(
LauncherRegistryInfo::ProcessType aProcessType) {
LauncherResult<std::wstring> timestampName = ResolveValueName(aProcessType);
if (timestampName.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(timestampName);
}
LSTATUS result =
::RegDeleteValueW(mRegKey.get(), timestampName.unwrap().c_str());
if (result == ERROR_SUCCESS) {
return true;
}
if (result == ERROR_FILE_NOT_FOUND) {
return false;
}
return LAUNCHER_ERROR_FROM_WIN32(result);
}
LauncherVoidResult LauncherRegistryInfo::ClearStartTimestamps() {
LauncherResult<uint64_t> lastBrowserTimestamp =
GetStartTimestamp(ProcessType::Browser);
if (lastBrowserTimestamp.isOk() && lastBrowserTimestamp.unwrap() == 0ULL) {
// Only proceed when the browser timestamp is non-zero
return Ok();
}
LauncherResult<bool> clearedLauncherTimestamp =
ClearStartTimestamp(ProcessType::Launcher);
if (clearedLauncherTimestamp.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(clearedLauncherTimestamp);
}
LauncherResult<bool> clearedBrowserTimestamp =
ClearStartTimestamp(ProcessType::Browser);
if (clearedBrowserTimestamp.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(clearedBrowserTimestamp);
}
return Ok();
}
LauncherResult<DWORD> LauncherRegistryInfo::GetSavedImageTimestamp() {
std::wstring imageTimestampValueName = ResolveImageTimestampValueName();
DWORD value;
DWORD valueLen = sizeof(value);
DWORD type;
LSTATUS result = ::RegQueryValueExW(
mRegKey.get(), imageTimestampValueName.c_str(), nullptr, &type,
reinterpret_cast<PBYTE>(&value), &valueLen);
// NB: If the value does not exist, result == ERROR_FILE_NOT_FOUND
if (result != ERROR_SUCCESS) {
return LAUNCHER_ERROR_FROM_WIN32(result);
}
if (type != REG_DWORD) {
return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
}
return value;
}
LauncherResult<uint64_t> LauncherRegistryInfo::GetStartTimestamp(
LauncherRegistryInfo::ProcessType aProcessType) {
LauncherResult<std::wstring> name = ResolveValueName(aProcessType);
if (name.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(name);
}
uint64_t value;
DWORD valueLen = sizeof(value);
DWORD type;
LSTATUS result =
::RegQueryValueExW(mRegKey.get(), name.unwrap().c_str(), nullptr, &type,
reinterpret_cast<PBYTE>(&value), &valueLen);
// NB: If the value does not exist, result == ERROR_FILE_NOT_FOUND
if (result != ERROR_SUCCESS) {
return LAUNCHER_ERROR_FROM_WIN32(result);
}
if (type != REG_QWORD) {
return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
}
return value;
}
} // namespace mozilla

View File

@ -0,0 +1,74 @@
/* -*- 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_LauncherRegistryInfo_h
#define mozilla_LauncherRegistryInfo_h
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/LauncherResult.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "nsWindowsHelpers.h"
#include <string>
/**
* We use std::wstring here because this code must be usable within both the
* launcher process and Gecko itself.
*/
namespace mozilla {
class LauncherRegistryInfo final {
public:
enum class ProcessType { Launcher, Browser };
enum class EnabledState {
Enabled,
FailDisabled,
ForceDisabled,
};
LauncherRegistryInfo() : mBinPath(GetFullBinaryPath().get()) {}
LauncherVoidResult ReflectPrefToRegistry(const bool aEnable);
LauncherResult<EnabledState> IsEnabled();
LauncherResult<ProcessType> Check(const ProcessType aDesiredType);
LauncherVoidResult DisableDueToFailure();
private:
enum class Disposition { CreatedNew, OpenedExisting };
private:
LauncherResult<Disposition> Open();
LauncherVoidResult WriteStartTimestamp(ProcessType aProcessType,
Maybe<uint64_t> aValue = Nothing());
LauncherResult<DWORD> GetCurrentImageTimestamp();
LauncherVoidResult WriteImageTimestamp(DWORD aTimestamp);
LauncherResult<bool> ClearStartTimestamp(ProcessType aProcessType);
LauncherVoidResult ClearStartTimestamps();
LauncherResult<DWORD> GetSavedImageTimestamp();
LauncherResult<uint64_t> GetStartTimestamp(ProcessType aProcessType);
LauncherResult<std::wstring> ResolveValueName(ProcessType aProcessType);
std::wstring ResolveImageTimestampValueName();
private:
nsAutoRegKey mRegKey;
std::wstring mBinPath;
std::wstring mImageValueName;
std::wstring mBrowserValueName;
std::wstring mLauncherValueName;
static const wchar_t kLauncherSubKeyPath[];
static const wchar_t kLauncherSuffix[];
static const wchar_t kBrowserSuffix[];
static const wchar_t kImageTimestampSuffix[];
};
} // namespace mozilla
#endif // mozilla_LauncherRegistryInfo_h

View File

@ -12,6 +12,13 @@
namespace mozilla {
#if defined(MOZILLA_INTERNAL_API)
template <typename T>
using LauncherResult = WindowsErrorResult<T>;
#else
struct LauncherError {
LauncherError(const char* aFile, int aLine, WindowsError aWin32Error)
: mFile(aFile), mLine(aLine), mError(aWin32Error) {}
@ -19,15 +26,48 @@ struct LauncherError {
const char* mFile;
int mLine;
WindowsError mError;
bool operator==(const LauncherError& aOther) const {
return mError == aOther.mError;
}
bool operator!=(const LauncherError& aOther) const {
return mError != aOther.mError;
}
bool operator==(const WindowsError& aOther) const { return mError == aOther; }
bool operator!=(const WindowsError& aOther) const { return mError != aOther; }
};
template <typename T>
using LauncherResult = Result<T, LauncherError>;
using LauncherVoidResult = Result<Ok, LauncherError>;
#endif // defined(MOZILLA_INTERNAL_API)
using LauncherVoidResult = LauncherResult<Ok>;
} // namespace mozilla
#if defined(MOZILLA_INTERNAL_API)
#define LAUNCHER_ERROR_GENERIC() \
::mozilla::Err(::mozilla::WindowsError::CreateGeneric())
#define LAUNCHER_ERROR_FROM_WIN32(err) \
::mozilla::Err(::mozilla::WindowsError::FromWin32Error(err))
#define LAUNCHER_ERROR_FROM_LAST() \
::mozilla::Err(::mozilla::WindowsError::FromLastError())
#define LAUNCHER_ERROR_FROM_NTSTATUS(ntstatus) \
::mozilla::Err(::mozilla::WindowsError::FromNtStatus(ntstatus))
#define LAUNCHER_ERROR_FROM_HRESULT(hresult) \
::mozilla::Err(::mozilla::WindowsError::FromHResult(hresult))
#else
#define LAUNCHER_ERROR_GENERIC() \
::mozilla::Err(::mozilla::LauncherError( \
__FILE__, __LINE__, ::mozilla::WindowsError::CreateGeneric()))
@ -38,8 +78,7 @@ using LauncherVoidResult = Result<Ok, LauncherError>;
#define LAUNCHER_ERROR_FROM_LAST() \
::mozilla::Err(::mozilla::LauncherError( \
__FILE__, __LINE__, \
::mozilla::WindowsError::FromWin32Error(::GetLastError())))
__FILE__, __LINE__, ::mozilla::WindowsError::FromLastError()))
#define LAUNCHER_ERROR_FROM_NTSTATUS(ntstatus) \
::mozilla::Err(::mozilla::LauncherError( \
@ -49,12 +88,14 @@ using LauncherVoidResult = Result<Ok, LauncherError>;
::mozilla::Err(::mozilla::LauncherError( \
__FILE__, __LINE__, ::mozilla::WindowsError::FromHResult(hresult)))
// This macro enables moving of a mozilla::LauncherError from a
// mozilla::LauncherResult<Foo> into a mozilla::LauncherResult<Bar>
#define LAUNCHER_ERROR_FROM_RESULT(result) ::mozilla::Err(result.unwrapErr())
// This macro wraps the supplied WindowsError with a LauncherError
#define LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(err) \
::mozilla::Err(::mozilla::LauncherError(__FILE__, __LINE__, err))
#endif // defined(MOZILLA_INTERNAL_API)
// This macro enables moving of a mozilla::LauncherError from a
// mozilla::LauncherResult<Foo> into a mozilla::LauncherResult<Bar>
#define LAUNCHER_ERROR_FROM_RESULT(result) ::mozilla::Err(result.unwrapErr())
#endif // mozilla_LauncherResult_h

View File

@ -59,6 +59,14 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
LOCAL_INCLUDES += [
'../components/printingui',
]
if CONFIG['MOZ_LAUNCHER_PROCESS']:
EXPORTS.mozilla += [
'LauncherRegistryInfo.h',
'LauncherResult.h',
]
UNIFIED_SOURCES += [
'LauncherRegistryInfo.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
UNIFIED_SOURCES += [
'MacApplicationDelegate.mm',
@ -156,7 +164,8 @@ if CONFIG['MOZ_X11']:
DEFINES['USE_GLX_TEST'] = True
for var in ('MOZ_APP_NAME', 'MOZ_APP_BASENAME', 'MOZ_APP_DISPLAYNAME',
'MOZ_APP_VERSION', 'OS_TARGET', 'MOZ_WIDGET_TOOLKIT'):
'MOZ_APP_VENDOR', 'MOZ_APP_VERSION', 'OS_TARGET',
'MOZ_WIDGET_TOOLKIT'):
DEFINES[var] = '"%s"' % CONFIG[var]
if CONFIG['MOZ_UPDATER'] and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':

View File

@ -0,0 +1,691 @@
/* -*- 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 "mozilla/LauncherRegistryInfo.h"
#include "mozilla/NativeNt.h"
#include "mozilla/ScopeExit.h"
#include "CmdLineAndEnvUtils.h"
#include "nsWindowsHelpers.h"
#include "LauncherRegistryInfo.cpp"
#include <string>
const char kFailFmt[] = "TEST-FAILED | LauncherRegistryInfo | %s | %S\n";
static const wchar_t kRegKeyPath[] = L"SOFTWARE\\" EXPAND_STRING_MACRO(
MOZ_APP_VENDOR) L"\\" EXPAND_STRING_MACRO(MOZ_APP_BASENAME) L"\\Launcher";
static const wchar_t kBrowserSuffix[] = L"|Browser";
static const wchar_t kLauncherSuffix[] = L"|Launcher";
static const wchar_t kImageSuffix[] = L"|Image";
static std::wstring gBrowserValue;
static std::wstring gLauncherValue;
static std::wstring gImageValue;
static DWORD gMyImageTimestamp;
using QWordResult = mozilla::Result<DWORD64, mozilla::WindowsError>;
using DWordResult = mozilla::Result<DWORD, mozilla::WindowsError>;
using VoidResult = mozilla::Result<mozilla::Ok, mozilla::WindowsError>;
#define RUN_TEST(fn) \
if ((vr = fn()).isErr()) { \
printf(kFailFmt, #fn, vr.unwrapErr().AsString().get()); \
return 1; \
}
static QWordResult GetBrowserTimestamp() {
DWORD64 qword;
DWORD size = sizeof(qword);
LSTATUS status =
::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, gBrowserValue.c_str(),
RRF_RT_QWORD, nullptr, &qword, &size);
if (status == ERROR_SUCCESS) {
return qword;
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static VoidResult SetBrowserTimestamp(DWORD64 aTimestamp) {
LSTATUS status =
::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, gBrowserValue.c_str(),
REG_QWORD, &aTimestamp, sizeof(aTimestamp));
if (status == ERROR_SUCCESS) {
return mozilla::Ok();
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static VoidResult DeleteBrowserTimestamp() {
LSTATUS status = ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath,
gBrowserValue.c_str());
if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) {
return mozilla::Ok();
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static QWordResult GetLauncherTimestamp() {
DWORD64 qword;
DWORD size = sizeof(qword);
LSTATUS status =
::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, gLauncherValue.c_str(),
RRF_RT_QWORD, nullptr, &qword, &size);
if (status == ERROR_SUCCESS) {
return qword;
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static VoidResult SetLauncherTimestamp(DWORD64 aTimestamp) {
LSTATUS status =
::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, gLauncherValue.c_str(),
REG_QWORD, &aTimestamp, sizeof(aTimestamp));
if (status == ERROR_SUCCESS) {
return mozilla::Ok();
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static VoidResult DeleteLauncherTimestamp() {
LSTATUS status = ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath,
gLauncherValue.c_str());
if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) {
return mozilla::Ok();
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static DWordResult GetImageTimestamp() {
DWORD dword;
DWORD size = sizeof(dword);
LSTATUS status =
::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, gImageValue.c_str(),
RRF_RT_DWORD, nullptr, &dword, &size);
if (status == ERROR_SUCCESS) {
return dword;
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static VoidResult SetImageTimestamp(DWORD aTimestamp) {
LSTATUS status =
::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, gImageValue.c_str(),
REG_DWORD, &aTimestamp, sizeof(aTimestamp));
if (status == ERROR_SUCCESS) {
return mozilla::Ok();
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static VoidResult DeleteImageTimestamp() {
LSTATUS status =
::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, gImageValue.c_str());
if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) {
return mozilla::Ok();
}
return mozilla::Err(mozilla::WindowsError::FromWin32Error(status));
}
static VoidResult DeleteAllTimestamps() {
VoidResult vr = DeleteBrowserTimestamp();
if (vr.isErr()) {
return vr;
}
vr = DeleteLauncherTimestamp();
if (vr.isErr()) {
return vr;
}
return DeleteImageTimestamp();
}
static VoidResult SetupEnabledScenario() {
// Reset the registry state to an enabled state. First, we delete all existing
// timestamps (if any).
VoidResult vr = DeleteAllTimestamps();
if (vr.isErr()) {
return vr;
}
// Now we run Check(Launcher)...
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Launcher) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
// ...and Check(Browser)
result = info.Check(mozilla::LauncherRegistryInfo::ProcessType::Browser);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
// By this point we are considered to be fully enabled.
return mozilla::Ok();
}
static VoidResult TestEmptyRegistry() {
VoidResult vr = DeleteAllTimestamps();
if (vr.isErr()) {
return vr;
}
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Launcher) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
// LauncherRegistryInfo should have created Launcher and Image values
QWordResult launcherTs = GetLauncherTimestamp();
if (launcherTs.isErr()) {
return mozilla::Err(launcherTs.unwrapErr());
}
DWordResult imageTs = GetImageTimestamp();
if (imageTs.isErr()) {
return mozilla::Err(imageTs.unwrapErr());
}
if (imageTs.unwrap() != gMyImageTimestamp) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
QWordResult browserTs = GetBrowserTimestamp();
if (browserTs.isOk()) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
if (browserTs.unwrapErr() !=
mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) {
return mozilla::Err(browserTs.unwrapErr());
}
return mozilla::Ok();
}
// This test depends on the side effects from having just run TestEmptyRegistry
static VoidResult TestNormal() {
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Browser);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
QWordResult launcherTs = GetLauncherTimestamp();
if (launcherTs.isErr()) {
return mozilla::Err(launcherTs.unwrapErr());
}
QWordResult browserTs = GetBrowserTimestamp();
if (browserTs.isErr()) {
return mozilla::Err(browserTs.unwrapErr());
}
if (browserTs.unwrap() < launcherTs.unwrap()) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> enabled =
info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::Enabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
return mozilla::Ok();
}
// This test depends on the side effects from having just run TestNormal
static VoidResult TestBrowserNoLauncher() {
VoidResult vr = DeleteLauncherTimestamp();
if (vr.isErr()) {
return vr;
}
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
// Verify that we still don't have a launcher timestamp
QWordResult launcherTs = GetLauncherTimestamp();
if (launcherTs.isOk()) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
if (launcherTs.unwrapErr() !=
mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) {
return mozilla::Err(launcherTs.unwrapErr());
}
// Verify that the browser timestamp is now zero
QWordResult browserTs = GetBrowserTimestamp();
if (browserTs.isErr()) {
return mozilla::Err(browserTs.unwrapErr());
}
if (browserTs.unwrap() != 0ULL) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> enabled =
info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
return mozilla::Ok();
}
static VoidResult TestLauncherNoBrowser() {
VoidResult vr = DeleteAllTimestamps();
if (vr.isErr()) {
return vr;
}
vr = SetLauncherTimestamp(0x77777777);
if (vr.isErr()) {
return vr;
}
vr = SetImageTimestamp(gMyImageTimestamp);
if (vr.isErr()) {
return vr;
}
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> enabled =
info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::FailDisabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
return mozilla::Ok();
}
static VoidResult TestBrowserLessThanLauncher() {
VoidResult vr = SetLauncherTimestamp(0x77777777);
if (vr.isErr()) {
return vr;
}
vr = SetBrowserTimestamp(0ULL);
if (vr.isErr()) {
return vr;
}
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> enabled =
info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::FailDisabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
return mozilla::Ok();
}
static VoidResult TestImageTimestampChange() {
// This should reset the timestamps and then essentially run like
// TestEmptyRegistry
VoidResult vr = SetImageTimestamp(0x12345678);
if (vr.isErr()) {
return vr;
}
vr = SetLauncherTimestamp(1ULL);
if (vr.isErr()) {
return vr;
}
vr = SetBrowserTimestamp(2ULL);
if (vr.isErr()) {
return vr;
}
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Launcher) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
QWordResult launcherTs = GetLauncherTimestamp();
if (launcherTs.isErr()) {
return mozilla::Err(launcherTs.unwrapErr());
}
DWordResult imageTs = GetImageTimestamp();
if (imageTs.isErr()) {
return mozilla::Err(imageTs.unwrapErr());
}
if (imageTs.unwrap() != gMyImageTimestamp) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
QWordResult browserTs = GetBrowserTimestamp();
if (browserTs.isOk()) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
if (browserTs.unwrapErr() !=
mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) {
return mozilla::Err(browserTs.unwrapErr());
}
return mozilla::Ok();
}
static VoidResult TestImageTimestampChangeWhenDisabled() {
VoidResult vr = SetImageTimestamp(0x12345678);
if (vr.isErr()) {
return vr;
}
vr = DeleteLauncherTimestamp();
if (vr.isErr()) {
return vr;
}
vr = SetBrowserTimestamp(0ULL);
if (vr.isErr()) {
return vr;
}
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> result =
info.Check(mozilla::LauncherRegistryInfo::ProcessType::Launcher);
if (result.isErr()) {
return mozilla::Err(result.unwrapErr().mError);
}
if (result.unwrap() != mozilla::LauncherRegistryInfo::ProcessType::Browser) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> enabled =
info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
return mozilla::Ok();
}
static VoidResult TestDisableDueToFailure() {
VoidResult vr = SetupEnabledScenario();
if (vr.isErr()) {
return vr;
}
// Check that we are indeed enabled.
mozilla::LauncherRegistryInfo info;
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> enabled =
info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::Enabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
// Now call DisableDueToFailure
mozilla::LauncherVoidResult lvr = info.DisableDueToFailure();
if (lvr.isErr()) {
return mozilla::Err(lvr.unwrapErr().mError);
}
// We should now be FailDisabled
enabled = info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::FailDisabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
// If we delete the launcher timestamp, IsEnabled should then return
// ForceDisabled.
vr = DeleteLauncherTimestamp();
if (vr.isErr()) {
return vr;
}
enabled = info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
return mozilla::Ok();
}
static VoidResult TestPrefReflection() {
// Reset the registry to a known good state.
VoidResult vr = SetupEnabledScenario();
if (vr.isErr()) {
return vr;
}
// First, test to see what happens when the pref is set to ON.
mozilla::LauncherRegistryInfo info;
mozilla::LauncherVoidResult reflectOk = info.ReflectPrefToRegistry(true);
if (reflectOk.isErr()) {
return mozilla::Err(reflectOk.unwrapErr().mError);
}
// Launcher and browser timestamps should be non-existent.
QWordResult launcherTs = GetLauncherTimestamp();
if (launcherTs.isOk()) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
if (launcherTs.unwrapErr() !=
mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) {
return mozilla::Err(launcherTs.unwrapErr());
}
QWordResult browserTs = GetBrowserTimestamp();
if (browserTs.isOk()) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
if (browserTs.unwrapErr() !=
mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) {
return mozilla::Err(browserTs.unwrapErr());
}
// IsEnabled should give us Enabled.
mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> enabled =
info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::Enabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
// Okay, so far so good. Let's reset the board and see what happens when we
// flip the pref to OFF.
vr = SetupEnabledScenario();
if (vr.isErr()) {
return vr;
}
reflectOk = info.ReflectPrefToRegistry(false);
if (reflectOk.isErr()) {
return mozilla::Err(reflectOk.unwrapErr().mError);
}
// Launcher timestamp should be non-existent.
launcherTs = GetLauncherTimestamp();
if (launcherTs.isOk()) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_UNEXPECTED));
}
if (launcherTs.unwrapErr() !=
mozilla::WindowsError::FromWin32Error(ERROR_FILE_NOT_FOUND)) {
return mozilla::Err(launcherTs.unwrapErr());
}
// Browser timestamp should be zero
browserTs = GetBrowserTimestamp();
if (browserTs.isErr()) {
return mozilla::Err(browserTs.unwrapErr());
}
if (browserTs.unwrap() != 0ULL) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
// IsEnabled should give us ForceDisabled
enabled = info.IsEnabled();
if (enabled.isErr()) {
return mozilla::Err(enabled.unwrapErr().mError);
}
if (enabled.unwrap() !=
mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) {
return mozilla::Err(mozilla::WindowsError::FromHResult(E_FAIL));
}
return mozilla::Ok();
}
int main(int argc, char* argv[]) {
auto fullPath = mozilla::GetFullBinaryPath();
if (!fullPath) {
return 1;
}
// Global setup for all tests
gBrowserValue = fullPath.get();
gBrowserValue += kBrowserSuffix;
gLauncherValue = fullPath.get();
gLauncherValue += kLauncherSuffix;
gImageValue = fullPath.get();
gImageValue += kImageSuffix;
mozilla::nt::PEHeaders myHeaders(::GetModuleHandleW(nullptr));
if (!myHeaders) {
return 1;
}
if (!myHeaders.GetTimeStamp(gMyImageTimestamp)) {
return 1;
}
auto onExit = mozilla::MakeScopeExit([] { DeleteAllTimestamps(); });
VoidResult vr = mozilla::Ok();
RUN_TEST(TestEmptyRegistry);
RUN_TEST(TestNormal);
RUN_TEST(TestBrowserNoLauncher);
RUN_TEST(TestLauncherNoBrowser);
RUN_TEST(TestBrowserLessThanLauncher);
RUN_TEST(TestImageTimestampChange);
RUN_TEST(TestImageTimestampChangeWhenDisabled);
RUN_TEST(TestDisableDueToFailure);
RUN_TEST(TestPrefReflection);
return 0;
}

View File

@ -4,9 +4,13 @@
# 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/.
SimplePrograms([
GeckoCppUnitTests(
[
'TestLauncherRegistryInfo',
'TestXREMakeCommandLineWin',
])
],
linkage=None
)
DEFINES['NS_NO_XPCOM'] = True
@ -24,5 +28,9 @@ OS_LIBS += [
'ws2_32',
]
# Needed for TestLauncherRegistryInfo
for var in ('MOZ_APP_BASENAME', 'MOZ_APP_VENDOR'):
DEFINES[var] = '"%s"' % CONFIG[var]
if CONFIG['CC_TYPE'] == 'clang-cl':
AllowCompilerWarnings() # workaround for bug 1090497

View File

@ -149,6 +149,14 @@ class WindowsError final {
return Nothing();
}
bool operator==(const WindowsError& aOther) const {
return mHResult == aOther.mHResult;
}
bool operator!=(const WindowsError& aOther) const {
return mHResult != aOther.mHResult;
}
static DWORD NtStatusToWin32Error(NTSTATUS aNtStatus) {
static const DynamicallyLinkedFunctionPtr<decltype(&RtlNtStatusToDosError)>
pRtlNtStatusToDosError(L"ntdll.dll", "RtlNtStatusToDosError");