diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /browser/app | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/app')
47 files changed, 8393 insertions, 0 deletions
diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in new file mode 100644 index 0000000000..54d6b43fe1 --- /dev/null +++ b/browser/app/Makefile.in @@ -0,0 +1,106 @@ +# 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/. + +dist_dest = $(DIST)/$(MOZ_MACBUNDLE_NAME) + +# hardcode en-US for the moment +AB_CD = en-US + +# Build a binary bootstrapping with XRE_main + +ifndef MOZ_WINCONSOLE +ifneq (,$(MOZ_DEBUG)$(MOZ_ASAN)) +MOZ_WINCONSOLE = 1 +else +MOZ_WINCONSOLE = 0 +endif +endif + +include $(topsrcdir)/config/config.mk + +# If we are trying to show an error dialog about the lack of SSE2 support, +# make sure that code itself doesn't use SSE2. +ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR +CXX := $(filter-out -march=% -msse -msse2 -mfpmath=sse,$(CXX)) +CXX += -march=pentiumpro +endif + +ifeq ($(OS_ARCH),WINNT) +# Rebuild firefox.exe if the manifest changes - it's included by splash.rc. +# (this dependency should really be just for firefox.exe, not other targets) +# Note the manifest file exists in the tree, so we use the explicit filename +# here. +EXTRA_DEPS += $(srcdir)/firefox.exe.manifest +endif + +PROGRAMS_DEST = $(DIST)/bin +objdir = $(topobjdir)/browser/app + +include $(topsrcdir)/config/rules.mk + +ifneq (,$(filter-out WINNT,$(OS_ARCH))) + +ifdef COMPILE_ENVIRONMENT +ifndef MOZ_NO_PIE_COMPAT +libs:: + cp -p $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/bin/$(MOZ_APP_NAME)-bin$(BIN_SUFFIX) +endif +endif + +endif + +# channel-prefs.js is handled separate from other prefs due to bug 756325 +# DO NOT change the content of channel-prefs.js without taking the appropriate +# steps. See bug 1431342. +libs:: $(srcdir)/profile/channel-prefs.js + $(NSINSTALL) -D $(DIST)/bin/defaults/pref + $(call py_action,preprocessor,-Fsubstitution $(PREF_PPFLAGS) $(ACDEFINES) $^ -o $(DIST)/bin/defaults/pref/channel-prefs.js) + +ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) + +MAC_APP_NAME = $(MOZ_APP_DISPLAYNAME) + +ifdef MOZ_DEBUG +MAC_APP_NAME := $(MAC_APP_NAME)Debug +endif + +AB_CD = $(MOZ_UI_LOCALE) + +ifeq (zh-TW,$(AB_CD)) +LPROJ_ROOT := $(subst -,_,$(AB_CD)) +else +LPROJ_ROOT := $(firstword $(subst -, ,$(AB_CD))) +endif +LPROJ := Contents/Resources/$(LPROJ_ROOT).lproj + +repackage:: + $(RM) -r $(dist_dest) + +MAC_BUNDLE_VERSION = $(shell $(PYTHON3) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/buildid.h) + +.PHONY: repackage +tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) $(objdir)/macbuild/Contents/MacOS-files.txt + rm -rf $(dist_dest) + $(MKDIR) -p '$(dist_dest)/Contents/MacOS' + $(MKDIR) -p '$(dist_dest)/$(LPROJ)' + rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents '$(dist_dest)' --exclude English.lproj + rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ '$(dist_dest)/$(LPROJ)' + $(call py_action,preprocessor,-Fsubstitution -DAPP_VERSION='$(MOZ_APP_VERSION)' -DMOZ_APP_NAME='$(MOZ_APP_NAME)' -DMAC_APP_NAME='$(MAC_APP_NAME)' -DMOZ_MACBUNDLE_ID='$(MOZ_MACBUNDLE_ID)' -DMAC_BUNDLE_VERSION='$(MAC_BUNDLE_VERSION)' -DMOZ_DEVELOPER_REPO_PATH='$(topsrcdir)' -DMOZ_DEVELOPER_OBJ_PATH='$(topobjdir)' $(srcdir)/macbuild/Contents/Info.plist.in -o '$(dist_dest)/Contents/Info.plist') + $(call py_action,preprocessor,-Fsubstitution --output-encoding utf-16 -DMAC_APP_NAME='$(MAC_APP_NAME)' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in -o '$(dist_dest)/$(LPROJ)/InfoPlist.strings') + rsync -a --exclude-from='$(objdir)/macbuild/Contents/MacOS-files.txt' $(DIST)/bin/ '$(dist_dest)/Contents/Resources' + rsync -a --include-from='$(objdir)/macbuild/Contents/MacOS-files.txt' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS' + # MacOS-files-copy.in is a list of files that should be copies rather + # than symlinks and placed in .app/Contents/MacOS. + rsync -aL --include-from='$(srcdir)/macbuild/Contents/MacOS-files-copy.in' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS' + $(RM) '$(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME)' + rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) '$(dist_dest)/Contents/MacOS' + cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/firefox.icns '$(dist_dest)/Contents/Resources/firefox.icns' + cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/document.icns '$(dist_dest)/Contents/Resources/document.icns' + $(MKDIR) -p '$(dist_dest)/Contents/Library/LaunchServices' +ifdef MOZ_UPDATER + mv -f '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater' '$(dist_dest)/Contents/Library/LaunchServices' + ln -s ../../../../Library/LaunchServices/org.mozilla.updater '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater' +endif + printf APPLMOZB > '$(dist_dest)/Contents/PkgInfo' +endif diff --git a/browser/app/firefox.exe.manifest b/browser/app/firefox.exe.manifest new file mode 100644 index 0000000000..1e1d6f651d --- /dev/null +++ b/browser/app/firefox.exe.manifest @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity + version="1.0.0.0" + processorArchitecture="*" + name="Firefox" + type="win32" +/> +<description>Firefox</description> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="*" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> +</dependency> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="mozglue" + version="1.0.0.0" + language="*" + /> + </dependentAssembly> +</dependency> +<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3"> + <ms_asmv3:security> + <ms_asmv3:requestedPrivileges> + <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" /> + </ms_asmv3:requestedPrivileges> + </ms_asmv3:security> +</ms_asmv3:trustInfo> + <ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3"> + <ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> + <dpiAware>True/PM</dpiAware> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness> + </ms_asmv3:windowsSettings> + </ms_asmv3:application> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> + </application> + </compatibility> +</assembly> diff --git a/browser/app/macbuild/Contents/Info.plist.in b/browser/app/macbuild/Contents/Info.plist.in new file mode 100644 index 0000000000..fb5e6993ea --- /dev/null +++ b/browser/app/macbuild/Contents/Info.plist.in @@ -0,0 +1,263 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>html</string> + <string>htm</string> + <string>shtml</string> + <string>xht</string> + <string>xhtml</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeName</key> + <string>HTML Document</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>HTML</string> + </array> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>json</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeMIMETypes</key> + <array> + <string>application/json</string> + </array> + <key>CFBundleTypeName</key> + <string>JSON File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + </array> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>svg</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeMIMETypes</key> + <array> + <string>image/svg+xml</string> + </array> + <key>CFBundleTypeName</key> + <string>SVG document</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + </array> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>NSDocumentClass</key> + <string>BrowserDocument</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>text</string> + <string>txt</string> + <string>js</string> + <string>log</string> + <string>css</string> + <string>xul</string> + <string>rdf</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeName</key> + <string>Text Document</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>jpeg</string> + <string>jpg</string> + <string>png</string> + <string>gif</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>fileBookmark.icns</string> + <key>CFBundleTypeName</key> + <string>document.icns</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>GIFf</string> + <string>JPEG</string> + <string>PNGf</string> + </array> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>oga</string> + <string>ogg</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeMIMETypes</key> + <array> + <string>audio/ogg</string> + </array> + <key>CFBundleTypeName</key> + <string>HTML5 Audio (Ogg)</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>ogv</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeMIMETypes</key> + <array> + <string>video/ogg</string> + </array> + <key>CFBundleTypeName</key> + <string>HTML5 Video (Ogg)</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>webm</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeMIMETypes</key> + <array> + <string>video/webm</string> + </array> + <key>CFBundleTypeName</key> + <string>HTML5 Video (WebM)</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + </dict> + </array> + <key>CFBundleExecutable</key> + <string>@MOZ_APP_NAME@</string> + <key>CFBundleGetInfoString</key> + <string>@MAC_APP_NAME@ @APP_VERSION@</string> + <key>CFBundleIconFile</key> + <string>firefox.icns</string> + <key>CFBundleIdentifier</key> + <string>@MOZ_MACBUNDLE_ID@</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>@MAC_APP_NAME@</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>@APP_VERSION@</string> + <key>CFBundleSignature</key> + <string>MOZB</string> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleURLIconFile</key> + <string>document.icns</string> + <key>CFBundleURLName</key> + <string>http URL</string> + <key>CFBundleURLSchemes</key> + <array> + <string>http</string> + </array> + </dict> + <dict> + <key>CFBundleURLIconFile</key> + <string>document.icns</string> + <key>CFBundleURLName</key> + <string>https URL</string> + <key>CFBundleURLSchemes</key> + <array> + <string>https</string> + </array> + </dict> + <dict> + <key>CFBundleURLName</key> + <string>ftp URL</string> + <key>CFBundleURLSchemes</key> + <array> + <string>ftp</string> + </array> + </dict> + <dict> + <key>CFBundleURLName</key> + <string>file URL</string> + <key>CFBundleURLSchemes</key> + <array> + <string>file</string> + </array> + </dict> + </array> + <key>CFBundleVersion</key> + <string>@MAC_BUNDLE_VERSION@</string> + <key>NSUserActivityTypes</key> + <array> + <string>NSUserActivityTypeBrowsingWeb</string> + </array> + <key>NSAppleScriptEnabled</key> + <true/> + <key>LSApplicationCategoryType</key> + <string>public.app-category.productivity</string> + <key>LSEnvironment</key> + <dict> + <key>MallocNanoZone</key> + <string>0</string> + </dict> + <key>LSFileQuarantineEnabled</key> + <true/> + <key>LSMinimumSystemVersion</key> + <string>10.12.0</string> + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> + <key>NSRequiresAquaSystemAppearance</key> + <true/> + <key>NSPrincipalClass</key> + <string>GeckoNSApplication</string> + <key>SMPrivilegedExecutables</key> + <dict> + <key>org.mozilla.updater</key> + <string>identifier "org.mozilla.updater" and ((anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9]) or (anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] and certificate leaf[field.1.2.840.113635.100.6.1.13] and certificate leaf[subject.OU] = "43AQ936H96"))</string> + </dict> + <key>MozillaDeveloperRepoPath</key> + <string>@MOZ_DEVELOPER_REPO_PATH@</string> + <key>MozillaDeveloperObjPath</key> + <string>@MOZ_DEVELOPER_OBJ_PATH@</string> + + <key>NSCameraUsageDescription</key> + <string>Only sites you allow within @MAC_APP_NAME@ will be able to use the camera.</string> + + <key>NSMicrophoneUsageDescription</key> + <string>Only sites you allow within @MAC_APP_NAME@ will be able to use the microphone.</string> +</dict> +</plist> diff --git a/browser/app/macbuild/Contents/MacOS-files-copy.in b/browser/app/macbuild/Contents/MacOS-files-copy.in new file mode 100644 index 0000000000..e9d0f0efb9 --- /dev/null +++ b/browser/app/macbuild/Contents/MacOS-files-copy.in @@ -0,0 +1,11 @@ +# Specifies files that should be copied (via deep copy, resolving symlinks) +# from dist/bin to the .app/Contents/MacOS directory. Linking is preferred to +# reduce disk I/O during builds, so just include dylibs which need to be in the +# same directory as returned by dladddr(3). +# +# Some of these dylibs load other dylibs which are assumed to be siblings in +# the same directory obtained from dladdr(3). With macOS 10.15, dladdr returns +# absolute resolved paths which breaks this assumption if symlinks are used +# because the symlink targets are in different directories. Hence the need for +# them to be copied to the same directory. +/*.dylib diff --git a/browser/app/macbuild/Contents/MacOS-files.in b/browser/app/macbuild/Contents/MacOS-files.in new file mode 100644 index 0000000000..3c6a1db5d6 --- /dev/null +++ b/browser/app/macbuild/Contents/MacOS-files.in @@ -0,0 +1,23 @@ +#if 0 +; Specifies files that should be copied (preserving symlinks) from dist/bin +; to the .app/Contents/MacOS directory. +#endif +#filter substitution +/*.app/*** +/certutil +/@MOZ_APP_NAME@-bin +#if defined(MOZ_GECKODRIVER) +/geckodriver +#endif +/gtest/*** +#if defined(MOZ_ASAN) || defined(MOZ_TSAN) +/llvm-symbolizer +#endif +#if defined(MOZ_CRASHREPORTER) +/minidump-analyzer +#endif +/pingsender +/pk12util +/ssltunnel +/xpcshell +/XUL diff --git a/browser/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in b/browser/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in new file mode 100644 index 0000000000..c84535de6a --- /dev/null +++ b/browser/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in @@ -0,0 +1,5 @@ +/* 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/. */ + +CFBundleName = "@MAC_APP_NAME@"; diff --git a/browser/app/macbuild/Contents/moz.build b/browser/app/macbuild/Contents/moz.build new file mode 100644 index 0000000000..b8b28926de --- /dev/null +++ b/browser/app/macbuild/Contents/moz.build @@ -0,0 +1,25 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +defs = [] + +for s in ( + "MOZ_GECKODRIVER", + "MOZ_ASAN", + "MOZ_TSAN", + "MOZ_CRASHREPORTER", + "MOZ_APP_NAME", +): + if CONFIG[s]: + defs.append("-D%s=%s" % (s, "1" if CONFIG[s] is True else CONFIG[s])) + +GeneratedFile( + "MacOS-files.txt", + script="/python/mozbuild/mozbuild/action/preprocessor.py", + entry_point="generate", + inputs=["MacOS-files.in"], + flags=defs, +) diff --git a/browser/app/macversion.py b/browser/app/macversion.py new file mode 100644 index 0000000000..3d9aaaa94a --- /dev/null +++ b/browser/app/macversion.py @@ -0,0 +1,47 @@ +#!/usr/bin/python +# 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/. + +from __future__ import absolute_import, print_function, unicode_literals +import io +from optparse import OptionParser +import sys +import re + +o = OptionParser() +o.add_option("--buildid", dest="buildid") +o.add_option("--version", dest="version") + +(options, args) = o.parse_args() + +if not options.buildid: + print("--buildid is required", file=sys.stderr) + sys.exit(1) + +if not options.version: + print("--version is required", file=sys.stderr) + sys.exit(1) + +# We want to build a version number that matches the format allowed for +# CFBundleVersion (nnnnn[.nn[.nn]]). We'll incorporate both the version +# number as well as the date, so that it changes at least daily (for nightly +# builds), but also so that newly-built older versions (e.g. beta build) aren't +# considered "newer" than previously-built newer versions (e.g. a trunk nightly) + +define, MOZ_BUILDID, buildid = ( + io.open(options.buildid, "r", encoding="utf-8").read().split() +) + +# extract only the major version (i.e. "14" from "14.0b1") +majorVersion = re.match(r"^(\d+)[^\d].*", options.version).group(1) +# last two digits of the year +twodigityear = buildid[2:4] +month = buildid[4:6] +if month[0] == "0": + month = month[1] +day = buildid[6:8] +if day[0] == "0": + day = day[1] + +print("%s.%s.%s" % (majorVersion + twodigityear, month, day)) diff --git a/browser/app/module.ver b/browser/app/module.ver new file mode 100644 index 0000000000..5ef8d2a02a --- /dev/null +++ b/browser/app/module.ver @@ -0,0 +1,8 @@ +WIN32_MODULE_COMPANYNAME=Mozilla Corporation +WIN32_MODULE_COPYRIGHT=©Firefox and Mozilla Developers; available under the MPL 2 license. +WIN32_MODULE_PRODUCTVERSION=@MOZ_APP_WINVERSION@ +WIN32_MODULE_PRODUCTVERSION_STRING=@MOZ_APP_VERSION@ +WIN32_MODULE_TRADEMARKS=Firefox is a Trademark of The Mozilla Foundation. +WIN32_MODULE_DESCRIPTION=@MOZ_APP_DISPLAYNAME@ +WIN32_MODULE_PRODUCTNAME=@MOZ_APP_DISPLAYNAME@ +WIN32_MODULE_NAME=@MOZ_APP_DISPLAYNAME@ diff --git a/browser/app/moz.build b/browser/app/moz.build new file mode 100644 index 0000000000..9da2da3376 --- /dev/null +++ b/browser/app/moz.build @@ -0,0 +1,151 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Firefox", "General") + +with Files("firefox.exe.manifest"): + BUG_COMPONENT = ("Core", "Widget: Win32") +with Files("module.ver"): + BUG_COMPONENT = ("Core", "Widget: Win32") +with Files("splash.rc"): + BUG_COMPONENT = ("Core", "Widget: Win32") + +with Files("macversion.py"): + BUG_COMPONENT = ("Core", "Widget: Cocoa") +with Files("macbuild/**"): + BUG_COMPONENT = ("Core", "Widget: Cocoa") + +with Files("moz.build"): + BUG_COMPONENT = ("Firefox Build System", "General") +with Files("Makefile.in"): + BUG_COMPONENT = ("Firefox Build System", "General") + +with Files("profile/channel-prefs.js"): + BUG_COMPONENT = ("Firefox", "Installer") +with Files("profile/firefox.js"): + BUG_COMPONENT = ("Firefox", "General") + +if CONFIG["MOZ_MACBUNDLE_NAME"]: + DIRS += ["macbuild/Contents"] + +if CONFIG["MOZ_NO_PIE_COMPAT"]: + GeckoProgram(CONFIG["MOZ_APP_NAME"] + "-bin") + + DIRS += ["no-pie"] +else: + GeckoProgram(CONFIG["MOZ_APP_NAME"]) + +SOURCES += [ + "nsBrowserApp.cpp", +] + +# Neither channel-prefs.js nor firefox.exe want to end up in dist/bin/browser. +DIST_SUBDIR = "" + +LOCAL_INCLUDES += [ + "!/build", + "/toolkit/xre", + "/xpcom/base", + "/xpcom/build", +] + +if CONFIG["LIBFUZZER"]: + USE_LIBS += ["fuzzer"] + LOCAL_INCLUDES += [ + "/tools/fuzzing/libfuzzer", + ] + +if CONFIG["ENABLE_GECKODRIVER"]: + DEFINES["MOZ_GECKODRIVER"] = True + +if CONFIG["CC_TYPE"] == "clang-cl": + # Always enter a Windows program through wmain, whether or not we're + # a console application. + WIN32_EXE_LDFLAGS += ["-ENTRY:wmainCRTStartup"] + +if CONFIG["OS_ARCH"] == "WINNT": + RCINCLUDE = "splash.rc" + DIRS += [ + "winlauncher", + ] + USE_LIBS += [ + "winlauncher", + ] + LOCAL_INCLUDES += [ + "/browser/app/winlauncher", + ] + DELAYLOAD_DLLS += [ + "oleaut32.dll", + "ole32.dll", + "rpcrt4.dll", + "version.dll", + ] + + if CONFIG["CC_TYPE"] == "clang-cl": + libpath_flag = "-LIBPATH:" + else: + libpath_flag = "-L" + + WIN32_EXE_LDFLAGS += [ + libpath_flag + OBJDIR + "/winlauncher/freestanding", + ] + +if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "Darwin": + USE_LIBS += [ + "mozsandbox", + ] + +if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "WINNT": + # For sandbox includes and the include dependencies those have + LOCAL_INCLUDES += [ + "/security/sandbox/chromium", + "/security/sandbox/chromium-shim", + ] + + USE_LIBS += [ + "sandbox_s", + ] + + DELAYLOAD_DLLS += [ + "winmm.dll", + "user32.dll", + ] + +# Control the default heap size. +# This is the heap returned by GetProcessHeap(). +# As we use the CRT heap, the default size is too large and wastes VM. +# +# The default heap size is 1MB on Win32. +# The heap will grow if need be. +# +# Set it to 256k. See bug 127069. +if CONFIG["OS_ARCH"] == "WINNT" and CONFIG["CC_TYPE"] not in ("clang", "gcc"): + LDFLAGS += ["/HEAP:0x40000"] + +DisableStlWrapping() + +if CONFIG["HAVE_CLOCK_MONOTONIC"]: + OS_LIBS += CONFIG["REALTIME_LIBS"] + +if CONFIG["MOZ_LINUX_32_SSE2_STARTUP_ERROR"]: + DEFINES["MOZ_LINUX_32_SSE2_STARTUP_ERROR"] = True + COMPILE_FLAGS["OS_CXXFLAGS"] = [ + f + for f in COMPILE_FLAGS.get("OS_CXXFLAGS", []) + if not f.startswith("-march=") and f not in ("-msse", "-msse2", "-mfpmath=sse") + ] + [ + "-mno-sse", + "-mno-sse2", + "-mfpmath=387", + ] + +for icon in ("firefox", "document", "newwindow", "newtab", "pbmode"): + DEFINES[icon.upper() + "_ICO"] = '"%s/%s/%s.ico"' % ( + TOPSRCDIR, + CONFIG["MOZ_BRANDING_DIRECTORY"], + icon, + ) diff --git a/browser/app/no-pie/NoPie.c b/browser/app/no-pie/NoPie.c new file mode 100644 index 0000000000..39b206e0af --- /dev/null +++ b/browser/app/no-pie/NoPie.c @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 <errno.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int main(int argc, char* argv[]) { + // Ideally, we'd use mozilla::BinaryPath, but that pulls in stdc++compat, + // and further causes trouble linking with LTO. + char path[PATH_MAX + 4]; + ssize_t len = readlink("/proc/self/exe", path, PATH_MAX - 1); + if (len < 0) { + fprintf(stderr, "Couldn't find the application directory.\n"); + return 255; + } + strcpy(path + len, "-bin"); + execv(path, argv); + // execv never returns. If it did, there was an error. + fprintf(stderr, "Exec failed with error: %s\n", strerror(errno)); + return 255; +} diff --git a/browser/app/no-pie/moz.build b/browser/app/no-pie/moz.build new file mode 100644 index 0000000000..74aa89409d --- /dev/null +++ b/browser/app/no-pie/moz.build @@ -0,0 +1,24 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Program(CONFIG["MOZ_APP_NAME"]) + +SOURCES += [ + "NoPie.c", +] + +# For some reason, LTO messes things up. We don't care anyways. +CFLAGS += [ + "-fno-lto", +] + +# Use OS_LIBS instead of LDFLAGS to "force" the flag to come after -pie +# from MOZ_PROGRAM_LDFLAGS. +if CONFIG["CC_TYPE"] == "clang": + # clang < 5.0 doesn't support -no-pie. + OS_LIBS += ["-nopie"] +else: + OS_LIBS += ["-no-pie"] diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp new file mode 100644 index 0000000000..de7bd4d94a --- /dev/null +++ b/browser/app/nsBrowserApp.cpp @@ -0,0 +1,364 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsXULAppAPI.h" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/XREAppData.h" +#include "XREShellData.h" +#include "application.ini.h" +#include "mozilla/Bootstrap.h" +#if defined(XP_WIN) +# include <windows.h> +# include <stdlib.h> +# include "mozilla/PreXULSkeletonUI.h" +#elif defined(XP_UNIX) +# include <sys/resource.h> +# include <unistd.h> +#endif + +#include <stdio.h> +#include <stdarg.h> +#include <time.h> + +#include "nsCOMPtr.h" + +#ifdef XP_WIN +# include "freestanding/SharedSection.h" +# include "LauncherProcessWin.h" +# include "mozilla/WindowsDllBlocklist.h" + +# define XRE_WANT_ENVIRON +# define strcasecmp _stricmp +# ifdef MOZ_SANDBOX +# include "mozilla/sandboxing/SandboxInitialization.h" +# endif +#endif +#include "BinaryPath.h" + +#include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL + +#include "mozilla/Sprintf.h" +#include "mozilla/StartupTimeline.h" +#include "BaseProfiler.h" + +#ifdef LIBFUZZER +# include "FuzzerDefs.h" +#endif + +#ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR +# include <cpuid.h> +# include "mozilla/Unused.h" + +static bool IsSSE2Available() { + // The rest of the app has been compiled to assume that SSE2 is present + // unconditionally, so we can't use the normal copy of SSE.cpp here. + // Since SSE.cpp caches the results and we need them only transiently, + // instead of #including SSE.cpp here, let's just inline the specific check + // that's needed. + unsigned int level = 1u; + unsigned int eax, ebx, ecx, edx; + unsigned int bits = (1u << 26); + unsigned int max = __get_cpuid_max(0, nullptr); + if (level > max) { + return false; + } + __cpuid_count(level, 0, eax, ebx, ecx, edx); + return (edx & bits) == bits; +} + +static const char sSSE2Message[] = + "This browser version requires a processor with the SSE2 instruction " + "set extension.\nYou may be able to obtain a version that does not " + "require SSE2 from your Linux distribution.\n"; + +__attribute__((constructor)) static void SSE2Check() { + if (IsSSE2Available()) { + return; + } + // Using write() in order to avoid jemalloc-based buffering. Ignoring return + // values, since there isn't much we could do on failure and there is no + // point in trying to recover from errors. + MOZ_UNUSED( + write(STDERR_FILENO, sSSE2Message, MOZ_ARRAY_LENGTH(sSSE2Message) - 1)); + // _exit() instead of exit() to avoid running the usual "at exit" code. + _exit(255); +} +#endif + +#if !defined(MOZ_WIDGET_COCOA) && !defined(MOZ_WIDGET_ANDROID) +# define MOZ_BROWSER_CAN_BE_CONTENTPROC +# include "../../ipc/contentproc/plugin-container.cpp" +#endif + +using namespace mozilla; + +#ifdef XP_MACOSX +# define kOSXResourcesFolder "Resources" +#endif +#define kDesktopFolder "browser" + +static MOZ_FORMAT_PRINTF(1, 2) void Output(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + +#ifndef XP_WIN + vfprintf(stderr, fmt, ap); +#else + char msg[2048]; + vsnprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap); + + wchar_t wide_msg[2048]; + MultiByteToWideChar(CP_UTF8, 0, msg, -1, wide_msg, _countof(wide_msg)); +# if MOZ_WINCONSOLE + fwprintf_s(stderr, wide_msg); +# else + // Linking user32 at load-time interferes with the DLL blocklist (bug 932100). + // This is a rare codepath, so we can load user32 at run-time instead. + HMODULE user32 = LoadLibraryW(L"user32.dll"); + if (user32) { + decltype(MessageBoxW)* messageBoxW = + (decltype(MessageBoxW)*)GetProcAddress(user32, "MessageBoxW"); + if (messageBoxW) { + messageBoxW(nullptr, wide_msg, L"Firefox", + MB_OK | MB_ICONERROR | MB_SETFOREGROUND); + } + FreeLibrary(user32); + } +# endif +#endif + + va_end(ap); +} + +/** + * Return true if |arg| matches the given argument name. + */ +static bool IsArg(const char* arg, const char* s) { + if (*arg == '-') { + if (*++arg == '-') ++arg; + return !strcasecmp(arg, s); + } + +#if defined(XP_WIN) + if (*arg == '/') return !strcasecmp(++arg, s); +#endif + + return false; +} + +Bootstrap::UniquePtr gBootstrap; + +static int do_main(int argc, char* argv[], char* envp[]) { + // Allow firefox.exe to launch XULRunner apps via -app <application.ini> + // Note that -app must be the *first* argument. + const char* appDataFile = getenv("XUL_APP_FILE"); + if ((!appDataFile || !*appDataFile) && (argc > 1 && IsArg(argv[1], "app"))) { + if (argc == 2) { + Output("Incorrect number of arguments passed to -app"); + return 255; + } + appDataFile = argv[2]; + + char appEnv[MAXPATHLEN]; + SprintfLiteral(appEnv, "XUL_APP_FILE=%s", argv[2]); + if (putenv(strdup(appEnv))) { + Output("Couldn't set %s.\n", appEnv); + return 255; + } + argv[2] = argv[0]; + argv += 2; + argc -= 2; + } else if (argc > 1 && IsArg(argv[1], "xpcshell")) { + for (int i = 1; i < argc; i++) { + argv[i] = argv[i + 1]; + } + + XREShellData shellData; +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + shellData.sandboxBrokerServices = + sandboxing::GetInitializedBrokerServices(); +#endif + + return gBootstrap->XRE_XPCShellMain(--argc, argv, envp, &shellData); + } + + BootstrapConfig config; + + if (appDataFile && *appDataFile) { + config.appData = nullptr; + config.appDataPath = appDataFile; + } else { + // no -app flag so we use the compiled-in app data + config.appData = &sAppData; + config.appDataPath = kDesktopFolder; + } + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + sandbox::BrokerServices* brokerServices = + sandboxing::GetInitializedBrokerServices(); + sandboxing::PermissionsService* permissionsService = + sandboxing::GetPermissionsService(); + if (!brokerServices) { + Output("Couldn't initialize the broker services.\n"); + return 255; + } + config.sandboxBrokerServices = brokerServices; + config.sandboxPermissionsService = permissionsService; +#endif + +#ifdef LIBFUZZER + if (getenv("FUZZER")) + gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver); +#endif + + // Note: keep in sync with LauncherProcessWin. + const char* acceptableParams[] = {"url", nullptr}; + EnsureCommandlineSafe(argc, argv, acceptableParams); + + return gBootstrap->XRE_main(argc, argv, config); +} + +static nsresult InitXPCOMGlue(LibLoadingStrategy aLibLoadingStrategy) { + if (gBootstrap) { + return NS_OK; + } + + UniqueFreePtr<char> exePath = BinaryPath::Get(); + if (!exePath) { + Output("Couldn't find the application directory.\n"); + return NS_ERROR_FAILURE; + } + + gBootstrap = mozilla::GetBootstrap(exePath.get(), aLibLoadingStrategy); + if (!gBootstrap) { + Output("Couldn't load XPCOM.\n"); + return NS_ERROR_FAILURE; + } + + // This will set this thread as the main thread. + gBootstrap->NS_LogInit(); + + return NS_OK; +} + +#ifdef HAS_DLL_BLOCKLIST +// NB: This must be extern, as this value is checked elsewhere +uint32_t gBlocklistInitFlags = eDllBlocklistInitFlagDefault; +#endif + +int main(int argc, char* argv[], char* envp[]) { +#if defined(MOZ_ENABLE_FORKSERVER) + if (strcmp(argv[argc - 1], "forkserver") == 0) { + nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead); + if (NS_FAILED(rv)) { + return 255; + } + + // Run a fork server in this process, single thread. When it + // returns, it means the fork server have been stopped or a new + // content process is created. + // + // For the later case, XRE_ForkServer() will return false, running + // in a content process just forked from the fork server process. + // argc & argv will be updated with the values passing from the + // chrome process. With the new values, this function + // continues the reset of the code acting as a content process. + if (gBootstrap->XRE_ForkServer(&argc, &argv)) { + // Return from the fork server in the fork server process. + // Stop the fork server. + gBootstrap->NS_LogTerm(); + return 0; + } + // In a content process forked from the fork server. + // Start acting as a content process. + } +#endif + + mozilla::TimeStamp start = mozilla::TimeStamp::Now(); + + AUTO_BASE_PROFILER_INIT; + AUTO_BASE_PROFILER_LABEL("nsBrowserApp main", OTHER); + +#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC + // We are launching as a content process, delegate to the appropriate + // main + if (argc > 1 && IsArg(argv[1], "contentproc")) { +# ifdef HAS_DLL_BLOCKLIST + DllBlocklist_Initialize(gBlocklistInitFlags | + eDllBlocklistInitFlagIsChildProcess); +# endif +# if defined(XP_WIN) && defined(MOZ_SANDBOX) + // We need to initialize the sandbox TargetServices before InitXPCOMGlue + // because we might need the sandbox broker to give access to some files. + if (IsSandboxedProcess() && !sandboxing::GetInitializedTargetServices()) { + Output("Failed to initialize the sandbox target services."); + return 255; + } +# endif + + nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead); + if (NS_FAILED(rv)) { + return 255; + } + + int result = content_process_main(gBootstrap.get(), argc, argv); + +# if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST) + DllBlocklist_Shutdown(); +# endif + + // InitXPCOMGlue calls NS_LogInit, so we need to balance it here. + gBootstrap->NS_LogTerm(); + + return result; + } +#endif + +#ifdef HAS_DLL_BLOCKLIST + DllBlocklist_Initialize(gBlocklistInitFlags); +#endif + +#if defined(XP_WIN) + // Once the browser process hits the main function, we no longer need + // a writable section handle because all dependent modules have been + // loaded. + mozilla::freestanding::gSharedSection.ConvertToReadOnly(); + ::RtlRunOnceInitialize(&mozilla::freestanding::gK32ExportsResolveOnce); + + mozilla::CreateAndStorePreXULSkeletonUI(GetModuleHandle(nullptr), argc, argv); +#endif + + nsresult rv = InitXPCOMGlue(LibLoadingStrategy::ReadAhead); + if (NS_FAILED(rv)) { + return 255; + } + + gBootstrap->XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start); + +#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC + gBootstrap->XRE_EnableSameExecutableForContentProc(); +#endif + + int result = do_main(argc, argv, envp); + + gBootstrap->NS_LogTerm(); + +#if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST) + DllBlocklist_Shutdown(); +#endif + +#ifdef XP_MACOSX + // Allow writes again. While we would like to catch writes from static + // destructors to allow early exits to use _exit, we know that there is + // at least one such write that we don't control (see bug 826029). For + // now we enable writes again and early exits will have to use exit instead + // of _exit. + gBootstrap->XRE_StopLateWriteChecks(); +#endif + + gBootstrap.reset(); + + return result; +} diff --git a/browser/app/permissions b/browser/app/permissions new file mode 100644 index 0000000000..991284081d --- /dev/null +++ b/browser/app/permissions @@ -0,0 +1,25 @@ +# This file has default permissions for the permission manager. +# The file-format is strict: +# * matchtype \t type \t permission \t host +# * "origin" should be used for matchtype, "host" is supported for legacy reasons +# * type is a string that identifies the type of permission (e.g. "cookie") +# * permission is an integer between 1 and 15 +# See PermissionManager.cpp for more... + +# UITour +# Bug 1557153: www.mozilla.org gets a special workaround in UITourChild.jsm +origin uitour 1 https://www.mozilla.org +origin uitour 1 https://monitor.firefox.com +origin uitour 1 https://screenshots.firefox.com +origin uitour 1 https://support.mozilla.org +origin uitour 1 about:home +origin uitour 1 about:newtab + +# XPInstall +origin install 1 https://addons.mozilla.org + +# Remote troubleshooting +origin remote-troubleshooting 1 https://support.mozilla.org + +# addon install +origin install 1 https://fpn.firefox.com diff --git a/browser/app/profile/channel-prefs.js b/browser/app/profile/channel-prefs.js new file mode 100644 index 0000000000..eed6e634c2 --- /dev/null +++ b/browser/app/profile/channel-prefs.js @@ -0,0 +1,9 @@ +/* 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/. */ +// +// This pref is in its own file for complex reasons. See the comment in +// browser/app/Makefile.in, bug 756325, and bug 1431342 for details. Do not add +// other prefs to this file. + +pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@"); diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js new file mode 100644 index 0000000000..5aad2ddcf4 --- /dev/null +++ b/browser/app/profile/firefox.js @@ -0,0 +1,2530 @@ +#filter dumbComments emptyLines substitution + +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +// 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/. + +// Non-static prefs that are specific to desktop Firefox belong in this file +// (unless there is a compelling and documented reason for them to belong in +// another file). +// +// Please indent all prefs defined within #ifdef/#ifndef conditions. This +// improves readability, particular for conditional blocks that exceed a single +// screen. + +#ifdef XP_UNIX + #ifndef XP_MACOSX + #define UNIX_BUT_NOT_MAC + #endif +#endif + +pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindowMac.xhtml"); + +// Enables some extra Extension System Logging (can reduce performance) +pref("extensions.logging.enabled", false); + +// Disables strict compatibility, making addons compatible-by-default. +pref("extensions.strictCompatibility", false); + +// Temporary preference to forcibly make themes more safe with Australis even if +// extensions.checkCompatibility=false has been set. +pref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "29.0a1"); + +pref("extensions.webextPermissionPrompts", true); +pref("extensions.webextOptionalPermissionPrompts", true); +// If enabled, install origin permission verification happens after addons are downloaded. +pref("extensions.postDownloadThirdPartyPrompt", true); + +// Preferences for AMO integration +pref("extensions.getAddons.cache.enabled", true); +pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/api/v4/addons/search/?guid=%IDS%&lang=%LOCALE%"); +pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%"); +pref("extensions.getAddons.link.url", "https://addons.mozilla.org/%LOCALE%/firefox/"); +pref("extensions.getAddons.langpacks.url", "https://services.addons.mozilla.org/api/v4/addons/language-tools/?app=firefox&type=language&appversion=%VERSION%"); +pref("extensions.getAddons.discovery.api_url", "https://services.addons.mozilla.org/api/v4/discovery/?lang=%LOCALE%&edition=%DISTRIBUTION%"); + +// Use bloomfilters for the addons blocklist, instead of JSON only. +pref("extensions.blocklist.useMLBF", true); +pref("extensions.blocklist.useMLBF.stashes", true); + +// The URL for the privacy policy related to recommended extensions. +pref("extensions.recommendations.privacyPolicyUrl", "https://www.mozilla.org/privacy/firefox/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_content=privacy-policy-link#addons"); +// The URL for Firefox Color, recommended on the theme page in about:addons. +pref("extensions.recommendations.themeRecommendationUrl", "https://color.firefox.com/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_content=theme-footer-link"); + +pref("extensions.update.autoUpdateDefault", true); + +// Check AUS for system add-on updates. +pref("extensions.systemAddon.update.url", "https://aus5.mozilla.org/update/3/SystemAddons/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml"); +pref("extensions.systemAddon.update.enabled", true); + +// Disable add-ons that are not installed by the user in all scopes by default. +// See the SCOPE constants in AddonManager.jsm for values to use here. +pref("extensions.autoDisableScopes", 15); +// Scopes to scan for changes at startup. +pref("extensions.startupScanScopes", 0); + +pref("extensions.geckoProfiler.acceptedExtensionIds", "geckoprofiler@mozilla.com,quantum-foxfooding@mozilla.com,raptor@mozilla.org"); + + +pref("extensions.webextensions.remote", true); +pref("extensions.webextensions.background-delayed-startup", true); + +// Require signed add-ons by default +pref("extensions.langpacks.signatures.required", true); +pref("xpinstall.signatures.required", true); +pref("xpinstall.signatures.devInfoURL", "https://wiki.mozilla.org/Addons/Extension_Signing"); + +// Enable extensionStorage storage actor by default +pref("devtools.storage.extensionStorage.enabled", true); + +// Dictionary download preference +pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/language-tools/"); + +// At startup, should we check to see if the installation +// date is older than some threshold +pref("app.update.checkInstallTime", true); + +// The number of days a binary is permitted to be old without checking is defined in +// firefox-branding.js (app.update.checkInstallTime.days) + +// The minimum delay in seconds for the timer to fire between the notification +// of each consumer of the timer manager. +// minimum=30 seconds, default=120 seconds, and maximum=300 seconds +pref("app.update.timerMinimumDelay", 120); + +// The minimum delay in milliseconds for the first firing after startup of the timer +// to notify consumers of the timer manager. +// minimum=10 seconds, default=30 seconds, and maximum=120 seconds +pref("app.update.timerFirstInterval", 30000); + +// App-specific update preferences + +// The interval to check for updates (app.update.interval) is defined in +// firefox-branding.js + +// Enables some extra Application Update Logging (can reduce performance) +pref("app.update.log", false); +// Causes Application Update Logging to be sent to a file in the profile +// directory. This preference is automatically disabled on application start to +// prevent it from being left on accidentally. Turning this pref on enables +// logging, even if app.update.log is false. +pref("app.update.log.file", false); + +// The number of general background check failures to allow before notifying the +// user of the failure. User initiated update checks always notify the user of +// the failure. +pref("app.update.backgroundMaxErrors", 10); + +// Ids of the links to the "What's new" update documentation +pref("app.update.link.updateAvailableWhatsNew", "update-available-whats-new"); +pref("app.update.link.updateManualWhatsNew", "update-manual-whats-new"); + +// How many times we should let downloads fail before prompting the user to +// download a fresh installer. +pref("app.update.download.promptMaxAttempts", 2); + +// How many times we should let an elevation prompt fail before prompting the user to +// download a fresh installer. +pref("app.update.elevation.promptMaxAttempts", 2); + +// If set to true, a message will be displayed in the hamburger menu while +// an update is being downloaded. +pref("app.update.notifyDuringDownload", false); + +// If set to true, the Update Service will automatically download updates if the +// user can apply updates. This pref is no longer used on Windows, except as the +// default value to migrate to the new location that this data is now stored +// (which is in a file in the update directory). Because of this, this pref +// should no longer be used directly. Instead, getAppUpdateAutoEnabled and +// getAppUpdateAutoEnabled from UpdateUtils.jsm should be used. +#ifndef XP_WIN + pref("app.update.auto", true); +#endif + +// If set to true, the Update Service will apply updates in the background +// when it finishes downloading them. +pref("app.update.staging.enabled", true); + +// Update service URL: +// app.update.url was removed in Bug 1568994 +// app.update.url.manual is in branding section +// app.update.url.details is in branding section + +// app.update.badgeWaitTime is in branding section +// app.update.interval is in branding section +// app.update.promptWaitTime is in branding section + +// Whether or not to attempt using the service for updates. +#ifdef MOZ_MAINTENANCE_SERVICE + pref("app.update.service.enabled", true); +#endif + +#ifdef MOZ_BITS_DOWNLOAD + // If set to true, the Update Service will attempt to use Windows BITS to + // download updates and will fallback to downloading internally if that fails. + pref("app.update.BITS.enabled", true); +#endif + +pref("app.update.langpack.enabled", true); + +// Symmetric (can be overridden by individual extensions) update preferences. +// e.g. +// extensions.{GUID}.update.enabled +// extensions.{GUID}.update.url +// .. etc .. +// +pref("extensions.update.enabled", true); +pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%"); +pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%"); +pref("extensions.update.interval", 86400); // Check for updates to Extensions and + // Themes every day + +pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes"); + +#if defined(MOZ_WIDEVINE_EME) + pref("browser.eme.ui.enabled", true); +#else + pref("browser.eme.ui.enabled", false); +#endif + +// UI tour experience. +pref("browser.uitour.enabled", true); +pref("browser.uitour.loglevel", "Error"); +pref("browser.uitour.requireSecure", true); +pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/"); +pref("browser.uitour.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tour/"); +// How long to show a Hearbeat survey (two hours, in seconds) +pref("browser.uitour.surveyDuration", 7200); + +pref("keyword.enabled", true); + +// Fixup whitelists, the urlbar won't try to search for these words, but will +// instead consider them valid TLDs. Don't check these directly, use +// Services.uriFixup.isDomainKnown() instead. +pref("browser.fixup.domainwhitelist.localhost", true); +// https://tools.ietf.org/html/rfc2606 +pref("browser.fixup.domainsuffixwhitelist.test", true); +pref("browser.fixup.domainsuffixwhitelist.example", true); +pref("browser.fixup.domainsuffixwhitelist.invalid", true); +pref("browser.fixup.domainsuffixwhitelist.localhost", true); +// https://tools.ietf.org/html/draft-wkumari-dnsop-internal-00 +pref("browser.fixup.domainsuffixwhitelist.internal", true); +// https://tools.ietf.org/html/rfc6762 +pref("browser.fixup.domainsuffixwhitelist.local", true); + +// Whether to always go through the DNS server before sending a single word +// search string, that may contain a valid host, to a search engine. +pref("browser.fixup.dns_first_for_single_words", false); + +#ifdef UNIX_BUT_NOT_MAC + pref("general.autoScroll", false); +#else + pref("general.autoScroll", true); +#endif + +// UI density of the browser chrome. This mostly affects toolbarbutton +// and urlbar spacing. The possible values are 0=normal, 1=compact, 2=touch. +pref("browser.uidensity", 0); +// Whether Firefox will automatically override the uidensity to "touch" +// while the user is in a touch environment (such as Windows tablet mode). +pref("browser.touchmode.auto", true); + +// At startup, check if we're the default browser and prompt user if not. +pref("browser.shell.checkDefaultBrowser", true); +pref("browser.shell.shortcutFavicons",true); +pref("browser.shell.mostRecentDateSetAsDefault", ""); +pref("browser.shell.skipDefaultBrowserCheckOnFirstRun", true); +pref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun", false); +pref("browser.shell.defaultBrowserCheckCount", 0); +#ifdef EARLY_BETA_OR_EARLIER +pref("browser.defaultbrowser.notificationbar", true); +#else +pref("browser.defaultbrowser.notificationbar", false); +#endif +pref("browser.defaultbrowser.notificationbar.checkcount", 0); +pref("browser.defaultbrowser.notificationbar.checklimit", 10000); + +// 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session +// The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore +pref("browser.startup.page", 1); +pref("browser.startup.homepage", "about:home"); +#ifdef NIGHTLY_BUILD +pref("browser.startup.homepage.abouthome_cache.enabled", true); +#else +pref("browser.startup.homepage.abouthome_cache.enabled", false); +#endif +pref("browser.startup.homepage.abouthome_cache.loglevel", "Warn"); + +// Whether we should skip the homepage when opening the first-run page +pref("browser.startup.firstrunSkipsHomepage", true); + +// Show an about:blank window as early as possible for quick startup feedback. +// Held to nightly on Linux due to bug 1450626. +// Disabled on Mac because the bouncing dock icon already provides feedback. +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) && defined(NIGHTLY_BUILD) + pref("browser.startup.blankWindow", true); +#else + pref("browser.startup.blankWindow", false); +#endif + +// Show a skeleton UI window prior to loading libxul. Only visible for windows +// users as it is not implemented anywhere else. +#if defined(XP_WIN) +#ifdef NIGHTLY_BUILD +pref("browser.startup.preXulSkeletonUI", true); +#else +pref("browser.startup.preXulSkeletonUI", false); +#endif +#endif + +// Don't create the hidden window during startup on +// platforms that don't always need it (Win/Linux). +pref("toolkit.lazyHiddenWindow", true); + +pref("browser.slowStartup.notificationDisabled", false); +pref("browser.slowStartup.timeThreshold", 20000); +pref("browser.slowStartup.maxSamples", 5); + +pref("browser.chrome.site_icons", true); +// browser.warnOnQuit == false will override all other possible prompts when quitting or restarting +pref("browser.warnOnQuit", true); +pref("browser.fullscreen.autohide", true); +pref("browser.overlink-delay", 80); + +// Whether using `ctrl` when hitting return/enter in the URL bar +// (or clicking 'go') should prefix 'www.' and suffix +// browser.fixup.alternate.suffix to the URL bar value prior to +// navigating. +pref("browser.urlbar.ctrlCanonizesURLs", true); + +// Whether we announce to screen readers when tab-to-search results are +// inserted. +pref("browser.urlbar.accessibility.tabToSearch.announceResults", true); + +// Control autoFill behavior +pref("browser.urlbar.autoFill", true); + +// Whether to warm up network connections for autofill or search results. +pref("browser.urlbar.speculativeConnect.enabled", true); + +// Whether bookmarklets should be filtered out of Address Bar matches. +// This is enabled for security reasons, when true it is still possible to +// search for bookmarklets typing "javascript: " followed by the actual query. +pref("browser.urlbar.filter.javascript", true); + +// Enable a certain level of urlbar logging to the Browser Console. See Log.jsm. +pref("browser.urlbar.loglevel", "Error"); + +// the maximum number of results to show in autocomplete when doing richResults +pref("browser.urlbar.maxRichResults", 10); + +// The maximum number of historical search results to show. +pref("browser.urlbar.maxHistoricalSearchSuggestions", 2); + +// The default behavior for the urlbar can be configured to use any combination +// of the match filters with each additional filter adding more results (union). +pref("browser.urlbar.suggest.bookmark", true); +pref("browser.urlbar.suggest.history", true); +pref("browser.urlbar.suggest.openpage", true); +pref("browser.urlbar.suggest.searches", true); +pref("browser.urlbar.suggest.topsites", true); +pref("browser.urlbar.suggest.engines", true); + +// As a user privacy measure, don't fetch search suggestions if a pasted string +// is longer than this. +pref("browser.urlbar.maxCharsForSearchSuggestions", 100); + +pref("browser.urlbar.trimURLs", true); + +// If changed to true, copying the entire URL from the location bar will put the +// human readable (percent-decoded) URL on the clipboard. +pref("browser.urlbar.decodeURLsOnCopy", false); + +// Whether or not to move tabs into the active window when using the "Switch to +// Tab" feature of the awesomebar. +pref("browser.urlbar.switchTabs.adoptIntoActiveWindow", false); + +// Whether addresses and search results typed into the address bar +// should be opened in new tabs by default. +pref("browser.urlbar.openintab", false); + +// If true, we show tail suggestions when available. +pref("browser.urlbar.richSuggestions.tail", true); + +// Controls the empty search behavior in Search Mode: +// 0 - Show nothing +// 1 - Show search history +// 2 - Show search and browsing history +pref("browser.urlbar.update2.emptySearchBehavior", 0); + +// Whether the urlbar displays one-offs to filter searches to history, +// bookmarks, or tabs. +pref("browser.urlbar.shortcuts.bookmarks", true); +pref("browser.urlbar.shortcuts.tabs", true); +pref("browser.urlbar.shortcuts.history", true); + +pref("browser.urlbar.eventTelemetry.enabled", false); + +// When we send events to Urlbar extensions, we wait this amount of time in +// milliseconds for them to respond before timing out. +pref("browser.urlbar.extension.timeout", 400); + +// Controls when to DNS resolve single word search strings, after they were +// searched for. If the string is resolved as a valid host, show a +// "Did you mean to go to 'host'" prompt. +// 0 - never resolve; 1 - use heuristics (default); 2 - always resolve +pref("browser.urlbar.dnsResolveSingleWordsAfterSearch", 1); + +// Whether IME composition should close the results panel. +// The default value is true because some IME open a picker panel, and we end +// up with two panels on top of each other. Since for now we can't detect that +// we leave this choice to the user, hopefully in the future this can be flipped +// for everyone. +pref("browser.urlbar.imeCompositionClosesPanel", true); + +pref("browser.altClickSave", false); + +// Enable logging downloads operations to the Console. +pref("browser.download.loglevel", "Error"); + +// Number of milliseconds to wait for the http headers (and thus +// the Content-Disposition filename) before giving up and falling back to +// picking a filename without that info in hand so that the user sees some +// feedback from their action. +pref("browser.download.saveLinkAsFilenameTimeout", 4000); + +pref("browser.download.useDownloadDir", true); +pref("browser.download.folderList", 1); +pref("browser.download.manager.addToRecentDocs", true); +pref("browser.download.manager.resumeOnWakeDelay", 10000); + +// This allows disabling the animated notifications shown by +// the Downloads Indicator when a download starts or completes. +pref("browser.download.animateNotifications", true); + +// This records whether or not the panel has been shown at least once. +pref("browser.download.panel.shown", false); + +// This records whether or not to show the 'Open in system viewer' context menu item when appropriate +pref("browser.download.openInSystemViewerContextMenuItem", true); + +// This records whether or not to show the 'Always open...' context menu item when appropriate +pref("browser.download.alwaysOpenInSystemViewerContextMenuItem", true); + +// Open downloaded file types internally for the given types. +// This is a comma-separated list, the empty string ("") means no types are +// viewable internally. +pref("browser.download.viewableInternally.enabledTypes", "xml,svg,webp,avif"); + + +// This controls whether the button is automatically shown/hidden depending +// on whether there are downloads to show. +pref("browser.download.autohideButton", true); + +#ifndef XP_MACOSX + pref("browser.helperApps.deleteTempFileOnExit", true); +#endif + +// This controls the visibility of the radio button in the +// Unknown Content Type (Helper App) dialog that will open +// the content in the browser for PDF and for other +// Viewable Internally types +// (see browser.download.viewableInternally.enabledTypes) +pref("browser.helperApps.showOpenOptionForPdfJS", true); +pref("browser.helperApps.showOpenOptionForViewableInternally", true); + +// search engines URL +pref("browser.search.searchEnginesURL", "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/"); + +// search bar results always open in a new tab +pref("browser.search.openintab", false); + +// context menu searches open in the foreground +pref("browser.search.context.loadInBackground", false); + +// comma seperated list of of engines to hide in the search panel. +pref("browser.search.hiddenOneOffs", ""); + +// Mirrors whether the search-container widget is in the navigation toolbar. +pref("browser.search.widget.inNavBar", false); + +// Enables display of the options for the user using a separate default search +// engine in private browsing mode. +pref("browser.search.separatePrivateDefault.ui.enabled", false); +// The maximum amount of times the private default banner is shown. +pref("browser.search.separatePrivateDefault.ui.banner.max", 0); + +// Enables the display of the Mozilla VPN banner in private browsing windows +pref("browser.privatebrowsing.vpnpromourl", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-%CHANNEL%-browser&utm_campaign=private-browsing-vpn-link"); + +pref("browser.sessionhistory.max_entries", 50); + +// Built-in default permissions. +pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions"); + +// Set default fallback values for site permissions we want +// the user to be able to globally change. +pref("permissions.default.camera", 0); +pref("permissions.default.microphone", 0); +pref("permissions.default.geo", 0); +pref("permissions.default.xr", 0); +pref("permissions.default.desktop-notification", 0); +pref("permissions.default.shortcuts", 0); + +pref("permissions.desktop-notification.postPrompt.enabled", true); +pref("permissions.desktop-notification.notNow.enabled", false); + +pref("permissions.fullscreen.allowed", false); + +// handle links targeting new windows +// 1=current window/tab, 2=new window, 3=new tab in most recent window +pref("browser.link.open_newwindow", 3); + +// handle external links (i.e. links opened from a different application) +// default: use browser.link.open_newwindow +// 1-3: see browser.link.open_newwindow for interpretation +pref("browser.link.open_newwindow.override.external", -1); + +// 0: no restrictions - divert everything +// 1: don't divert window.open at all +// 2: don't divert window.open with features +pref("browser.link.open_newwindow.restriction", 2); + +// If true, this pref causes windows opened by window.open to be forced into new +// tabs (rather than potentially opening separate windows, depending on +// window.open arguments) when the browser is in fullscreen mode. +// We set this differently on Mac because the fullscreen implementation there is +// different. +#ifdef XP_MACOSX + pref("browser.link.open_newwindow.disabled_in_fullscreen", true); +#else + pref("browser.link.open_newwindow.disabled_in_fullscreen", false); +#endif + +// Tabbed browser +pref("browser.tabs.closeTabByDblclick", false); +pref("browser.tabs.closeWindowWithLastTab", true); +pref("browser.tabs.allowTabDetach", true); +// Open related links to a tab, e.g., link in current tab, at next to the +// current tab if |insertRelatedAfterCurrent| is true. Otherwise, always +// append new tab to the end. +pref("browser.tabs.insertRelatedAfterCurrent", true); +// Open all links, e.g., bookmarks, history items at next to current tab +// if |insertAfterCurrent| is true. Otherwise, append new tab to the end +// for non-related links. Note that if this is set to true, it will trump +// the value of browser.tabs.insertRelatedAfterCurrent. +pref("browser.tabs.insertAfterCurrent", false); +pref("browser.tabs.warnOnClose", true); +pref("browser.tabs.warnOnCloseOtherTabs", true); +pref("browser.tabs.warnOnOpen", true); +pref("browser.tabs.maxOpenBeforeWarn", 15); +pref("browser.tabs.loadInBackground", true); +pref("browser.tabs.opentabfor.middleclick", true); +pref("browser.tabs.loadDivertedInBackground", false); +pref("browser.tabs.loadBookmarksInBackground", false); +pref("browser.tabs.loadBookmarksInTabs", false); +pref("browser.tabs.tabClipWidth", 140); +pref("browser.tabs.tabMinWidth", 76); +// Initial titlebar state is managed by -moz-gtk-csd-hide-titlebar-by-default +// on Linux. +#ifndef UNIX_BUT_NOT_MAC + pref("browser.tabs.drawInTitlebar", true); +#endif + +//Control the visibility of Tab Manager Menu. +pref("browser.tabs.tabmanager.enabled", false); + +// Offer additional drag space to the user. The drag space +// will only be shown if browser.tabs.drawInTitlebar is true. +pref("browser.tabs.extraDragSpace", false); + +// When tabs opened by links in other tabs via a combination of +// browser.link.open_newwindow being set to 3 and target="_blank" etc are +// closed: +// true return to the tab that opened this tab (its owner) +// false return to the adjacent tab (old default) +pref("browser.tabs.selectOwnerOnClose", true); + +// This should match Chromium's audio indicator delay. +pref("browser.tabs.delayHidingAudioPlayingIconMS", 3000); + +// Pref to control whether we use a separate privileged content process +// for about: pages. This pref name did not age well: we will have multiple +// types of privileged content processes, each with different privileges. +// types of privleged content processes, each with different privleges. +pref("browser.tabs.remote.separatePrivilegedContentProcess", true); + +#if defined(NIGHTLY_BUILD) && !defined(MOZ_ASAN) + // This pref will cause assertions when a remoteType triggers a process switch + // to a new remoteType it should not be able to trigger. + pref("browser.tabs.remote.enforceRemoteTypeRestrictions", true); +#endif + +// Pref to control whether we use a separate privileged content process +// for certain mozilla webpages (which are listed in the pref +// browser.tabs.remote.separatedMozillaDomains). +pref("browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true); + +#ifdef NIGHTLY_BUILD +pref("browser.tabs.tooltipsShowPidAndActiveness", true); +#else +pref("browser.tabs.tooltipsShowPidAndActiveness", false); +#endif + +// allow_eval_* is enabled on Firefox Desktop only at this +// point in time +pref("security.allow_eval_with_system_principal", false); +pref("security.allow_eval_in_parent_process", false); + +#if defined(NIGHTLY_BUILD) + pref("security.allow_parent_unrestricted_js_loads", false); +#endif + +// Unload tabs when available memory is running low +pref("browser.tabs.unloadOnLowMemory", false); + +pref("browser.ctrlTab.recentlyUsedOrder", true); + +// By default, do not export HTML at shutdown. +// If true, at shutdown the bookmarks in your menu and toolbar will +// be exported as HTML to the bookmarks.html file. +pref("browser.bookmarks.autoExportHTML", false); + +// The maximum number of daily bookmark backups to +// keep in {PROFILEDIR}/bookmarkbackups. Special values: +// -1: unlimited +// 0: no backups created (and deletes all existing backups) +pref("browser.bookmarks.max_backups", 15); + +// Whether menu should close after Ctrl-click, middle-click, etc. +pref("browser.bookmarks.openInTabClosesMenu", true); + +// Where new bookmarks go by default. +// Use PlacesUIUtils.defaultParentGuid to read this; do NOT read the pref +// directly. +// The pref is ignored if the browser.toolbars.bookmarks.2h2020 pref is false, +// in which case bookmarks always go in the "Other bookmarks" folder. +// The value is one of: +// - a bookmarks guid +// - "toolbar", "menu" or "unfiled" for those folders. +// If we use the pref but the value isn't any of these, we'll fall back to +// the bookmarks toolbar as a default. +pref("browser.bookmarks.defaultLocation", "toolbar"); + +// Scripts & Windows prefs +pref("dom.disable_open_during_load", true); +pref("javascript.options.showInConsole", true); + +// allow JS to move and resize existing windows +pref("dom.disable_window_move_resize", false); +// prevent JS from monkeying with window focus, etc +pref("dom.disable_window_flip", true); + +// popups.policy 1=allow,2=reject +pref("privacy.popups.policy", 1); +pref("privacy.popups.usecustom", true); +pref("privacy.popups.showBrowserMessage", true); + +pref("privacy.item.cookies", false); + +pref("privacy.clearOnShutdown.history", true); +pref("privacy.clearOnShutdown.formdata", true); +pref("privacy.clearOnShutdown.downloads", true); +pref("privacy.clearOnShutdown.cookies", true); +pref("privacy.clearOnShutdown.cache", true); +pref("privacy.clearOnShutdown.sessions", true); +pref("privacy.clearOnShutdown.offlineApps", false); +pref("privacy.clearOnShutdown.siteSettings", false); +pref("privacy.clearOnShutdown.openWindows", false); + +pref("privacy.cpd.history", true); +pref("privacy.cpd.formdata", true); +pref("privacy.cpd.passwords", false); +pref("privacy.cpd.downloads", true); +pref("privacy.cpd.cookies", true); +pref("privacy.cpd.cache", true); +pref("privacy.cpd.sessions", true); +pref("privacy.cpd.offlineApps", false); +pref("privacy.cpd.siteSettings", false); +pref("privacy.cpd.openWindows", false); + +pref("privacy.history.custom", false); + +// What default should we use for the time span in the sanitizer: +// 0 - Clear everything +// 1 - Last Hour +// 2 - Last 2 Hours +// 3 - Last 4 Hours +// 4 - Today +// 5 - Last 5 minutes +// 6 - Last 24 hours +pref("privacy.sanitize.timeSpan", 1); + +pref("privacy.sanitize.migrateFx3Prefs", false); + +pref("privacy.panicButton.enabled", true); + +// Time until temporary permissions expire, in ms +pref("privacy.temporary_permission_expire_time_ms", 3600000); + +pref("network.proxy.share_proxy_settings", false); // use the same proxy settings for all protocols + +// simple gestures support +pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"); +pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"); +pref("browser.gesture.swipe.up", "cmd_scrollTop"); +pref("browser.gesture.swipe.down", "cmd_scrollBottom"); +pref("browser.gesture.pinch.latched", false); +pref("browser.gesture.pinch.threshold", 25); +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // Enabled for touch input display zoom. + pref("browser.gesture.pinch.out", "cmd_fullZoomEnlarge"); + pref("browser.gesture.pinch.in", "cmd_fullZoomReduce"); + pref("browser.gesture.pinch.out.shift", "cmd_fullZoomReset"); + pref("browser.gesture.pinch.in.shift", "cmd_fullZoomReset"); +#else + // Disabled by default due to issues with track pad input. + pref("browser.gesture.pinch.out", ""); + pref("browser.gesture.pinch.in", ""); + pref("browser.gesture.pinch.out.shift", ""); + pref("browser.gesture.pinch.in.shift", ""); +#endif +pref("browser.gesture.twist.latched", false); +pref("browser.gesture.twist.threshold", 0); +pref("browser.gesture.twist.right", "cmd_gestureRotateRight"); +pref("browser.gesture.twist.left", "cmd_gestureRotateLeft"); +pref("browser.gesture.twist.end", "cmd_gestureRotateEnd"); +pref("browser.gesture.tap", "cmd_fullZoomReset"); + +pref("browser.history_swipe_animation.disabled", false); + +// 0: Nothing happens +// 1: Scrolling contents +// 2: Go back or go forward, in your history +// 3: Zoom in or out (reflowing zoom). +// 4: Treat vertical wheel as horizontal scroll +// 5: Zoom in or out (pinch zoom). +#ifdef XP_MACOSX + // On macOS, if the wheel has one axis only, shift+wheel comes through as a + // horizontal scroll event. Thus, we can't assign anything other than normal + // scrolling to shift+wheel. + pref("mousewheel.with_shift.action", 1); + pref("mousewheel.with_alt.action", 2); + // On MacOS X, control+wheel is typically handled by system and we don't + // receive the event. So, command key which is the main modifier key for + // acceleration is the best modifier for zoom-in/out. However, we should keep + // the control key setting for backward compatibility. + pref("mousewheel.with_meta.action", 3); // command key on Mac + // Disable control-/meta-modified horizontal wheel events, since those are + // used on Mac as part of modified swipe gestures (e.g. Left swipe+Cmd is + // "go back" in a new tab). + pref("mousewheel.with_control.action.override_x", 0); + pref("mousewheel.with_meta.action.override_x", 0); +#else + // On the other platforms (non-macOS), user may use legacy mouse which + // supports only vertical wheel but want to scroll horizontally. For such + // users, we should provide horizontal scroll with shift+wheel (same as + // Chrome). However, shift+wheel was used for navigating history. For users + // who want to keep using this feature, let's enable it with alt+wheel. This + // is better for consistency with macOS users. + pref("mousewheel.with_shift.action", 4); + pref("mousewheel.with_alt.action", 2); + pref("mousewheel.with_meta.action", 1); // win key on Win, Super/Hyper on Linux +#endif +pref("mousewheel.with_control.action",3); +pref("mousewheel.with_win.action", 1); + +pref("browser.xul.error_pages.expert_bad_cert", false); +pref("browser.xul.error_pages.show_safe_browsing_details_on_load", false); + +// Enable captive portal detection. +pref("network.captive-portal-service.enabled", true); + +// If true, network link events will change the value of navigator.onLine +pref("network.manage-offline-status", true); + +// We want to make sure mail URLs are handled externally... +pref("network.protocol-handler.external.mailto", true); // for mail +pref("network.protocol-handler.external.news", true); // for news +pref("network.protocol-handler.external.snews", true); // for secure news +pref("network.protocol-handler.external.nntp", true); // also news +#ifdef XP_WIN + pref("network.protocol-handler.external.ms-windows-store", true); +#endif + +// ...without warning dialogs +pref("network.protocol-handler.warn-external.mailto", false); +pref("network.protocol-handler.warn-external.news", false); +pref("network.protocol-handler.warn-external.snews", false); +pref("network.protocol-handler.warn-external.nntp", false); +#ifdef XP_WIN + pref("network.protocol-handler.warn-external.ms-windows-store", false); +#endif + +// By default, all protocol handlers are exposed. This means that +// the browser will respond to openURL commands for all URL types. +// It will also try to open link clicks inside the browser before +// failing over to the system handlers. +pref("network.protocol-handler.expose-all", true); +pref("network.protocol-handler.expose.mailto", false); +pref("network.protocol-handler.expose.news", false); +pref("network.protocol-handler.expose.snews", false); +pref("network.protocol-handler.expose.nntp", false); + +pref("accessibility.typeaheadfind", false); +pref("accessibility.typeaheadfind.timeout", 5000); +pref("accessibility.typeaheadfind.linksonly", false); +pref("accessibility.typeaheadfind.flashBar", 1); + +// Accessibility indicator preferences such as support URL, enabled flag. +pref("accessibility.support.url", "https://support.mozilla.org/%LOCALE%/kb/accessibility-services"); +pref("accessibility.indicator.enabled", false); + +pref("plugins.testmode", false); + +// Should plugins that are hidden show the infobar UI? +pref("plugins.show_infobar", false); + +#if defined(_ARM64_) && defined(XP_WIN) + pref("plugin.default.state", 0); +#else + pref("plugin.default.state", 1); +#endif + +// Enables the download and use of the flash blocklists. +pref("plugins.flashBlock.enabled", true); + +// Prefer HTML5 video over Flash content, and don't +// load plugin instances with no src declared. +// These prefs are documented in details on all.js. +// With the "follow-ctp" setting, this will only +// apply to users that have plugin.state.flash = 1. +pref("plugins.favorfallback.mode", "follow-ctp"); +pref("plugins.favorfallback.rules", "nosrc,video"); + +#ifdef XP_WIN + pref("browser.preferences.instantApply", false); +#else + pref("browser.preferences.instantApply", true); +#endif + +// Toggling Search bar on and off in about:preferences +pref("browser.preferences.search", true); +#if defined(NIGHTLY_BUILD) +pref("browser.preferences.experimental", true); +#else +pref("browser.preferences.experimental", false); +#endif +pref("browser.preferences.experimental.hidden", false); +pref("browser.preferences.defaultPerformanceSettings.enabled", true); + +pref("browser.preferences.exposeHTTPSOnly", true); + +pref("browser.proton.enabled", false); + +// Backspace and Shift+Backspace behavior +// 0 goes Back/Forward +// 1 act like PgUp/PgDown +// 2 and other values, nothing +#ifdef UNIX_BUT_NOT_MAC + pref("browser.backspace_action", 2); +#else + pref("browser.backspace_action", 0); +#endif + +pref("intl.regional_prefs.use_os_locales", false); + +// this will automatically enable inline spellchecking (if it is available) for +// editable elements in HTML +// 0 = spellcheck nothing +// 1 = check multi-line controls [default] +// 2 = check multi/single line controls +pref("layout.spellcheckDefault", 1); + +pref("browser.send_pings", false); + +// At startup, if the handler service notices that the version number in the +// region.properties file is newer than the version number in the handler +// service datastore, it will add any new handlers it finds in the prefs (as +// seeded by this file) to its datastore. +pref("gecko.handlerService.defaultHandlersVersion", "chrome://browser-region/locale/region.properties"); + +// The default set of web-based protocol handlers shown in the application +// selection dialog for webcal: ; I've arbitrarily picked 4 default handlers +// per protocol, but if some locale wants more than that (or defaults for some +// protocol not currently listed here), we should go ahead and add those. + +// webcal +pref("gecko.handlerService.schemes.webcal.0.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.webcal.0.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.webcal.1.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.webcal.1.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.webcal.2.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.webcal.2.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.webcal.3.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.webcal.3.uriTemplate", "chrome://browser-region/locale/region.properties"); + +// mailto +pref("gecko.handlerService.schemes.mailto.0.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.mailto.0.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.mailto.1.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.mailto.1.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.mailto.2.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.mailto.2.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.mailto.3.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.mailto.3.uriTemplate", "chrome://browser-region/locale/region.properties"); + +// irc +pref("gecko.handlerService.schemes.irc.0.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.irc.0.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.irc.1.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.irc.1.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.irc.2.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.irc.2.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.irc.3.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.irc.3.uriTemplate", "chrome://browser-region/locale/region.properties"); + +// ircs +pref("gecko.handlerService.schemes.ircs.0.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.ircs.0.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.ircs.1.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.ircs.1.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.ircs.2.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.ircs.2.uriTemplate", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.ircs.3.name", "chrome://browser-region/locale/region.properties"); +pref("gecko.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-region/locale/region.properties"); + +pref("browser.geolocation.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/geolocation/"); +pref("browser.xr.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/xr/"); + +pref("browser.sessionstore.resume_from_crash", true); +pref("browser.sessionstore.resume_session_once", false); +pref("browser.sessionstore.resuming_after_os_restart", false); + +// Minimal interval between two save operations in milliseconds (while the user is active). +pref("browser.sessionstore.interval", 15000); // 15 seconds + +// Minimal interval between two save operations in milliseconds (while the user is idle). +pref("browser.sessionstore.interval.idle", 3600000); // 1h + +// Time (ms) before we assume that the user is idle and that we don't need to +// collect/save the session quite as often. +pref("browser.sessionstore.idleDelay", 180000); // 3 minutes + +// on which sites to save text data, POSTDATA and cookies +// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere +pref("browser.sessionstore.privacy_level", 0); +// how many tabs can be reopened (per window) +pref("browser.sessionstore.max_tabs_undo", 25); +// how many windows can be reopened (per session) - on non-OS X platforms this +// pref may be ignored when dealing with pop-up windows to ensure proper startup +pref("browser.sessionstore.max_windows_undo", 3); +// number of crashes that can occur before the about:sessionrestore page is displayed +// (this pref has no effect if more than 6 hours have passed since the last crash) +pref("browser.sessionstore.max_resumed_crashes", 1); +// number of back button session history entries to restore (-1 = all of them) +pref("browser.sessionstore.max_serialize_back", 10); +// number of forward button session history entries to restore (-1 = all of them) +pref("browser.sessionstore.max_serialize_forward", -1); +// restore_on_demand overrides MAX_CONCURRENT_TAB_RESTORES (sessionstore constant) +// and restore_hidden_tabs. When true, tabs will not be restored until they are +// focused (also applies to tabs that aren't visible). When false, the values +// for MAX_CONCURRENT_TAB_RESTORES and restore_hidden_tabs are respected. +// Selected tabs are always restored regardless of this pref. +pref("browser.sessionstore.restore_on_demand", true); +// Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not +pref("browser.sessionstore.restore_hidden_tabs", false); +// If restore_on_demand is set, pinned tabs are restored on startup by default. +// When set to true, this pref overrides that behavior, and pinned tabs will only +// be restored when they are focused. +pref("browser.sessionstore.restore_pinned_tabs_on_demand", false); +// The version at which we performed the latest upgrade backup +pref("browser.sessionstore.upgradeBackup.latestBuildID", ""); +// How many upgrade backups should be kept +pref("browser.sessionstore.upgradeBackup.maxUpgradeBackups", 3); +// End-users should not run sessionstore in debug mode +pref("browser.sessionstore.debug", false); +// Causes SessionStore to ignore non-final update messages from +// browser tabs that were not caused by a flush from the parent. +// This is a testing flag and should not be used by end-users. +pref("browser.sessionstore.debug.no_auto_updates", false); +// Forget closed windows/tabs after two weeks +pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000); +// Amount of failed SessionFile writes until we restart the worker. +pref("browser.sessionstore.max_write_failures", 5); + +// Whether to warn the user when quitting, even though their tabs will be restored. +pref("browser.sessionstore.warnOnQuit", false); + +// allow META refresh by default +pref("accessibility.blockautorefresh", false); + +// Whether history is enabled or not. +pref("places.history.enabled", true); + +// Whether or not diacritics must match in history text searches. +pref("places.search.matchDiacritics", false); + +// the (maximum) number of the recent visits to sample +// when calculating frecency +pref("places.frecency.numVisits", 10); + +// buckets (in days) for frecency calculation +pref("places.frecency.firstBucketCutoff", 4); +pref("places.frecency.secondBucketCutoff", 14); +pref("places.frecency.thirdBucketCutoff", 31); +pref("places.frecency.fourthBucketCutoff", 90); + +// weights for buckets for frecency calculations +pref("places.frecency.firstBucketWeight", 100); +pref("places.frecency.secondBucketWeight", 70); +pref("places.frecency.thirdBucketWeight", 50); +pref("places.frecency.fourthBucketWeight", 30); +pref("places.frecency.defaultBucketWeight", 10); + +// bonus (in percent) for visit transition types for frecency calculations +pref("places.frecency.embedVisitBonus", 0); +pref("places.frecency.framedLinkVisitBonus", 0); +pref("places.frecency.linkVisitBonus", 100); +pref("places.frecency.typedVisitBonus", 2000); +// The bookmarks bonus is always added on top of any other bonus, including +// the redirect source and the typed ones. +pref("places.frecency.bookmarkVisitBonus", 75); +// The redirect source bonus overwrites any transition bonus. +// 0 would hide these pages, instead we want them low ranked. Thus we use +// linkVisitBonus - bookmarkVisitBonus, so that a bookmarked source is in par +// with a common link. +pref("places.frecency.redirectSourceVisitBonus", 25); +pref("places.frecency.downloadVisitBonus", 0); +// The perm/temp redirects here relate to redirect targets, not sources. +pref("places.frecency.permRedirectVisitBonus", 50); +pref("places.frecency.tempRedirectVisitBonus", 40); +pref("places.frecency.reloadVisitBonus", 0); +pref("places.frecency.defaultVisitBonus", 0); + +// bonus (in percent) for place types for frecency calculations +pref("places.frecency.unvisitedBookmarkBonus", 140); +pref("places.frecency.unvisitedTypedBonus", 200); + +// Controls behavior of the "Add Exception" dialog launched from SSL error pages +// 0 - don't pre-populate anything +// 1 - pre-populate site URL, but don't fetch certificate +// 2 - pre-populate site URL and pre-fetch certificate +pref("browser.ssl_override_behavior", 2); + +// if true, use full page zoom instead of text zoom +pref("browser.zoom.full", true); + +// Whether or not to save and restore zoom levels on a per-site basis. +pref("browser.zoom.siteSpecific", true); + +// Whether or not to update background tabs to the current zoom level. +pref("browser.zoom.updateBackgroundTabs", true); + +// The breakpad report server to link to in about:crashes +pref("breakpad.reportURL", "https://crash-stats.mozilla.org/report/index/"); + +// URL for "Learn More" for DataCollection +pref("toolkit.datacollection.infoURL", + "https://www.mozilla.org/legal/privacy/firefox.html"); + +// URL for "Learn More" for Crash Reporter +pref("toolkit.crashreporter.infoURL", + "https://www.mozilla.org/legal/privacy/firefox.html#crash-reporter"); + +// base URL for web-based support pages +pref("app.support.baseURL", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/"); + +// base url for web-based feedback pages +#ifdef MOZ_DEV_EDITION + pref("app.feedback.baseURL", "https://input.mozilla.org/%LOCALE%/feedback/firefoxdev/%VERSION%/"); +#else + pref("app.feedback.baseURL", "https://input.mozilla.org/%LOCALE%/feedback/%APP%/%VERSION%/"); +#endif + +// Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror) +pref("security.alternate_certificate_error_page", "certerror"); + +pref("security.certerrors.recordEventTelemetry", true); +pref("security.certerrors.permanentOverride", true); +pref("security.certerrors.mitm.priming.enabled", true); +pref("security.certerrors.mitm.priming.endpoint", "https://mitmdetection.services.mozilla.com/"); +pref("security.certerrors.mitm.auto_enable_enterprise_roots", true); + +// Whether the bookmark panel should be shown when bookmarking a page. +pref("browser.bookmarks.editDialog.showForNewBookmarks", true); + +// Don't try to alter this pref, it'll be reset the next time you use the +// bookmarking dialog +pref("browser.bookmarks.editDialog.firstEditField", "namePicker"); + +// The number of recently selected folders in the edit bookmarks dialog. +pref("browser.bookmarks.editDialog.maxRecentFolders", 7); + +pref("dom.ipc.plugins.flash.disable-protected-mode", false); + +// Feature-disable the protected-mode auto-flip +pref("browser.flash-protected-mode-flip.enable", false); + +// Whether we've already flipped protected mode automatically +pref("browser.flash-protected-mode-flip.done", false); + +pref("dom.ipc.shims.enabledWarnings", false); + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + // Controls whether and how the Windows NPAPI plugin process is sandboxed. + // To get a different setting for a particular plugin replace "default", with + // the plugin's nice file name, see: nsPluginTag::GetNiceFileName. + // On windows these levels are: + // 0 - no sandbox + // 1 - sandbox with USER_NON_ADMIN access token level + // 2 - a more strict sandbox, which might cause functionality issues. This now + // includes running at low integrity. + // 3 - the strongest settings we seem to be able to use without breaking + // everything, but will probably cause some functionality restrictions + pref("dom.ipc.plugins.sandbox-level.default", 0); + #if defined(_AMD64_) + // The base sandbox level in nsPluginTag::InitSandboxLevel must be + // updated to keep in sync with this value. + pref("dom.ipc.plugins.sandbox-level.flash", 3); + #else + pref("dom.ipc.plugins.sandbox-level.flash", 0); + #endif + + // This controls the strength of the Windows content process sandbox for + // testing purposes. This will require a restart. + // On windows these levels are: + // See - security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp + // SetSecurityLevelForContentProcess() for what the different settings mean. + pref("security.sandbox.content.level", 6); + + // This controls the strength of the Windows GPU process sandbox. Changes + // will require restart. + // For information on what the level number means, see + // SetSecurityLevelForGPUProcess() in + // security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp + pref("security.sandbox.gpu.level", 0); +#endif + +#if defined(XP_MACOSX) && defined(MOZ_SANDBOX) + // This pref is discussed in bug 1083344, the naming is inspired from its + // Windows counterpart, but on Mac it's an integer which means: + // 0 -> "no sandbox" (nightly only) + // 1 -> "preliminary content sandboxing enabled: write access to + // home directory is prevented" + // 2 -> "preliminary content sandboxing enabled with profile protection: + // write access to home directory is prevented, read and write access + // to ~/Library and profile directories are prevented (excluding + // $PROFILE/{extensions,chrome})" + // 3 -> "no global read/write access, read access permitted to + // $PROFILE/{extensions,chrome}" + // This setting is read when the content process is started. On Mac the + // content process is killed when all windows are closed, so a change will + // take effect when the 1st window is opened. + pref("security.sandbox.content.level", 3); + + // Prefs for controlling whether and how the Mac NPAPI Flash plugin process is + // sandboxed. On Mac these levels are: + // 0 - "no sandbox" + // 1 - "global read access, limited write access for Flash functionality" + // 2 - "read access triggered by file dialog activity, limited read/write" + // "access for Flash functionality" + // 3 - "limited read/write access for Flash functionality" + pref("dom.ipc.plugins.sandbox-level.flash", 1); + // Controls the level used on older OS X versions. Is overriden when the + // "dom.ipc.plugins.sandbox-level.flash" is set to 0. + pref("dom.ipc.plugins.sandbox-level.flash.legacy", 1); + // The max OS minor version where we use the above legacy sandbox level. + pref("dom.ipc.plugins.sandbox-level.flash.max-legacy-os-minor", 10); + // Controls the sandbox level used by plugins other than Flash. On Mac, + // no other plugins are supported and this pref is only used for test + // plugins used in automated tests. + pref("dom.ipc.plugins.sandbox-level.default", 1); +#endif + +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + // This pref is introduced as part of bug 742434, the naming is inspired from + // its Windows/Mac counterpart, but on Linux it's an integer which means: + // 0 -> "no sandbox" + // 1 -> "content sandbox using seccomp-bpf when available" + ipc restrictions + // 2 -> "seccomp-bpf + write file broker" + // 3 -> "seccomp-bpf + read/write file brokering" + // 4 -> all of the above + network/socket restrictions + chroot + // + // The purpose of this setting is to allow Linux users or distros to disable + // the sandbox while we fix their problems, or to allow running Firefox with + // exotic configurations we can't reasonably support out of the box. + // + pref("security.sandbox.content.level", 4); + // Introduced as part of bug 1608558. Linux is currently the only platform + // that uses a sandbox level for the socket process. There are currently + // only 2 levels: + // 0 -> "no sandbox" + // 1 -> "sandboxed, allows socket operations and reading necessary certs" + pref("security.sandbox.socket.process.level", 1); + pref("security.sandbox.content.write_path_whitelist", ""); + pref("security.sandbox.content.read_path_whitelist", ""); + pref("security.sandbox.content.syscall_whitelist", ""); +#endif + +#if defined(XP_OPENBSD) && defined(MOZ_SANDBOX) + pref("security.sandbox.content.level", 1); +#endif + +#if defined(MOZ_SANDBOX) + // ID (a UUID when set by gecko) that is used to form the name of a + // sandbox-writable temporary directory to be used by content processes + // when a temporary writable file is required in a level 1 sandbox. + pref("security.sandbox.content.tempDirSuffix", ""); + pref("security.sandbox.plugin.tempDirSuffix", ""); + + // This pref determines if messages relevant to sandbox violations are + // logged. + #if defined(XP_WIN) || defined(XP_MACOSX) + pref("security.sandbox.logging.enabled", false); + #endif +#endif + +// This pref governs whether we attempt to work around problems caused by +// plugins using OS calls to manipulate the cursor while running out-of- +// process. These workarounds all involve intercepting (hooking) certain +// OS calls in the plugin process, then arranging to make certain OS calls +// in the browser process. Eventually plugins will be required to use the +// NPAPI to manipulate the cursor, and these workarounds will be removed. +// See bug 621117. +#ifdef XP_MACOSX + pref("dom.ipc.plugins.nativeCursorSupport", true); +#endif + +#ifdef XP_WIN + pref("browser.taskbar.previews.enable", false); + pref("browser.taskbar.previews.max", 20); + pref("browser.taskbar.previews.cachetime", 5); + pref("browser.taskbar.lists.enabled", true); + pref("browser.taskbar.lists.frequent.enabled", true); + pref("browser.taskbar.lists.recent.enabled", false); + pref("browser.taskbar.lists.maxListItemCount", 7); + pref("browser.taskbar.lists.tasks.enabled", true); + pref("browser.taskbar.lists.refreshInSeconds", 120); +#endif + +// Preferences to be synced by default +pref("services.sync.prefs.sync.accessibility.blockautorefresh", true); +pref("services.sync.prefs.sync.accessibility.browsewithcaret", true); +pref("services.sync.prefs.sync.accessibility.typeaheadfind", true); +pref("services.sync.prefs.sync.accessibility.typeaheadfind.linksonly", true); +pref("services.sync.prefs.sync.addons.ignoreUserEnabledChanges", true); +pref("services.sync.prefs.sync.app.shield.optoutstudies.enabled", true); +// The addons prefs related to repository verification are intentionally +// not synced for security reasons. If a system is compromised, a user +// could weaken the pref locally, install an add-on from an untrusted +// source, and this would propagate automatically to other, +// uncompromised Sync-connected devices. +pref("services.sync.prefs.sync.browser.contentblocking.category", true); +pref("services.sync.prefs.sync.browser.contentblocking.features.strict", true); +pref("services.sync.prefs.sync.browser.crashReports.unsubmittedCheck.autoSubmit2", true); +pref("services.sync.prefs.sync.browser.ctrlTab.recentlyUsedOrder", true); +pref("services.sync.prefs.sync.browser.discovery.enabled", true); +pref("services.sync.prefs.sync.browser.download.useDownloadDir", true); +pref("services.sync.prefs.sync.browser.formfill.enable", true); +pref("services.sync.prefs.sync.browser.link.open_newwindow", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.showSearch", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.showSponsored", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.showSponsoredTopSites", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.topsites", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.topSitesRows", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.snippets", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.section.topstories", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.topstories.rows", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.section.highlights", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeVisited", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeBookmarks", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeDownloads", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includePocket", true); +pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.rows", true); +pref("services.sync.prefs.sync.browser.newtabpage.enabled", true); +pref("services.sync.prefs.sync.browser.newtabpage.pinned", true); +pref("services.sync.prefs.sync.browser.offline-apps.notify", true); +pref("services.sync.prefs.sync.browser.safebrowsing.downloads.enabled", true); +pref("services.sync.prefs.sync.browser.safebrowsing.downloads.remote.block_potentially_unwanted", true); +pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true); +pref("services.sync.prefs.sync.browser.safebrowsing.phishing.enabled", true); +pref("services.sync.prefs.sync.browser.search.update", true); +pref("services.sync.prefs.sync.browser.search.widget.inNavBar", true); +pref("services.sync.prefs.sync.browser.sessionstore.warnOnQuit", true); +pref("services.sync.prefs.sync.browser.startup.homepage", true); +pref("services.sync.prefs.sync.browser.startup.page", true); +pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true); +pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true); +pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true); +pref("services.sync.prefs.sync.browser.taskbar.previews.enable", true); +pref("services.sync.prefs.sync.browser.urlbar.matchBuckets", true); +pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true); +pref("services.sync.prefs.sync.browser.urlbar.suggest.bookmark", true); +pref("services.sync.prefs.sync.browser.urlbar.suggest.history", true); +pref("services.sync.prefs.sync.browser.urlbar.suggest.openpage", true); +pref("services.sync.prefs.sync.browser.urlbar.suggest.searches", true); +pref("services.sync.prefs.sync.browser.urlbar.suggest.topsites", true); +pref("services.sync.prefs.sync.browser.urlbar.suggest.engines", true); +pref("services.sync.prefs.sync.dom.disable_open_during_load", true); +pref("services.sync.prefs.sync.dom.disable_window_flip", true); +pref("services.sync.prefs.sync.dom.disable_window_move_resize", true); +pref("services.sync.prefs.sync.dom.event.contextmenu.enabled", true); +pref("services.sync.prefs.sync.dom.security.https_only_mode", true); +pref("services.sync.prefs.sync.dom.security.https_only_mode_ever_enabled", true); +pref("services.sync.prefs.sync.dom.security.https_only_mode_ever_enabled_pbm", true); +pref("services.sync.prefs.sync.dom.security.https_only_mode_pbm", true); +pref("services.sync.prefs.sync.extensions.update.enabled", true); +pref("services.sync.prefs.sync.extensions.activeThemeID", true); +pref("services.sync.prefs.sync.general.autoScroll", true); +pref("services.sync.prefs.sync.general.smoothScroll", true); +pref("services.sync.prefs.sync.intl.accept_languages", true); +pref("services.sync.prefs.sync.intl.regional_prefs.use_os_locales", true); +pref("services.sync.prefs.sync.layout.spellcheckDefault", true); +pref("services.sync.prefs.sync.media.autoplay.default", true); +pref("services.sync.prefs.sync.media.eme.enabled", true); +pref("services.sync.prefs.sync.media.videocontrols.picture-in-picture.video-toggle.enabled", true); +pref("services.sync.prefs.sync.network.cookie.cookieBehavior", true); +pref("services.sync.prefs.sync.network.cookie.lifetimePolicy", true); +pref("services.sync.prefs.sync.network.cookie.thirdparty.sessionOnly", true); +pref("services.sync.prefs.sync.permissions.default.image", true); +pref("services.sync.prefs.sync.pref.downloads.disable_button.edit_actions", true); +pref("services.sync.prefs.sync.pref.privacy.disable_button.cookie_exceptions", true); +pref("services.sync.prefs.sync.privacy.clearOnShutdown.cache", true); +pref("services.sync.prefs.sync.privacy.clearOnShutdown.cookies", true); +pref("services.sync.prefs.sync.privacy.clearOnShutdown.downloads", true); +pref("services.sync.prefs.sync.privacy.clearOnShutdown.formdata", true); +pref("services.sync.prefs.sync.privacy.clearOnShutdown.history", true); +pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true); +pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true); +pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true); +pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true); +pref("services.sync.prefs.sync.privacy.fuzzyfox.enabled", false); +pref("services.sync.prefs.sync.privacy.fuzzyfox.clockgrainus", false); +pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true); +pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true); +pref("services.sync.prefs.sync.privacy.trackingprotection.cryptomining.enabled", true); +pref("services.sync.prefs.sync.privacy.trackingprotection.fingerprinting.enabled", true); +pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true); +pref("services.sync.prefs.sync.privacy.resistFingerprinting", true); +pref("services.sync.prefs.sync.privacy.reduceTimerPrecision", true); +pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.microseconds", true); +pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.jitter", true); +pref("services.sync.prefs.sync.privacy.userContext.enabled", true); +pref("services.sync.prefs.sync.privacy.userContext.newTabContainerOnLeftClick.enabled", true); +pref("services.sync.prefs.sync.security.default_personal_cert", true); +pref("services.sync.prefs.sync.services.sync.syncedTabs.showRemoteIcons", true); +pref("services.sync.prefs.sync.signon.autofillForms", true); +pref("services.sync.prefs.sync.signon.generation.enabled", true); +pref("services.sync.prefs.sync.signon.management.page.breach-alerts.enabled", true); +pref("services.sync.prefs.sync.signon.rememberSignons", true); +pref("services.sync.prefs.sync.spellchecker.dictionary", true); +pref("services.sync.prefs.sync.ui.osk.enabled", true); + +// A preference which, if false, means sync will only apply incoming preference +// changes if there's already a local services.sync.prefs.sync.* control pref. +// If true, all incoming preferences will be applied and the local "control +// pref" updated accordingly. +pref("services.sync.prefs.dangerously_allow_arbitrary", false); + +// A preference that controls whether we should show the icon for a remote tab. +// This pref has no UI but exists because some people may be concerned that +// fetching these icons to show remote tabs may leak information about that +// user's tabs and bookmarks. Note this pref is also synced. +pref("services.sync.syncedTabs.showRemoteIcons", true); + +// Whether the character encoding menu is under the main Firefox button. This +// preference is a string so that localizers can alter it. +pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties"); + +// Whether prompts should be content modal (1) tab modal (2) or window modal(3) by default +// This is a fallback value for when prompt callers do not specify a modalType. +pref("prompts.defaultModalType", 3); + +pref("browser.topsites.useRemoteSetting", false); + +// The base URL for the Quick Suggest anonymizing proxy. To make a request to +// the proxy, include a campaign ID in the path. +pref("browser.partnerlink.attributionURL", "https://topsites.services.mozilla.com/cid/"); +pref("browser.partnerlink.campaign.topsites", "amzn_2020_a1"); + +// Whether to show tab level system prompts opened via nsIPrompt(Service) as +// SubDialogs in the TabDialogBox (true) or as TabModalPrompt in the +// TabModalPromptBox (false). +pref("prompts.tabChromePromptSubDialog", true); + +// Whether to show the dialogs opened at the content level, such as +// alert() or prompt(), using a SubDialogManager in the TabDialogBox. +#ifdef EARLY_BETA_OR_EARLIER + pref("prompts.contentPromptSubDialog", true); +#else + pref("prompts.contentPromptSubDialog", false); +#endif + +// Activates preloading of the new tab url. +pref("browser.newtab.preload", true); + +// Preference to enable the entire new newtab experience at once. +pref("browser.newtabpage.activity-stream.newNewtabExperience.enabled", false); + +// A preference which allows us to enable the fly out customization overlay +// on the newtab page. +pref("browser.newtabpage.activity-stream.customizationMenu.enabled", false); +pref("browser.newtabpage.activity-stream.newNewtabExperience.colors", "#0090ED,#FF4F5F,#2AC3A2,#FF7139,#A172FF,#FFA437,#FF2A8A"); + +// Activity Stream prefs that control to which page to redirect +#ifndef RELEASE_OR_BETA + pref("browser.newtabpage.activity-stream.debug", false); +#endif + +pref("browser.library.activity-stream.enabled", true); + +// The remote FxA root content URL for the Activity Stream firstrun page. +pref("browser.newtabpage.activity-stream.fxaccounts.endpoint", "https://accounts.firefox.com/"); + +// The pref that controls if the search shortcuts experiment is on +pref("browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts", true); + +// ASRouter provider configuration +pref("browser.newtabpage.activity-stream.asrouter.providers.cfr", "{\"id\":\"cfr\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"cfr\",\"updateCycleInMs\":3600000}"); +pref("browser.newtabpage.activity-stream.asrouter.providers.whats-new-panel", "{\"id\":\"whats-new-panel\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"whats-new-panel\",\"updateCycleInMs\":3600000}"); +pref("browser.newtabpage.activity-stream.asrouter.providers.message-groups", "{\"id\":\"message-groups\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"message-groups\",\"updateCycleInMs\":3600000}"); +// This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into +// this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream +// repackager of this code using an alternate snippet url, please keep your users safe +pref("browser.newtabpage.activity-stream.asrouter.providers.snippets", "{\"id\":\"snippets\",\"enabled\":true,\"type\":\"remote\",\"url\":\"https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/\",\"updateCycleInMs\":14400000}"); +pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "{\"id\":\"messaging-experiments\",\"enabled\":true,\"type\":\"remote-experiments\",\"messageGroups\":[\"cfr\",\"whats-new-panel\",\"moments-page\",\"snippets\",\"cfr-fxa\",\"aboutwelcome\",\"infobar\"],\"updateCycleInMs\":3600000}"); + +// ASRouter user prefs +pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", true); +pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", true); + +// The pref that controls if ASRouter uses the remote fluent files. +// It's enabled by default, but could be disabled to force ASRouter to use the local files. +pref("browser.newtabpage.activity-stream.asrouter.useRemoteL10n", true); + +// These prefs control if Discovery Stream is enabled. +pref("browser.newtabpage.activity-stream.discoverystream.enabled", true); +pref("browser.newtabpage.activity-stream.discoverystream.hardcoded-basic-layout", false); +pref("browser.newtabpage.activity-stream.discoverystream.spocs-endpoint", ""); +pref("browser.newtabpage.activity-stream.discoverystream.spocs-endpoint-query", ""); + +// List of regions that do not get stories, regardless of locale-list-config. +pref("browser.newtabpage.activity-stream.discoverystream.region-stories-block", "FR"); +// List of locales that get stories, regardless of region-stories-config. +#ifdef NIGHTLY_BUILD + pref("browser.newtabpage.activity-stream.discoverystream.locale-list-config", "en-US,en-CA,en-GB"); +#else + pref("browser.newtabpage.activity-stream.discoverystream.locale-list-config", ""); +#endif +// List of regions that get stories by default. +pref("browser.newtabpage.activity-stream.discoverystream.region-stories-config", "US,DE,CA,GB,IE,CH,AT,BE,IN"); + +// List of regions that get spocs by default. +pref("browser.newtabpage.activity-stream.discoverystream.region-spocs-config", "US,CA,DE,GB"); +// List of regions that don't get the 7 row layout. +pref("browser.newtabpage.activity-stream.discoverystream.region-basic-config", ""); + +// Allows Pocket story collections to be dismissed. +pref("browser.newtabpage.activity-stream.discoverystream.isCollectionDismissible", true); +pref("browser.newtabpage.activity-stream.discoverystream.personalization.version", 2); +// Configurable keys used by personalization version 2. +pref("browser.newtabpage.activity-stream.discoverystream.personalization.modelKeys", "nb_model_arts_and_entertainment, nb_model_autos_and_vehicles, nb_model_beauty_and_fitness, nb_model_blogging_resources_and_services, nb_model_books_and_literature, nb_model_business_and_industrial, nb_model_computers_and_electronics, nb_model_finance, nb_model_food_and_drink, nb_model_games, nb_model_health, nb_model_hobbies_and_leisure, nb_model_home_and_garden, nb_model_internet_and_telecom, nb_model_jobs_and_education, nb_model_law_and_government, nb_model_online_communities, nb_model_people_and_society, nb_model_pets_and_animals, nb_model_real_estate, nb_model_reference, nb_model_science, nb_model_shopping, nb_model_sports, nb_model_travel"); +// System pref to allow Pocket stories personalization to be turned on/off. +pref("browser.newtabpage.activity-stream.discoverystream.recs.personalized", false); +// System pref to allow Pocket sponsored content personalization to be turned on/off. +pref("browser.newtabpage.activity-stream.discoverystream.spocs.personalized", true); + +// User pref to show stories on newtab (feeds.system.topstories has to be set to true as well) +pref("browser.newtabpage.activity-stream.feeds.section.topstories", true); + +// The pref controls if search hand-off is enabled for Activity Stream. +#ifdef NIGHTLY_BUILD + pref("browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", true); +#else + pref("browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", false); +#endif + +pref("browser.newtabpage.activity-stream.logowordmark.alwaysVisible", false); + +// Used to display triplet cards on newtab +pref("trailhead.firstrun.newtab.triplets", ""); +// Separate about welcome +pref("browser.aboutwelcome.enabled", true); +// Used to set multistage welcome UX +pref("browser.aboutwelcome.overrideContent", ""); + +// The pref that controls if the What's New panel is enabled. +pref("browser.messaging-system.whatsNewPanel.enabled", true); +// Used for CFR messages with scores. See Bug 1594422. +pref("browser.messaging-system.personalized-cfr.scores", "{}"); +pref("browser.messaging-system.personalized-cfr.score-threshold", 5000); + +// Experiment Manager +// See Console.jsm LOG_LEVELS for all possible values +pref("messaging-system.log", "warn"); +pref("messaging-system.rsexperimentloader.enabled", true); +pref("messaging-system.rsexperimentloader.collection_id", "nimbus-desktop-experiments"); + +// Enable the DOM fullscreen API. +pref("full-screen-api.enabled", true); + +// Startup Crash Tracking +// number of startup crashes that can occur before starting into safe mode automatically +// (this pref has no effect if more than 6 hours have passed since the last crash) +pref("toolkit.startup.max_resumed_crashes", 3); + +// Whether to use RegisterApplicationRestart to restart the browser and resume +// the session on next Windows startup +#if defined(XP_WIN) + pref("toolkit.winRegisterApplicationRestart", true); +#endif + +// Completely disable pdf.js as an option to preview pdfs within firefox. +// Note: if this is not disabled it does not necessarily mean pdf.js is the pdf +// handler just that it is an option. +pref("pdfjs.disabled", false); +// Used by pdf.js to know the first time firefox is run with it installed so it +// can become the default pdf viewer. +pref("pdfjs.firstRun", true); +// The values of preferredAction and alwaysAskBeforeHandling before pdf.js +// became the default. +pref("pdfjs.previousHandler.preferredAction", 0); +pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false); + +// Try to convert PDFs sent as octet-stream +pref("pdfjs.handleOctetStream", true); + +// Is the sidebar positioned ahead of the content browser +pref("sidebar.position_start", true); + +pref("security.identitypopup.recordEventTelemetry", true); +pref("security.protectionspopup.recordEventTelemetry", true); +pref("security.app_menu.recordEventTelemetry", true); + +// Block insecure active content on https pages +pref("security.mixed_content.block_active_content", true); + +// Show in-content login form warning UI for insecure login fields +pref("security.insecure_field_warning.contextual.enabled", true); + +// Show degraded UI for http pages. +pref("security.insecure_connection_icon.enabled", true); +// Show degraded UI for http pages in private mode. +pref("security.insecure_connection_icon.pbmode.enabled", true); + +// For secure connections, show gray instead of green lock icon +pref("security.secure_connection_icon_color_gray", true); + +// Show "Not Secure" text for http pages; disabled for now +pref("security.insecure_connection_text.enabled", false); +pref("security.insecure_connection_text.pbmode.enabled", false); + +// 1 = allow MITM for certificate pinning checks. +pref("security.cert_pinning.enforcement_level", 1); + + +// If this turns true, Moz*Gesture events are not called stopPropagation() +// before content. +pref("dom.debug.propagate_gesture_events_through_content", false); + +// CustomizableUI debug logging. +pref("browser.uiCustomization.debug", false); + +// CustomizableUI state of the browser's user interface +pref("browser.uiCustomization.state", ""); + +// If set to false, FxAccounts and Sync will be unavailable. +// A restart is mandatory after flipping that preference. +pref("identity.fxaccounts.enabled", true); + +// The remote FxA root content URL. Must use HTTPS. +pref("identity.fxaccounts.remote.root", "https://accounts.firefox.com/"); + +// The value of the context query parameter passed in fxa requests. +pref("identity.fxaccounts.contextParam", "fx_desktop_v3"); + +// The remote URL of the FxA Profile Server +pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1"); + +// The remote URL of the FxA OAuth Server +pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1"); + +// Whether FxA pairing using QR codes is enabled. +pref("identity.fxaccounts.pairing.enabled", true); + +// The remote URI of the FxA pairing server +pref("identity.fxaccounts.remote.pairing.uri", "wss://channelserver.services.mozilla.com"); + +// Token server used by the FxA Sync identity. +pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5"); + +// Fetch Sync tokens using the OAuth token function +pref("identity.sync.useOAuthForSyncToken", true); + +// Using session tokens to fetch OAuth tokens +pref("identity.fxaccounts.useSessionTokensForOAuth", true); + +// Auto-config URL for FxA self-hosters, makes an HTTP request to +// [identity.fxaccounts.autoconfig.uri]/.well-known/fxa-client-configuration +// This is now the prefered way of pointing to a custom FxA server, instead +// of making changes to "identity.fxaccounts.*.uri". +pref("identity.fxaccounts.autoconfig.uri", ""); + +// URL for help link about Send Tab. +pref("identity.sendtabpromo.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/send-tab"); + +// URLs for promo links to mobile browsers. Note that consumers are expected to +// append a value for utm_campaign. +pref("identity.mobilepromo.android", "https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign="); +pref("identity.mobilepromo.ios", "https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign="); + +// Migrate any existing Firefox Account data from the default profile to the +// Developer Edition profile. +#ifdef MOZ_DEV_EDITION + pref("identity.fxaccounts.migrateToDevEdition", true); +#else + pref("identity.fxaccounts.migrateToDevEdition", false); +#endif + +// If activated, send tab will use the new FxA commands backend. +pref("identity.fxaccounts.commands.enabled", true); +// How often should we try to fetch missed FxA commands on sync (in seconds). +// Default is 24 hours. +pref("identity.fxaccounts.commands.missed.fetch_interval", 86400); + +// Whether we should run a test-pattern through EME GMPs before assuming they'll +// decode H.264. +pref("media.gmp.trial-create.enabled", true); + +// Note: when media.gmp-*.visible is true, provided we're running on a +// supported platform/OS version, the corresponding CDM appears in the +// plugins list, Firefox will download the GMP/CDM if enabled, and our +// UI to re-enable EME prompts the user to re-enable EME if it's disabled +// and script requests EME. If *.visible is false, we won't show the UI +// to enable the CDM if its disabled; it's as if the keysystem is completely +// unsupported. + +#ifdef MOZ_WIDEVINE_EME + pref("media.gmp-widevinecdm.visible", true); + pref("media.gmp-widevinecdm.enabled", true); +#endif + +pref("media.gmp-gmpopenh264.visible", true); +pref("media.gmp-gmpopenh264.enabled", true); + +// Set Firefox to block autoplay, asking for permission by default. +pref("media.autoplay.default", 1); // 0=Allowed, 1=Blocked, 5=All Blocked + +#ifdef NIGHTLY_BUILD + // Block WebAudio from playing automatically. + pref("media.autoplay.block-webaudio", true); +#else + pref("media.autoplay.block-webaudio", false); +#endif + +pref("media.videocontrols.picture-in-picture.enabled", true); +pref("media.videocontrols.picture-in-picture.video-toggle.enabled", true); +pref("media.videocontrols.picture-in-picture.keyboard-controls.enabled", true); + +#ifdef NIGHTLY_BUILD + // Show the audio toggle for Picture-in-Picture. + pref("media.videocontrols.picture-in-picture.audio-toggle.enabled", true); + // Enable keyboard controls for Picture-in-Picture. +#else + pref("media.videocontrols.picture-in-picture.audio-toggle.enabled", false); +#endif + +pref("browser.translation.detectLanguage", false); +pref("browser.translation.neverForLanguages", ""); +// Show the translation UI bits, like the info bar, notification icon and preferences. +pref("browser.translation.ui.show", false); +// Allows to define the translation engine. Google is default, Bing or Yandex are other options. +pref("browser.translation.engine", "Google"); + +// Telemetry settings. +// Determines if Telemetry pings can be archived locally. +pref("toolkit.telemetry.archive.enabled", true); +// Enables sending the shutdown ping when Firefox shuts down. +pref("toolkit.telemetry.shutdownPingSender.enabled", true); +// Enables sending the shutdown ping using the pingsender from the first session. +pref("toolkit.telemetry.shutdownPingSender.enabledFirstSession", false); +// Enables sending a duplicate of the first shutdown ping from the first session. +pref("toolkit.telemetry.firstShutdownPing.enabled", true); +// Enables sending the 'new-profile' ping on new profiles. +pref("toolkit.telemetry.newProfilePing.enabled", true); +// Enables sending 'update' pings on Firefox updates. +pref("toolkit.telemetry.updatePing.enabled", true); +// Enables sending 'bhr' pings when the browser hangs. +pref("toolkit.telemetry.bhrPing.enabled", true); +// Whether to enable Ecosystem Telemetry, requires a restart. +pref("toolkit.telemetry.ecosystemtelemetry.enabled", false); + +// Ping Centre Telemetry settings. +pref("browser.ping-centre.telemetry", true); +pref("browser.ping-centre.log", false); + +// Enable GMP support in the addon manager. +pref("media.gmp-provider.enabled", true); + +#ifdef NIGHTLY_BUILD +// Enable Dynamic First-Party Isolation in Nightly. +pref("network.cookie.cookieBehavior", 5 /* BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN */); +#else +// Enable blocking access to storage from tracking resources by default. +pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */); +#endif + +// Enable fingerprinting blocking by default for all channels, only on desktop. +pref("privacy.trackingprotection.fingerprinting.enabled", true); + +// Enable cryptomining blocking by default for all channels, only on desktop. +pref("privacy.trackingprotection.cryptomining.enabled", true); + +pref("browser.contentblocking.database.enabled", true); + +pref("dom.storage_access.enabled", true); + +pref("browser.contentblocking.cryptomining.preferences.ui.enabled", true); +pref("browser.contentblocking.fingerprinting.preferences.ui.enabled", true); +#ifdef NIGHTLY_BUILD + // Enable cookieBehavior = BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN as an option in the custom category ui + pref("browser.contentblocking.reject-and-isolate-cookies.preferences.ui.enabled", true); +#endif +// State Partitioning MVP UI. +pref("browser.contentblocking.state-partitioning.mvp.ui.enabled", true); + +// Possible values for browser.contentblocking.features.strict pref: +// Tracking Protection: +// "tp": tracking protection enabled +// "-tp": tracking protection disabled +// Tracking Protection in private windows: +// "tpPrivate": tracking protection in private windows enabled +// "-tpPrivate": tracking protection in private windows disabled +// Fingerprinting: +// "fp": fingerprinting blocking enabled +// "-fp": fingerprinting blocking disabled +// Cryptomining: +// "cm": cryptomining blocking enabled +// "-cm": cryptomining blocking disabled +// Social Tracking Protection: +// "stp": social tracking protection enabled +// "-stp": social tracking protection disabled +// Cookie behavior: +// "cookieBehavior0": cookie behaviour BEHAVIOR_ACCEPT +// "cookieBehavior1": cookie behaviour BEHAVIOR_REJECT_FOREIGN +// "cookieBehavior2": cookie behaviour BEHAVIOR_REJECT +// "cookieBehavior3": cookie behaviour BEHAVIOR_LIMIT_FOREIGN +// "cookieBehavior4": cookie behaviour BEHAVIOR_REJECT_TRACKER +// "cookieBehavior5": cookie behaviour BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN +// One value from each section must be included in the browser.contentblocking.features.strict pref. +pref("browser.contentblocking.features.strict", "tp,tpPrivate,cookieBehavior5,cm,fp,stp,lvl2"); + +// Hide the "Change Block List" link for trackers/tracking content in the custom +// Content Blocking/ETP panel. By default, it will not be visible. There is also +// an UI migration in place to set this pref to true if a user has a custom block +// lists enabled. +pref("browser.contentblocking.customBlockList.preferences.ui.enabled", false); + +pref("browser.contentblocking.reportBreakage.url", "https://tracking-protection-issues.herokuapp.com/new"); + +// Enable Protections report's Lockwise card by default. +pref("browser.contentblocking.report.lockwise.enabled", true); + +// Enable Protections report's Monitor card by default. +pref("browser.contentblocking.report.monitor.enabled", true); + +// Disable Protections report's Proxy card by default. +pref("browser.contentblocking.report.proxy.enabled", false); + +// Disable the mobile promotion by default. +pref("browser.contentblocking.report.show_mobile_app", true); + +// Enable the vpn card by default. +pref("browser.contentblocking.report.vpn.enabled", true); +// Only show vpn card to certain regions. Comma separated string of two letter ISO 3166-1 country codes. +pref("browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb"); +// Comma separated string of mozilla vpn supported platforms. +pref("browser.contentblocking.report.vpn_platforms", "win"); +pref("browser.contentblocking.report.hide_vpn_banner", false); +pref("browser.contentblocking.report.vpn_sub_id", "sub_HrfCZF7VPHzZkA"); + +pref("browser.contentblocking.report.monitor.url", "https://monitor.firefox.com/?entrypoint=protection_report_monitor&utm_source=about-protections"); +pref("browser.contentblocking.report.monitor.how_it_works.url", "https://monitor.firefox.com/about"); +pref("browser.contentblocking.report.monitor.sign_in_url", "https://monitor.firefox.com/oauth/init?entrypoint=protection_report_monitor&utm_source=about-protections&email="); +pref("browser.contentblocking.report.monitor.preferences_url", "https://monitor.firefox.com/user/preferences"); +pref("browser.contentblocking.report.monitor.home_page_url", "https://monitor.firefox.com/user/dashboard"); +pref("browser.contentblocking.report.manage_devices.url", "https://accounts.firefox.com/settings/clients"); +pref("browser.contentblocking.report.endpoint_url", "https://monitor.firefox.com/user/breach-stats?includeResolved=true"); +pref("browser.contentblocking.report.proxy_extension.url", "https://fpn.firefox.com/browser?utm_source=firefox-desktop&utm_medium=referral&utm_campaign=about-protections&utm_content=about-protections"); +pref("browser.contentblocking.report.mobile-ios.url", "https://apps.apple.com/app/firefox-private-safe-browser/id989804926"); +pref("browser.contentblocking.report.mobile-android.url", "https://play.google.com/store/apps/details?id=org.mozilla.firefox&referrer=utm_source%3Dprotection_report%26utm_content%3Dmobile_promotion"); +pref("browser.contentblocking.report.vpn.url", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=about-protections-card"); +pref("browser.contentblocking.report.vpn-promo.url", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=about-protections-top-promo"); +pref("browser.contentblocking.report.vpn-android.url", "https://play.google.com/store/apps/details?id=org.mozilla.firefox.vpn&referrer=utm_source%3Dfirefox-browser%26utm_medium%3Dfirefox-browser%26utm_campaign%3Dabout-protections-mobile-vpn%26anid%3D--"); +pref("browser.contentblocking.report.vpn-ios.url", "https://apps.apple.com/us/app/firefox-private-network-vpn/id1489407738"); + +// Protection Report's SUMO urls +pref("browser.contentblocking.report.lockwise.how_it_works.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/password-manager-report"); +pref("browser.contentblocking.report.social.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/social-media-tracking-report"); +pref("browser.contentblocking.report.cookie.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/cross-site-tracking-report"); +pref("browser.contentblocking.report.tracker.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/tracking-content-report"); +pref("browser.contentblocking.report.fingerprinter.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/fingerprinters-report"); +pref("browser.contentblocking.report.cryptominer.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/cryptominers-report"); + +pref("browser.contentblocking.cfr-milestone.enabled", true); +pref("browser.contentblocking.cfr-milestone.milestone-achieved", 0); +// Milestones should always be in increasing order +pref("browser.contentblocking.cfr-milestone.milestones", "[1000, 5000, 10000, 25000, 50000, 100000, 250000, 314159, 500000, 750000, 1000000, 1250000, 1500000, 1750000, 2000000, 2250000, 2500000, 8675309]"); + +// Enables the new Protections Panel. +#ifdef NIGHTLY_BUILD + pref("browser.protections_panel.enabled", true); + pref("browser.protections_panel.infoMessage.seen", false); +#endif + +// Always enable newtab segregation using containers +pref("privacy.usercontext.about_newtab_segregation.enabled", true); +// Enable Contextual Identity Containers +#ifdef NIGHTLY_BUILD + pref("privacy.userContext.enabled", true); + pref("privacy.userContext.ui.enabled", true); +#else + pref("privacy.userContext.enabled", false); + pref("privacy.userContext.ui.enabled", false); +#endif +pref("privacy.userContext.extension", ""); +// allows user to open container menu on a left click instead of a new +// tab in the default container +pref("privacy.userContext.newTabContainerOnLeftClick.enabled", false); + +#if defined(NIGHTLY_BUILD) || defined(XP_WIN) || defined(XP_MACOSX) +// Set to true to allow the user to silence all notifications when +// sharing the screen. Only shipping on Windows and macOS out to +// release. Enabled for all desktop platforms on Nightly. +pref("privacy.webrtc.allowSilencingNotifications", true); +// Set to true to use the legacy WebRTC global indicator +pref("privacy.webrtc.legacyGlobalIndicator", false); +pref("privacy.webrtc.hideGlobalIndicator", false); +#else +pref("privacy.webrtc.allowSilencingNotifications", false); +pref("privacy.webrtc.legacyGlobalIndicator", true); +#endif + +// Set to true to add toggles to the WebRTC indicator for globally +// muting the camera and microphone. +pref("privacy.webrtc.globalMuteToggles", false); + +// Set to true to enable a warning displayed when attempting +// to switch tabs in a window that's being shared over WebRTC. +pref("privacy.webrtc.sharedTabWarning", false); + +// Start the browser in e10s mode +pref("browser.tabs.remote.autostart", true); +pref("browser.tabs.remote.desktopbehavior", true); + +// Run media transport in a separate process? +#ifdef EARLY_BETA_OR_EARLIER + pref("media.peerconnection.mtransport_process", true); +#else + pref("media.peerconnection.mtransport_process", false); +#endif + +// For speculatively warming up tabs to improve perceived +// performance while using the async tab switcher. +pref("browser.tabs.remote.warmup.enabled", true); + +// Caches tab layers to improve perceived performance +// of tab switches. +pref("browser.tabs.remote.tabCacheSize", 0); + +pref("browser.tabs.remote.warmup.maxTabs", 3); +pref("browser.tabs.remote.warmup.unloadDelayMs", 2000); + +// For the about:tabcrashed page +pref("browser.tabs.crashReporting.sendReport", true); +pref("browser.tabs.crashReporting.includeURL", false); +pref("browser.tabs.crashReporting.requestEmail", false); +pref("browser.tabs.crashReporting.emailMe", false); +pref("browser.tabs.crashReporting.email", ""); + +// If true, unprivileged extensions may use experimental APIs on +// nightly and developer edition. +pref("extensions.experiments.enabled", false); + +#if defined(XP_WIN) + // Allows us to deprioritize the processes of background tabs at an OS level + pref("dom.ipc.processPriorityManager.enabled", true); +#endif + +// Don't limit how many nodes we care about on desktop: +pref("reader.parse-node-limit", 0); + +// On desktop, we want the URLs to be included here for ease of debugging, +// and because (normally) these errors are not persisted anywhere. +pref("reader.errors.includeURLs", true); + +pref("view_source.tab", true); + +pref("dom.serviceWorkers.enabled", true); + +// Enable Push API. +pref("dom.push.enabled", true); + +// These are the thumbnail width/height set in about:newtab. +// If you change this, ENSURE IT IS THE SAME SIZE SET +// by about:newtab. These values are in CSS pixels. +pref("toolkit.pageThumbs.minWidth", 280); +pref("toolkit.pageThumbs.minHeight", 190); + +// Enable speech synthesis +pref("media.webspeech.synth.enabled", true); + +pref("browser.esedbreader.loglevel", "Error"); + +pref("browser.laterrun.enabled", false); + +pref("dom.ipc.processPrelaunch.enabled", true); + +// See comments in bug 1340115 on how we got to these numbers. +pref("browser.migrate.chrome.history.limit", 2000); +pref("browser.migrate.chrome.history.maxAgeInDays", 180); +pref("browser.migrate.showBookmarksToolbarAfterMigration", true); + +pref("extensions.pocket.api", "api.getpocket.com"); +pref("extensions.pocket.enabled", true); +pref("extensions.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4"); +pref("extensions.pocket.site", "getpocket.com"); +pref("extensions.pocket.onSaveRecs", true); +pref("extensions.pocket.onSaveRecs.locales", "en-US,en-GB,en-CA"); + +// Control what version of the logged out doorhanger is displayed +// Possibilities are: `control`, `control-one-button`, `variant_a`, `variant_b`, `variant_c` +pref("extensions.pocket.loggedOutVariant", "control"); + +#ifdef NIGHTLY_BUILD +pref("signon.management.page.fileImport.enabled", true); +pref("signon.management.page.os-auth.enabled", true); +#else +pref("signon.management.page.fileImport.enabled", false); +pref("signon.management.page.os-auth.enabled", false); +#endif +pref("signon.management.page.breach-alerts.enabled", true); +pref("signon.management.page.vulnerable-passwords.enabled", true); +pref("signon.management.page.sort", "name"); +// The utm_creative value is appended within the code (specific to the location on +// where it is clicked). Be sure that if these two prefs are updated, that +// the utm_creative param be last. +pref("signon.management.page.breachAlertUrl", + "https://monitor.firefox.com/breach-details/"); +pref("signon.management.page.showPasswordSyncNotification", true); +pref("signon.passwordEditCapture.enabled", true); +pref("signon.showAutoCompleteFooter", true); +pref("signon.showAutoCompleteImport", "import"); +pref("signon.suggestImportCount", 3); + +// Enable the "Simplify Page" feature in Print Preview. This feature +// is disabled by default in toolkit. +pref("print.use_simplify_page", true); + +// Space separated list of URLS that are allowed to send objects (instead of +// only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js +pref("webchannel.allowObject.urlWhitelist", "https://content.cdn.mozilla.net https://support.mozilla.org https://install.mozilla.org"); + +// Whether or not the browser should scan for unsubmitted +// crash reports, and then show a notification for submitting +// those reports. +#ifdef NIGHTLY_BUILD + pref("browser.crashReports.unsubmittedCheck.enabled", true); +#else + pref("browser.crashReports.unsubmittedCheck.enabled", false); +#endif + +// chancesUntilSuppress is how many times we'll show the unsubmitted +// crash report notification across different days and shutdown +// without a user choice before we suppress the notification for +// some number of days. +pref("browser.crashReports.unsubmittedCheck.chancesUntilSuppress", 4); +pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false); + +// Preferences for the form autofill system extension +// The truthy values of "extensions.formautofill.available" are "on" and "detect", +// any other value means autofill isn't available. +// "detect" means it's enabled if conditions defined in the extension are met. +pref("extensions.formautofill.available", "detect"); +pref("extensions.formautofill.addresses.enabled", true); +pref("extensions.formautofill.addresses.capture.enabled", false); +pref("extensions.formautofill.creditCards.available", true); +pref("extensions.formautofill.creditCards.enabled", true); +// Checkbox in sync options for credit card data sync service +pref("services.sync.engine.creditcards.available", true); +// Temporary preference to control displaying the UI elements for +// credit card autofill used for the duration of the A/B test. +pref("extensions.formautofill.creditCards.hideui", false); +// Pref for shield/heartbeat to recognize users who have used Credit Card +// Autofill. The valid values can be: +// 0: none +// 1: submitted a manually-filled credit card form (but didn't see the doorhanger +// because of a duplicate profile in the storage) +// 2: saw the doorhanger +// 3: submitted an autofill'ed credit card form +pref("extensions.formautofill.creditCards.used", 0); +pref("extensions.formautofill.firstTimeUse", true); +pref("extensions.formautofill.heuristics.enabled", true); +// Whether the user enabled the OS re-auth dialog. +pref("extensions.formautofill.reauth.enabled", false); +pref("extensions.formautofill.section.enabled", true); +pref("extensions.formautofill.loglevel", "Warn"); + +pref("toolkit.osKeyStore.loglevel", "Warn"); + +pref("extensions.formautofill.supportedCountries", "US,CA"); +pref("extensions.formautofill.supportRTL", false); + +// Whether or not to restore a session with lazy-browser tabs. +pref("browser.sessionstore.restore_tabs_lazily", true); + +pref("browser.suppress_first_window_animation", true); + +// Preference that allows individual users to disable Screenshots. +pref("extensions.screenshots.disabled", false); +// Preference that allows individual users to leave Screenshots enabled, but +// disable uploading to the server. +pref("extensions.screenshots.upload-disabled", false); + +// DoH Rollout: whether to enable automatic performance-based TRR-selection. +// This pref is controlled by a Normandy rollout so we don't overload providers. +pref("doh-rollout.trr-selection.enabled", false); + +// DoH Rollout: whether to enable automatic steering to provider endpoints. +// This pref is also controlled by a Normandy rollout. +pref("doh-rollout.provider-steering.enabled", false); + +// DoH Rollout: provider details for automatic steering. +pref("doh-rollout.provider-steering.provider-list", "[{ \"name\": \"comcast\", \"canonicalName\": \"doh-discovery.xfinity.com\", \"uri\": \"https://doh.xfinity.com/dns-query\" }]"); + +// DoH Rollout: whether to clear the mode value at shutdown. +pref("doh-rollout.clearModeOnShutdown", false); + +// URL for Learn More link for browser error logging in preferences +pref("browser.chrome.errorReporter.infoURL", + "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/nightly-error-collection"); + +// Normandy client preferences +pref("app.normandy.api_url", "https://normandy.cdn.mozilla.net/api/v1"); +pref("app.normandy.dev_mode", false); +pref("app.normandy.enabled", true); +pref("app.normandy.first_run", true); +pref("app.normandy.logging.level", 50); // Warn +pref("app.normandy.run_interval_seconds", 21600); // 6 hours +pref("app.normandy.shieldLearnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/shield"); +pref("app.normandy.last_seen_buildid", ""); +pref("app.normandy.onsync_skew_sec", 600); +#ifdef MOZ_DATA_REPORTING + pref("app.shield.optoutstudies.enabled", true); +#else + pref("app.shield.optoutstudies.enabled", false); +#endif + +// Multi-lingual preferences +#if defined(RELEASE_OR_BETA) && !defined(MOZ_DEV_EDITION) + pref("intl.multilingual.enabled", true); + pref("intl.multilingual.downloadEnabled", true); +#else + pref("intl.multilingual.enabled", false); + // AMO only serves language packs for release and beta versions. + pref("intl.multilingual.downloadEnabled", false); +#endif + +// Simulate conditions that will happen when the browser +// is running with Fission enabled. This is meant to assist +// development and testing of Fission. +// The current simulated conditions are: +// - Don't propagate events from subframes to JS child actors +pref("fission.frontend.simulate-events", false); +// - Only deliver subframe messages that specifies +// their destination (using the BrowsingContext id). +pref("fission.frontend.simulate-messages", false); + +// Coverage ping is disabled by default. +pref("toolkit.coverage.enabled", false); +pref("toolkit.coverage.endpoint.base", "https://coverage.mozilla.org"); + +// Discovery prefs +pref("browser.discovery.enabled", true); +pref("browser.discovery.containers.enabled", true); +pref("browser.discovery.sites", "addons.mozilla.org"); + +pref("browser.engagement.recent_visited_origins.expiry", 86400); // 24 * 60 * 60 (24 hours in seconds) +pref("browser.engagement.downloads-button.has-used", false); +pref("browser.engagement.fxa-toolbar-menu-button.has-used", false); +pref("browser.engagement.home-button.has-used", false); +pref("browser.engagement.sidebar-button.has-used", false); +pref("browser.engagement.library-button.has-used", false); +pref("browser.engagement.ctrlTab.has-used", false); + +pref("browser.aboutConfig.showWarning", true); + +pref("browser.toolbars.keyboard_navigation", true); + +// The visibility of the bookmarks toolbar. +// "newtab": Show on the New Tab Page +// "always": Always show +// "never": Never show +pref("browser.toolbars.bookmarks.visibility", "newtab"); + +// Visibility of the "Show Other Bookmarks" menuitem in the +// bookmarks toolbar contextmenu. +pref("browser.toolbars.bookmarks.showOtherBookmarks", true); + +// When true, this pref will always show the bookmarks bar on +// the New Tab Page, and other functionality to improve the usage of the +// Bookmarks Toolbar. +pref("browser.toolbars.bookmarks.2h2020", true); + +// Prefs to control the Firefox Account toolbar menu. +// This pref will surface existing Firefox Account information +// as a button next to the hamburger menu. It allows +// quick access to sign-in and manage your Firefox Account. +pref("identity.fxaccounts.toolbar.enabled", true); +pref("identity.fxaccounts.toolbar.accessed", false); + +// Prefs for different services supported by Firefox Account +pref("identity.fxaccounts.service.monitorLoginUrl", "https://monitor.firefox.com/"); + +// Check bundled omni JARs for corruption. +pref("corroborator.enabled", true); + +// Toolbox preferences +pref("devtools.toolbox.footer.height", 250); +pref("devtools.toolbox.sidebar.width", 500); +pref("devtools.toolbox.host", "bottom"); +pref("devtools.toolbox.previousHost", "right"); +pref("devtools.toolbox.selectedTool", "inspector"); +pref("devtools.toolbox.sideEnabled", true); +pref("devtools.toolbox.zoomValue", "1"); +pref("devtools.toolbox.splitconsoleEnabled", false); +pref("devtools.toolbox.splitconsoleHeight", 100); +pref("devtools.toolbox.tabsOrder", ""); + +// The fission pref for enabling the "Multiprocess Browser Toolbox", which will +// make it possible to debug anything in Firefox (See Bug 1570639 for more +// information). +#if defined(NIGHTLY_BUILD) +pref("devtools.browsertoolbox.fission", true); +#else +pref("devtools.browsertoolbox.fission", false); +#endif + +// This pref is also related to fission, but not only. It allows the toolbox +// to stay open even if the debugged tab switches to another process. +// It can happen between two documents, one running in the parent process like +// about:sessionrestore and another one running in the content process like +// any web page. Or between two distinct domain when running with fission turned +// on. See bug 1565263. +pref("devtools.target-switching.enabled", true); + +// Toolbox Button preferences +pref("devtools.command-button-pick.enabled", true); +pref("devtools.command-button-frames.enabled", true); +pref("devtools.command-button-splitconsole.enabled", true); +pref("devtools.command-button-paintflashing.enabled", false); +pref("devtools.command-button-responsive.enabled", true); +pref("devtools.command-button-screenshot.enabled", false); +pref("devtools.command-button-rulers.enabled", false); +pref("devtools.command-button-measure.enabled", false); +pref("devtools.command-button-noautohide.enabled", false); +pref("devtools.command-button-errorcount.enabled", true); +#ifndef MOZILLA_OFFICIAL + pref("devtools.command-button-fission-prefs.enabled", true); +#endif + +// Inspector preferences +// Enable the Inspector +pref("devtools.inspector.enabled", true); +// What was the last active sidebar in the inspector +pref("devtools.inspector.activeSidebar", "layoutview"); +pref("devtools.inspector.remote", false); + +// Enable the 3 pane mode in the inspector +pref("devtools.inspector.three-pane-enabled", true); +// Enable the 3 pane mode in the chrome inspector +pref("devtools.inspector.chrome.three-pane-enabled", false); +// Collapse pseudo-elements by default in the rule-view +pref("devtools.inspector.show_pseudo_elements", false); +// The default size for image preview tooltips in the rule-view/computed-view/markup-view +pref("devtools.inspector.imagePreviewTooltipSize", 300); +// Enable user agent style inspection in rule-view +pref("devtools.inspector.showUserAgentStyles", false); +// Show native anonymous content and user agent shadow roots +pref("devtools.inspector.showAllAnonymousContent", false); +// Enable the new Rules View +pref("devtools.inspector.new-rulesview.enabled", false); +// Enable the inline CSS compatiblity warning in inspector rule view +pref("devtools.inspector.ruleview.inline-compatibility-warning.enabled", false); +// Enable the compatibility tool in the inspector. +#if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) +pref("devtools.inspector.compatibility.enabled", true); +#else +pref("devtools.inspector.compatibility.enabled", false); +#endif +// Enable color scheme simulation in the inspector. +pref("devtools.inspector.color-scheme-simulation.enabled", false); +// Enable overflow debugging in the inspector. +pref("devtools.overflow.debugging.enabled", true); + +// Grid highlighter preferences +pref("devtools.gridinspector.gridOutlineMaxColumns", 50); +pref("devtools.gridinspector.gridOutlineMaxRows", 50); +pref("devtools.gridinspector.showGridAreas", false); +pref("devtools.gridinspector.showGridLineNumbers", false); +pref("devtools.gridinspector.showInfiniteLines", false); +// Max number of grid highlighters that can be displayed +pref("devtools.gridinspector.maxHighlighters", 3); + +// Whether or not the box model panel is opened in the layout view +pref("devtools.layout.boxmodel.opened", true); +// Whether or not the flexbox panel is opened in the layout view +pref("devtools.layout.flexbox.opened", true); +// Whether or not the flexbox container panel is opened in the layout view +pref("devtools.layout.flex-container.opened", true); +// Whether or not the flexbox item panel is opened in the layout view +pref("devtools.layout.flex-item.opened", true); +// Whether or not the grid inspector panel is opened in the layout view +pref("devtools.layout.grid.opened", true); + +// Enable hovering Box Model values and jumping to their source CSS rule in the +// rule-view. +#if defined(NIGHTLY_BUILD) + pref("devtools.layout.boxmodel.highlightProperty", true); +#else + pref("devtools.layout.boxmodel.highlightProperty", false); +#endif + +// By how many times eyedropper will magnify pixels +pref("devtools.eyedropper.zoom", 6); + +// Enable to collapse attributes that are too long. +pref("devtools.markup.collapseAttributes", true); +// Length to collapse attributes +pref("devtools.markup.collapseAttributeLength", 120); +// Whether to auto-beautify the HTML on copy. +pref("devtools.markup.beautifyOnCopy", false); +// Whether or not the DOM mutation breakpoints context menu are enabled in the +// markup view. +pref("devtools.markup.mutationBreakpoints.enabled", true); + +// DevTools default color unit +pref("devtools.defaultColorUnit", "authored"); + +// Enable the Memory tools +pref("devtools.memory.enabled", true); + +pref("devtools.memory.custom-census-displays", "{}"); +pref("devtools.memory.custom-label-displays", "{}"); +pref("devtools.memory.custom-tree-map-displays", "{}"); + +pref("devtools.memory.max-individuals", 1000); +pref("devtools.memory.max-retaining-paths", 10); + +// Enable the Performance tools +pref("devtools.performance.enabled", true); + +// The default Performance UI settings +pref("devtools.performance.memory.sample-probability", "0.05"); +// Can't go higher than this without causing internal allocation overflows while +// serializing the allocations data over the RDP. +pref("devtools.performance.memory.max-log-length", 125000); +pref("devtools.performance.timeline.hidden-markers", + "[\"Composite\",\"CompositeForwardTransaction\"]"); +pref("devtools.performance.profiler.buffer-size", 10000000); +pref("devtools.performance.profiler.sample-frequency-hz", 1000); +pref("devtools.performance.ui.invert-call-tree", true); +pref("devtools.performance.ui.invert-flame-graph", false); +pref("devtools.performance.ui.flatten-tree-recursion", true); +pref("devtools.performance.ui.show-platform-data", false); +pref("devtools.performance.ui.show-idle-blocks", true); +pref("devtools.performance.ui.enable-memory", false); +pref("devtools.performance.ui.enable-allocations", false); +pref("devtools.performance.ui.enable-framerate", true); +pref("devtools.performance.ui.show-jit-optimizations", false); +pref("devtools.performance.ui.show-triggers-for-gc-types", + "TOO_MUCH_MALLOC ALLOC_TRIGGER LAST_DITCH EAGER_ALLOC_TRIGGER"); + +// Temporary pref disabling memory flame views +// TODO remove once we have flame charts via bug 1148663 +pref("devtools.performance.ui.enable-memory-flame", false); + +// Enable experimental options in the UI only in Nightly +#if defined(NIGHTLY_BUILD) + pref("devtools.performance.ui.experimental", true); +#else + pref("devtools.performance.ui.experimental", false); +#endif + +// The default cache UI setting +pref("devtools.cache.disabled", false); + +// The default service workers UI setting +pref("devtools.serviceWorkers.testing.enabled", false); + +// Enable the Network Monitor +pref("devtools.netmonitor.enabled", true); + +pref("devtools.netmonitor.features.search", true); +pref("devtools.netmonitor.features.requestBlocking", true); + +// Enable the Application panel +pref("devtools.application.enabled", true); + +// The default Network Monitor UI settings +pref("devtools.netmonitor.panes-network-details-width", 550); +pref("devtools.netmonitor.panes-network-details-height", 450); +pref("devtools.netmonitor.panes-search-width", 550); +pref("devtools.netmonitor.panes-search-height", 450); +pref("devtools.netmonitor.filters", "[\"all\"]"); +pref("devtools.netmonitor.visibleColumns", + "[\"status\",\"method\",\"domain\",\"file\",\"initiator\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]" +); +pref("devtools.netmonitor.columnsData", + '[{"name":"status","minWidth":30,"width":5}, {"name":"method","minWidth":30,"width":5}, {"name":"domain","minWidth":30,"width":10}, {"name":"file","minWidth":30,"width":25}, {"name":"url","minWidth":30,"width":25},{"name":"initiator","minWidth":30,"width":10},{"name":"type","minWidth":30,"width":5},{"name":"transferred","minWidth":30,"width":10},{"name":"contentSize","minWidth":30,"width":5},{"name":"waterfall","minWidth":150,"width":15}]'); +pref("devtools.netmonitor.msg.payload-preview-height", 128); +pref("devtools.netmonitor.msg.visibleColumns", + '["data", "time"]' +); +pref("devtools.netmonitor.msg.displayed-messages.limit", 500); + +pref("devtools.netmonitor.response.ui.limit", 10240); + +// Save request/response bodies yes/no. +pref("devtools.netmonitor.saveRequestAndResponseBodies", true); + +// The default Network monitor HAR export setting +pref("devtools.netmonitor.har.defaultLogDir", ""); +pref("devtools.netmonitor.har.defaultFileName", "%hostname_Archive [%date]"); +pref("devtools.netmonitor.har.jsonp", false); +pref("devtools.netmonitor.har.jsonpCallback", ""); +pref("devtools.netmonitor.har.includeResponseBodies", true); +pref("devtools.netmonitor.har.compress", false); +pref("devtools.netmonitor.har.forceExport", false); +pref("devtools.netmonitor.har.pageLoadedTimeout", 1500); +pref("devtools.netmonitor.har.enableAutoExportToFile", false); + +pref("devtools.netmonitor.features.webSockets", true); + +// netmonitor audit +pref("devtools.netmonitor.audits.slow", 500); + +// Enable the EventSource Inspector +pref("devtools.netmonitor.features.serverSentEvents", true); + +// Enable the Storage Inspector +pref("devtools.storage.enabled", true); + +// Enable the Style Editor. +pref("devtools.styleeditor.enabled", true); +pref("devtools.styleeditor.autocompletion-enabled", true); +pref("devtools.styleeditor.showMediaSidebar", true); +pref("devtools.styleeditor.mediaSidebarWidth", 238); +pref("devtools.styleeditor.navSidebarWidth", 245); +pref("devtools.styleeditor.transitions", true); + +// Screenshot Option Settings. +pref("devtools.screenshot.clipboard.enabled", false); +pref("devtools.screenshot.audio.enabled", true); + +// Make sure the DOM panel is hidden by default +pref("devtools.dom.enabled", false); + +// Enable the Accessibility panel. +pref("devtools.accessibility.enabled", true); + +// Web console filters +pref("devtools.webconsole.filter.error", true); +pref("devtools.webconsole.filter.warn", true); +pref("devtools.webconsole.filter.info", true); +pref("devtools.webconsole.filter.log", true); +pref("devtools.webconsole.filter.debug", true); +pref("devtools.webconsole.filter.css", false); +pref("devtools.webconsole.filter.net", false); +pref("devtools.webconsole.filter.netxhr", false); + +// Webconsole autocomplete preference +pref("devtools.webconsole.input.autocomplete",true); + +// Show context selector in console input, in the browser toolbox +#if defined(NIGHTLY_BUILD) + pref("devtools.webconsole.input.context", true); +#else + pref("devtools.webconsole.input.context", false); +#endif + +// Show context selector in console input, in the content toolbox +pref("devtools.contenttoolbox.webconsole.input.context", false); + +// Set to true to eagerly show the results of webconsole terminal evaluations +// when they don't have side effects. +pref("devtools.webconsole.input.eagerEvaluation", true); + +// Browser console filters +pref("devtools.browserconsole.filter.error", true); +pref("devtools.browserconsole.filter.warn", true); +pref("devtools.browserconsole.filter.info", true); +pref("devtools.browserconsole.filter.log", true); +pref("devtools.browserconsole.filter.debug", true); +pref("devtools.browserconsole.filter.css", false); +pref("devtools.browserconsole.filter.net", false); +pref("devtools.browserconsole.filter.netxhr", false); + +// Max number of inputs to store in web console history. +pref("devtools.webconsole.inputHistoryCount", 300); + +// Persistent logging: |true| if you want the relevant tool to keep all of the +// logged messages after reloading the page, |false| if you want the output to +// be cleared each time page navigation happens. +pref("devtools.webconsole.persistlog", false); +pref("devtools.netmonitor.persistlog", false); + +// Web Console timestamp: |true| if you want the logs and instructions +// in the Web Console to display a timestamp, or |false| to not display +// any timestamps. +pref("devtools.webconsole.timestampMessages", false); + +// Enable the webconsole sidebar toggle in Nightly builds. +#if defined(NIGHTLY_BUILD) + pref("devtools.webconsole.sidebarToggle", true); +#else + pref("devtools.webconsole.sidebarToggle", false); +#endif + +// Saved editor mode state in the console. +pref("devtools.webconsole.input.editor", false); +pref("devtools.browserconsole.input.editor", false); + +// Editor width for webconsole and browserconsole. +pref("devtools.webconsole.input.editorWidth", 0); +pref("devtools.browserconsole.input.editorWidth", 0); + +// Display an onboarding UI for the Editor mode. +pref("devtools.webconsole.input.editorOnboarding", true); + +// Disable the new performance recording panel by default +pref("devtools.performance.new-panel-enabled", false); + +// Enable message grouping in the console, true by default +pref("devtools.webconsole.groupWarningMessages", true); + +// Saved state of the Display content messages checkbox in the browser console. +pref("devtools.browserconsole.contentMessages", false); + +// Enable client-side mapping service for source maps +pref("devtools.source-map.client-service.enabled", true); + +// The number of lines that are displayed in the web console. +pref("devtools.hud.loglimit", 10000); + +// The developer tools editor configuration: +// - tabsize: how many spaces to use when a Tab character is displayed. +// - expandtab: expand Tab characters to spaces. +// - keymap: which keymap to use (can be 'default', 'emacs' or 'vim') +// - autoclosebrackets: whether to permit automatic bracket/quote closing. +// - detectindentation: whether to detect the indentation from the file +// - enableCodeFolding: Whether to enable code folding or not. +pref("devtools.editor.tabsize", 2); +pref("devtools.editor.expandtab", true); +pref("devtools.editor.keymap", "default"); +pref("devtools.editor.autoclosebrackets", true); +pref("devtools.editor.detectindentation", true); +pref("devtools.editor.enableCodeFolding", true); +pref("devtools.editor.autocomplete", true); + +// The angle of the viewport. +pref("devtools.responsive.viewport.angle", 0); +// The width of the viewport. +pref("devtools.responsive.viewport.width", 320); +// The height of the viewport. +pref("devtools.responsive.viewport.height", 480); +// The pixel ratio of the viewport. +pref("devtools.responsive.viewport.pixelRatio", 0); +// Whether or not the viewports are left aligned. +pref("devtools.responsive.leftAlignViewport.enabled", false); +// Whether to reload when touch simulation is toggled +pref("devtools.responsive.reloadConditions.touchSimulation", false); +// Whether to reload when user agent is changed +pref("devtools.responsive.reloadConditions.userAgent", false); +// Whether to show the notification about reloading to apply emulation +pref("devtools.responsive.reloadNotification.enabled", true); +// Whether or not touch simulation is enabled. +pref("devtools.responsive.touchSimulation.enabled", false); +// Whether or not meta viewport is enabled, if and only if touchSimulation +// is also enabled. +pref("devtools.responsive.metaViewport.enabled", true); +// The user agent of the viewport. +pref("devtools.responsive.userAgent", ""); + +// Show the custom user agent input only in Nightly. +#if defined(NIGHTLY_BUILD) + pref("devtools.responsive.showUserAgentInput", true); +#else + pref("devtools.responsive.showUserAgentInput", false); +#endif + +// Show tab debug targets for This Firefox (on by default for local builds). +#ifdef MOZILLA_OFFICIAL + pref("devtools.aboutdebugging.local-tab-debugging", false); +#else + pref("devtools.aboutdebugging.local-tab-debugging", true); +#endif + +// Show process debug targets. +pref("devtools.aboutdebugging.process-debugging", true); +// Stringified array of network locations that users can connect to. +pref("devtools.aboutdebugging.network-locations", "[]"); +// Debug target pane collapse/expand settings. +pref("devtools.aboutdebugging.collapsibilities.installedExtension", false); +pref("devtools.aboutdebugging.collapsibilities.otherWorker", false); +pref("devtools.aboutdebugging.collapsibilities.serviceWorker", false); +pref("devtools.aboutdebugging.collapsibilities.sharedWorker", false); +pref("devtools.aboutdebugging.collapsibilities.tab", false); +pref("devtools.aboutdebugging.collapsibilities.temporaryExtension", false); + +// about:debugging: only show system and hidden extensions in local builds by +// default. +#ifdef MOZILLA_OFFICIAL + pref("devtools.aboutdebugging.showHiddenAddons", false); +#else + pref("devtools.aboutdebugging.showHiddenAddons", true); +#endif + +// Map top-level await expressions in the console +pref("devtools.debugger.features.map-await-expression", true); + +// This relies on javascript.options.asyncstack as well or it has no effect. +pref("devtools.debugger.features.async-captured-stacks", true); +pref("devtools.debugger.features.async-live-stacks", false); + +// Disable autohide for DevTools popups and tooltips. +// This is currently not exposed by any UI to avoid making +// about:devtools-toolbox tabs unusable by mistake. +pref("devtools.popup.disable_autohide", false); + +// FirstStartup service time-out in ms +pref("first-startup.timeout", 30000); + +// Enable the default browser agent. +// The agent still runs as scheduled if this pref is disabled, +// but it exits immediately before taking any action. +#ifdef XP_WIN + pref("default-browser-agent.enabled", true); +#endif + +// Test Prefs that do nothing for testing +#if defined(EARLY_BETA_OR_EARLIER) + pref("app.normandy.test-prefs.bool", false); + pref("app.normandy.test-prefs.integer", 0); + pref("app.normandy.test-prefs.string", ""); +#endif diff --git a/browser/app/splash.rc b/browser/app/splash.rc new file mode 100644 index 0000000000..a5d418e812 --- /dev/null +++ b/browser/app/splash.rc @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 <windows.h> +#include "nsNativeAppSupportWin.h" + +IDI_APPICON ICON FIREFOX_ICO +IDI_DOCUMENT ICON DOCUMENT_ICO +IDI_APPLICATION ICON FIREFOX_ICO +IDI_NEWWINDOW ICON NEWWINDOW_ICO +IDI_NEWTAB ICON NEWTAB_ICO +IDI_PBMODE ICON PBMODE_ICO + +STRINGTABLE DISCARDABLE +BEGIN + IDS_STARTMENU_APPNAME, "@MOZ_APP_DISPLAYNAME@" +END diff --git a/browser/app/winlauncher/DllBlocklistInit.cpp b/browser/app/winlauncher/DllBlocklistInit.cpp new file mode 100644 index 0000000000..4384bea1bd --- /dev/null +++ b/browser/app/winlauncher/DllBlocklistInit.cpp @@ -0,0 +1,213 @@ +/* -*- 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/. */ + +#define MOZ_USE_LAUNCHER_ERROR + +#include "nsWindowsDllInterceptor.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "mozilla/BinarySearch.h" +#include "mozilla/ImportDir.h" +#include "mozilla/NativeNt.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Types.h" +#include "mozilla/WindowsDllBlocklist.h" +#include "mozilla/WinHeaderOnlyUtils.h" + +#include "DllBlocklistInit.h" +#include "freestanding/DllBlocklist.h" +#include "freestanding/SharedSection.h" + +namespace mozilla { + +#if defined(MOZ_ASAN) || defined(_M_ARM64) + +// This DLL blocking code is incompatible with ASAN because +// it is able to execute before ASAN itself has even initialized. +// Also, AArch64 has not been tested with this. +LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP( + const wchar_t* aFullImagePath, HANDLE aChildProcess, + const IMAGE_THUNK_DATA*) { + return mozilla::Ok(); +} + +LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher( + const wchar_t* aFullImagePath, HANDLE aChildProcess) { + return mozilla::Ok(); +} + +#else + +static LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPInternal( + const wchar_t* aFullImagePath, nt::CrossExecTransferManager& aTransferMgr, + const IMAGE_THUNK_DATA* aCachedNtdllThunk) { + CrossProcessDllInterceptor intcpt(aTransferMgr.RemoteProcess()); + intcpt.Init(L"ntdll.dll"); + + bool ok = freestanding::stub_NtMapViewOfSection.SetDetour( + aTransferMgr, intcpt, "NtMapViewOfSection", + &freestanding::patched_NtMapViewOfSection); + if (!ok) { + return LAUNCHER_ERROR_FROM_DETOUR_ERROR(intcpt.GetLastDetourError()); + } + + ok = freestanding::stub_LdrLoadDll.SetDetour( + aTransferMgr, intcpt, "LdrLoadDll", &freestanding::patched_LdrLoadDll); + if (!ok) { + return LAUNCHER_ERROR_FROM_DETOUR_ERROR(intcpt.GetLastDetourError()); + } + + // Because aChildProcess has just been created in a suspended state, its + // dynamic linker has not yet been initialized, thus its executable has + // not yet been linked with ntdll.dll. If the blocklist hook intercepts a + // library load prior to the link, the hook will be unable to invoke any + // ntdll.dll functions. + // + // We know that the executable for our *current* process's binary is already + // linked into ntdll, so we obtain the IAT from our own executable and graft + // it onto the child process's IAT, thus enabling the child process's hook to + // safely make its ntdll calls. + + const nt::PEHeaders& ourExeImage = aTransferMgr.LocalPEHeaders(); + + // As part of our mitigation of binary tampering, copy our import directory + // from the original in our executable file. + LauncherVoidResult importDirRestored = + RestoreImportDirectory(aFullImagePath, aTransferMgr); + if (importDirRestored.isErr()) { + return importDirRestored; + } + + mozilla::nt::PEHeaders ntdllImage(::GetModuleHandleW(L"ntdll.dll")); + if (!ntdllImage) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); + } + + // If we have a cached IAT i.e. |aCachedNtdllThunk| is non-null, we can + // safely copy it to |aChildProcess| even if the local IAT has been modified. + // If |aCachedNtdllThunk| is null, we've failed to cache the IAT or we're in + // the launcher process where there is no chance to cache the IAT. In those + // cases, we retrieve the IAT with the boundary check to avoid a modified IAT + // from being copied into |aChildProcess|. + Maybe<Span<IMAGE_THUNK_DATA> > ntdllThunks; + if (aCachedNtdllThunk) { + ntdllThunks = ourExeImage.GetIATThunksForModule("ntdll.dll"); + } else { + Maybe<Range<const uint8_t> > ntdllBoundaries = ntdllImage.GetBounds(); + if (!ntdllBoundaries) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); + } + + // We can use GetIATThunksForModule() to check whether IAT is modified + // or not because no functions exported from ntdll.dll is forwarded. + ntdllThunks = + ourExeImage.GetIATThunksForModule("ntdll.dll", ntdllBoundaries.ptr()); + } + if (!ntdllThunks) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA); + } + + { // Scope for prot + PIMAGE_THUNK_DATA firstIatThunkDst = ntdllThunks.value().data(); + const IMAGE_THUNK_DATA* firstIatThunkSrc = + aCachedNtdllThunk ? aCachedNtdllThunk : firstIatThunkDst; + SIZE_T iatLength = ntdllThunks.value().LengthBytes(); + + AutoVirtualProtect prot = + aTransferMgr.Protect(firstIatThunkDst, iatLength, PAGE_READWRITE); + if (!prot) { + return LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(prot.GetError()); + } + + LauncherVoidResult writeResult = + aTransferMgr.Transfer(firstIatThunkDst, firstIatThunkSrc, iatLength); + if (writeResult.isErr()) { + return writeResult.propagateErr(); + } + } + + // Tell the mozglue blocklist that we have bootstrapped + uint32_t newFlags = eDllBlocklistInitFlagWasBootstrapped; + + if (gBlocklistInitFlags & eDllBlocklistInitFlagWasBootstrapped) { + // If we ourselves were bootstrapped, then we are starting a child process + // and need to set the appropriate flag. + newFlags |= eDllBlocklistInitFlagIsChildProcess; + } + + LauncherVoidResult writeResult = + aTransferMgr.Transfer(&gBlocklistInitFlags, &newFlags, sizeof(newFlags)); + if (writeResult.isErr()) { + return writeResult.propagateErr(); + } + + return Ok(); +} + +LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP( + const wchar_t* aFullImagePath, HANDLE aChildProcess, + const IMAGE_THUNK_DATA* aCachedNtdllThunk) { + nt::CrossExecTransferManager transferMgr(aChildProcess); + if (!transferMgr) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); + } + + // We come here when the browser process launches a sandbox process. + // If the launcher process already failed to bootstrap the browser process, + // we should not attempt to bootstrap a child process because it's likely + // to fail again. Instead, we only restore the import directory entry. + if (!(gBlocklistInitFlags & eDllBlocklistInitFlagWasBootstrapped)) { + return RestoreImportDirectory(aFullImagePath, transferMgr); + } + + // Transfer a readonly handle to the child processes because all information + // are already written to the section by the launcher and main process. + LauncherVoidResult transferResult = + freestanding::gSharedSection.TransferHandle(transferMgr, GENERIC_READ); + if (transferResult.isErr()) { + return transferResult.propagateErr(); + } + + return InitializeDllBlocklistOOPInternal(aFullImagePath, transferMgr, + aCachedNtdllThunk); +} + +LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher( + const wchar_t* aFullImagePath, HANDLE aChildProcess) { + nt::CrossExecTransferManager transferMgr(aChildProcess); + if (!transferMgr) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); + } + + // The launcher process initializes a section object, whose handle is + // transferred to the browser process, and that transferred handle in + // the browser process is transferred to the sandbox processes. + LauncherVoidResultWithLineInfo result = + freestanding::gSharedSection.Init(transferMgr.LocalPEHeaders()); + if (result.isErr()) { + return result.propagateErr(); + } + + // Transfer a writable handle to the main process because it needs to append + // dependent module paths to the section. + LauncherVoidResult transferResult = + freestanding::gSharedSection.TransferHandle(transferMgr, + GENERIC_READ | GENERIC_WRITE); + if (transferResult.isErr()) { + return transferResult.propagateErr(); + } + + auto clearInstance = MakeScopeExit([]() { + // After transfer, the launcher process does not need the object anymore. + freestanding::gSharedSection.Reset(nullptr); + }); + return InitializeDllBlocklistOOPInternal(aFullImagePath, transferMgr, + nullptr); +} + +#endif // defined(MOZ_ASAN) || defined(_M_ARM64) + +} // namespace mozilla diff --git a/browser/app/winlauncher/DllBlocklistInit.h b/browser/app/winlauncher/DllBlocklistInit.h new file mode 100644 index 0000000000..c6b87bce91 --- /dev/null +++ b/browser/app/winlauncher/DllBlocklistInit.h @@ -0,0 +1,25 @@ +/* -*- 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_DllBlocklistInit_h +#define mozilla_DllBlocklistInit_h + +#include <windows.h> + +#include "mozilla/WinHeaderOnlyUtils.h" + +namespace mozilla { + +LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP( + const wchar_t* aFullImagePath, HANDLE aChildProcess, + const IMAGE_THUNK_DATA* aCachedNtdllThunk); + +LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher( + const wchar_t* aFullImagePath, HANDLE aChildProcess); + +} // namespace mozilla + +#endif // mozilla_DllBlocklistInit_h diff --git a/browser/app/winlauncher/ErrorHandler.cpp b/browser/app/winlauncher/ErrorHandler.cpp new file mode 100644 index 0000000000..7b90b9c98b --- /dev/null +++ b/browser/app/winlauncher/ErrorHandler.cpp @@ -0,0 +1,781 @@ +/* -*- 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 "ErrorHandler.h" + +#include <utility> + +#include "mozilla/ArrayUtils.h" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/JSONWriter.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "mozilla/WinTokenUtils.h" +#include "mozilla/WindowsVersion.h" +#include "mozilla/XREAppData.h" +#include "mozilla/glue/WindowsDllServices.h" +#include "mozilla/mscom/ProcessRuntime.h" +#include "nsWindowsHelpers.h" + +#if defined(MOZ_LAUNCHER_PROCESS) +# include "mozilla/LauncherRegistryInfo.h" +#endif // defined(MOZ_LAUNCHER_PROCESS) + +#include <algorithm> +#include <process.h> +#include <sstream> +#include <string> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <objbase.h> +#include <rpc.h> +#if !defined(__MINGW32__) +# include <comutil.h> +# include <iwscapi.h> +# include <wscapi.h> +#endif // !defined(__MINGW32__) +#include <tlhelp32.h> + +#if !defined(RRF_SUBKEY_WOW6464KEY) +# define RRF_SUBKEY_WOW6464KEY 0x00010000 +#endif // !defined(RRF_SUBKEY_WOW6464KEY) + +#define QUOTE_ME2(x) #x +#define QUOTE_ME(x) QUOTE_ME2(x) + +#define TELEMETRY_BASE_URL L"https://incoming.telemetry.mozilla.org/submit" +#define TELEMETRY_NAMESPACE L"/firefox-launcher-process" +#define TELEMETRY_LAUNCHER_PING_DOCTYPE L"/launcher-process-failure" +#define TELEMETRY_LAUNCHER_PING_VERSION L"/1" + +static const wchar_t kUrl[] = TELEMETRY_BASE_URL TELEMETRY_NAMESPACE + TELEMETRY_LAUNCHER_PING_DOCTYPE TELEMETRY_LAUNCHER_PING_VERSION L"/"; +static const uint32_t kGuidCharLenWithNul = 39; +static const uint32_t kGuidCharLenNoBracesNoNul = 36; +static const mozilla::StaticXREAppData* gAppData; +static bool gForceEventLog = false; + +namespace { + +struct EventSourceDeleter { + using pointer = HANDLE; + + void operator()(pointer aEvtSrc) { ::DeregisterEventSource(aEvtSrc); } +}; + +using EventLog = mozilla::UniquePtr<HANDLE, EventSourceDeleter>; + +struct SerializedEventData { + HRESULT mHr; + uint32_t mLine; + char mFile[1]; +}; + +} // anonymous namespace + +static void PostErrorToLog(const mozilla::LauncherError& aError) { + // This is very bare-bones; just enough to spit out an HRESULT to the + // Application event log. + EventLog log(::RegisterEventSourceW(nullptr, L"Firefox")); + if (!log) { + return; + } + + size_t fileLen = strlen(aError.mFile); + size_t dataLen = sizeof(HRESULT) + sizeof(uint32_t) + fileLen; + auto evtDataBuf = mozilla::MakeUnique<char[]>(dataLen); + SerializedEventData& evtData = + *reinterpret_cast<SerializedEventData*>(evtDataBuf.get()); + evtData.mHr = aError.mError.AsHResult(); + evtData.mLine = aError.mLine; + // Since this is binary data, we're not concerning ourselves with null + // terminators. + memcpy(evtData.mFile, aError.mFile, fileLen); + + ::ReportEventW(log.get(), EVENTLOG_ERROR_TYPE, 0, aError.mError.AsHResult(), + nullptr, 0, dataLen, nullptr, evtDataBuf.get()); +} + +#if defined(MOZ_TELEMETRY_REPORTING) + +namespace { + +// This JSONWriteFunc writes directly to a temp file. By creating this file +// with the FILE_ATTRIBUTE_TEMPORARY attribute, we hint to the OS that this +// file is short-lived. The OS will try to avoid flushing it to disk if at +// all possible. +class TempFileWriter final : public mozilla::JSONWriteFunc { + public: + TempFileWriter() : mFailed(false), mSuccessfulHandoff(false) { + wchar_t name[MAX_PATH + 1] = {}; + if (_wtmpnam_s(name)) { + mFailed = true; + return; + } + + mTempFileName = name; + + mTempFile.own(::CreateFileW(name, GENERIC_WRITE, FILE_SHARE_READ, nullptr, + CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, nullptr)); + if (mTempFile.get() == INVALID_HANDLE_VALUE) { + mFailed = true; + } + } + + ~TempFileWriter() { + if (mSuccessfulHandoff) { + // It is no longer our responsibility to delete the temp file if we have + // successfully handed it off to pingsender. + return; + } + + mTempFile.reset(); + ::DeleteFileW(mTempFileName.c_str()); + } + + explicit operator bool() const { return !mFailed; } + + void Write(const mozilla::Span<const char>& aStr) override { + if (mFailed) { + return; + } + + DWORD bytesWritten = 0; + if (!::WriteFile(mTempFile, aStr.data(), aStr.size(), &bytesWritten, + nullptr) || + bytesWritten != aStr.size()) { + mFailed = true; + } + } + + const std::wstring& GetFileName() const { return mTempFileName; } + + void SetSuccessfulHandoff() { mSuccessfulHandoff = true; } + + private: + bool mFailed; + bool mSuccessfulHandoff; + std::wstring mTempFileName; + nsAutoHandle mTempFile; +}; + +using SigMap = mozilla::Vector<std::wstring, 0, InfallibleAllocPolicy>; + +} // anonymous namespace + +// This is the guideline for maximum string length for telemetry intake +static const size_t kMaxStrLen = 80; + +static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr, + const size_t aStrLenExclNul) { + // Yes, this might not handle surrogate pairs correctly. Let's just let + // WideCharToMultiByte fail in that unlikely case. + size_t cvtLen = std::min(aStrLenExclNul, kMaxStrLen); + + int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, nullptr, 0, + nullptr, nullptr); + if (!numConv) { + return nullptr; + } + + // Include room for the null terminator by adding one + auto buf = mozilla::MakeUnique<char[]>(numConv + 1); + + numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, buf.get(), numConv, + nullptr, nullptr); + if (!numConv) { + return nullptr; + } + + // Add null termination. numConv does not include the terminator, so we don't + // subtract 1 when indexing into buf. + buf[numConv] = 0; + + return buf; +} + +static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr) { + return WideToUTF8(aStr, wcslen(aStr)); +} + +static mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr) { + return WideToUTF8(aStr.c_str(), aStr.length()); +} + +// MinGW does not support the Windows Security Center APIs. +# if !defined(__MINGW32__) + +static mozilla::UniquePtr<char[]> WideToUTF8(const _bstr_t& aStr) { + return WideToUTF8(static_cast<const wchar_t*>(aStr), aStr.length()); +} + +namespace { + +struct ProviderKey { + WSC_SECURITY_PROVIDER mProviderType; + const char* mKey; +}; + +} // anonymous namespace + +static bool EnumWSCProductList(RefPtr<IWSCProductList>& aProdList, + mozilla::JSONWriter& aJson) { + LONG count; + HRESULT hr = aProdList->get_Count(&count); + if (FAILED(hr)) { + return false; + } + + // Unlikely, but put a bound on the max length of the output array for the + // purposes of telemetry intake. + count = std::min(count, 1000L); + + // Record the name(s) of each active registered product in this category + for (LONG index = 0; index < count; ++index) { + RefPtr<IWscProduct> product; + hr = aProdList->get_Item(index, getter_AddRefs(product)); + if (FAILED(hr)) { + return false; + } + + WSC_SECURITY_PRODUCT_STATE state; + hr = product->get_ProductState(&state); + if (FAILED(hr)) { + return false; + } + + // We only care about products that are active + if (state == WSC_SECURITY_PRODUCT_STATE_OFF || + state == WSC_SECURITY_PRODUCT_STATE_SNOOZED || + state == WSC_SECURITY_PRODUCT_STATE_EXPIRED) { + continue; + } + + _bstr_t bName; + hr = product->get_ProductName(bName.GetAddress()); + if (FAILED(hr)) { + return false; + } + + auto buf = WideToUTF8(bName); + if (!buf) { + return false; + } + + aJson.StringElement(mozilla::MakeStringSpan(buf.get())); + } + + return true; +} + +static const ProviderKey gProvKeys[] = { + {WSC_SECURITY_PROVIDER_ANTIVIRUS, "av"}, + {WSC_SECURITY_PROVIDER_ANTISPYWARE, "antispyware"}, + {WSC_SECURITY_PROVIDER_FIREWALL, "firewall"}}; + +static bool AddWscInfo(mozilla::JSONWriter& aJson) { + if (!mozilla::IsWin8OrLater()) { + // We haven't written anything yet, so we can return true here and continue + // capturing data. + return true; + } + + // We need COM for this. Using ProcessRuntime so that process-global COM + // configuration is done correctly + mozilla::mscom::ProcessRuntime mscom( + mozilla::mscom::ProcessRuntime::ProcessCategory::Launcher); + if (!mscom) { + // We haven't written anything yet, so we can return true here and continue + // capturing data. + return true; + } + + aJson.StartObjectProperty("security"); + + const CLSID clsid = __uuidof(WSCProductList); + const IID iid = __uuidof(IWSCProductList); + + for (uint32_t index = 0; index < mozilla::ArrayLength(gProvKeys); ++index) { + // NB: A separate instance of IWSCProductList is needed for each distinct + // security provider type; MSDN says that we cannot reuse the same object + // and call Initialize() to pave over the previous data. + RefPtr<IWSCProductList> prodList; + HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid, + getter_AddRefs(prodList)); + if (FAILED(hr)) { + return false; + } + + hr = prodList->Initialize(gProvKeys[index].mProviderType); + if (FAILED(hr)) { + return false; + } + + aJson.StartArrayProperty(mozilla::MakeStringSpan(gProvKeys[index].mKey)); + + if (!EnumWSCProductList(prodList, aJson)) { + return false; + } + + aJson.EndArray(); + } + + aJson.EndObject(); + + return true; +} +# endif // !defined(__MINGW32__) + +// Max array length for telemetry intake. +static const size_t kMaxArrayLen = 1000; + +static bool AddModuleInfo(const nsAutoHandle& aSnapshot, + mozilla::JSONWriter& aJson) { + if (aSnapshot.get() == INVALID_HANDLE_VALUE) { + // We haven't written anything yet, so we can return true here and continue + // capturing data. + return true; + } + + SigMap signatures; + size_t moduleCount = 0; + + MODULEENTRY32W module = {sizeof(module)}; + if (!::Module32FirstW(aSnapshot, &module)) { + // We haven't written anything yet, so we can return true here and continue + // capturing data. + return true; + } + + mozilla::glue::BasicDllServices dllServices; + + aJson.StartObjectProperty("modules"); + + // For each module, add its version number (or empty string if not present), + // followed by an optional index into the signatures array + do { + ++moduleCount; + + wchar_t leaf[_MAX_FNAME] = {}; + if (::_wsplitpath_s(module.szExePath, nullptr, 0, nullptr, 0, leaf, + mozilla::ArrayLength(leaf), nullptr, 0)) { + return false; + } + + if (_wcslwr_s(leaf, mozilla::ArrayLength(leaf))) { + return false; + } + + auto leafUtf8 = WideToUTF8(leaf); + if (!leafUtf8) { + return false; + } + + aJson.StartArrayProperty(mozilla::MakeStringSpan(leafUtf8.get())); + + std::string version; + DWORD verInfoSize = ::GetFileVersionInfoSizeW(module.szExePath, nullptr); + if (verInfoSize) { + auto verInfoBuf = mozilla::MakeUnique<BYTE[]>(verInfoSize); + + if (::GetFileVersionInfoW(module.szExePath, 0, verInfoSize, + verInfoBuf.get())) { + VS_FIXEDFILEINFO* fixedInfo = nullptr; + UINT fixedInfoLen = 0; + + if (::VerQueryValueW(verInfoBuf.get(), L"\\", + reinterpret_cast<LPVOID*>(&fixedInfo), + &fixedInfoLen)) { + std::ostringstream oss; + oss << HIWORD(fixedInfo->dwFileVersionMS) << '.' + << LOWORD(fixedInfo->dwFileVersionMS) << '.' + << HIWORD(fixedInfo->dwFileVersionLS) << '.' + << LOWORD(fixedInfo->dwFileVersionLS); + version = oss.str(); + } + } + } + + aJson.StringElement(version); + + mozilla::Maybe<ptrdiff_t> sigIndex; + auto signedBy = dllServices.GetBinaryOrgName(module.szExePath); + if (signedBy) { + std::wstring strSignedBy(signedBy.get()); + auto entry = std::find(signatures.begin(), signatures.end(), strSignedBy); + if (entry == signatures.end()) { + mozilla::Unused << signatures.append(std::move(strSignedBy)); + entry = &signatures.back(); + } + + sigIndex = mozilla::Some(entry - signatures.begin()); + } + + if (sigIndex) { + aJson.IntElement(sigIndex.value()); + } + + aJson.EndArray(); + } while (moduleCount < kMaxArrayLen && ::Module32NextW(aSnapshot, &module)); + + aJson.EndObject(); + + aJson.StartArrayProperty("signatures"); + + // Serialize each entry in the signatures array + for (auto&& itr : signatures) { + auto sigUtf8 = WideToUTF8(itr); + if (!sigUtf8) { + continue; + } + + aJson.StringElement(mozilla::MakeStringSpan(sigUtf8.get())); + } + + aJson.EndArray(); + + return true; +} + +namespace { + +struct PingThreadContext { + explicit PingThreadContext(const mozilla::LauncherError& aError, + const char* aProcessType) + : mLauncherError(aError), + mModulesSnapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)), + mProcessType(aProcessType ? aProcessType : "") {} + mozilla::LauncherError mLauncherError; + nsAutoHandle mModulesSnapshot; + std::string mProcessType; +}; + +} // anonymous namespace + +static bool PrepPing(const PingThreadContext& aContext, const std::wstring& aId, + mozilla::JSONWriter& aJson) { +# if defined(DEBUG) + const mozilla::JSONWriter::CollectionStyle style = + mozilla::JSONWriter::MultiLineStyle; +# else + const mozilla::JSONWriter::CollectionStyle style = + mozilla::JSONWriter::SingleLineStyle; +# endif // defined(DEBUG) + + aJson.Start(style); + + aJson.StringProperty("type", "launcher-process-failure"); + aJson.IntProperty("version", 1); + + auto idUtf8 = WideToUTF8(aId); + if (idUtf8) { + aJson.StringProperty("id", mozilla::MakeStringSpan(idUtf8.get())); + } + + time_t now; + time(&now); + tm gmTm; + if (!gmtime_s(&gmTm, &now)) { + char isoTimeBuf[32] = {}; + if (strftime(isoTimeBuf, mozilla::ArrayLength(isoTimeBuf), "%FT%T.000Z", + &gmTm)) { + aJson.StringProperty("creationDate", isoTimeBuf); + } + } + + aJson.StringProperty("update_channel", QUOTE_ME(MOZ_UPDATE_CHANNEL)); + + if (gAppData) { + aJson.StringProperty("build_id", + mozilla::MakeStringSpan(gAppData->buildID)); + aJson.StringProperty("build_version", + mozilla::MakeStringSpan(gAppData->version)); + } + + OSVERSIONINFOEXW osv = {sizeof(osv)}; + if (::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&osv))) { + std::ostringstream oss; + oss << osv.dwMajorVersion << "." << osv.dwMinorVersion << "." + << osv.dwBuildNumber; + + if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0) { + // Get the "Update Build Revision" (UBR) value + DWORD ubrValue; + DWORD ubrValueLen = sizeof(ubrValue); + LSTATUS ubrOk = + ::RegGetValueW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + L"UBR", RRF_RT_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, + &ubrValue, &ubrValueLen); + if (ubrOk == ERROR_SUCCESS) { + oss << "." << ubrValue; + } + } + + if (oss) { + aJson.StringProperty("os_version", oss.str()); + } + + bool isServer = osv.wProductType == VER_NT_DOMAIN_CONTROLLER || + osv.wProductType == VER_NT_SERVER; + aJson.BoolProperty("server_os", isServer); + } + + WCHAR localeName[LOCALE_NAME_MAX_LENGTH] = {}; + int localeNameLen = + ::GetUserDefaultLocaleName(localeName, mozilla::ArrayLength(localeName)); + if (localeNameLen) { + auto localeNameUtf8 = WideToUTF8(localeName, localeNameLen - 1); + if (localeNameUtf8) { + aJson.StringProperty("os_locale", + mozilla::MakeStringSpan(localeNameUtf8.get())); + } + } + + SYSTEM_INFO sysInfo; + ::GetNativeSystemInfo(&sysInfo); + aJson.IntProperty("cpu_arch", sysInfo.wProcessorArchitecture); + aJson.IntProperty("num_logical_cpus", sysInfo.dwNumberOfProcessors); + + mozilla::LauncherResult<bool> isAdminWithoutUac = + mozilla::IsAdminWithoutUac(); + if (isAdminWithoutUac.isOk()) { + aJson.BoolProperty("is_admin_without_uac", isAdminWithoutUac.unwrap()); + } + + if (!aContext.mProcessType.empty()) { + aJson.StringProperty("process_type", aContext.mProcessType); + } + + MEMORYSTATUSEX memStatus = {sizeof(memStatus)}; + if (::GlobalMemoryStatusEx(&memStatus)) { + aJson.StartObjectProperty("memory"); + aJson.IntProperty("total_phys", memStatus.ullTotalPhys); + aJson.IntProperty("avail_phys", memStatus.ullAvailPhys); + aJson.IntProperty("avail_page_file", memStatus.ullAvailPageFile); + aJson.IntProperty("avail_virt", memStatus.ullAvailVirtual); + aJson.EndObject(); + } + + aJson.StringProperty("xpcom_abi", TARGET_XPCOM_ABI); + + aJson.StartObjectProperty("launcher_error", style); + + std::string srcFileLeaf(aContext.mLauncherError.mFile); + // Obtain the leaf name of the file for privacy reasons + // (In case this is somebody's local build) + auto pos = srcFileLeaf.find_last_of("/\\"); + if (pos != std::string::npos) { + srcFileLeaf = srcFileLeaf.substr(pos + 1); + } + + aJson.StringProperty("source_file", srcFileLeaf); + + aJson.IntProperty("source_line", aContext.mLauncherError.mLine); + aJson.IntProperty("hresult", aContext.mLauncherError.mError.AsHResult()); + +# if defined(NIGHTLY_BUILD) + if (aContext.mLauncherError.mDetourError.isSome()) { + static const char* kHexMap = "0123456789abcdef"; + char hexStr[sizeof(mozilla::DetourError::mOrigBytes) * 2 + 1]; + int cnt = 0; + for (uint8_t byte : aContext.mLauncherError.mDetourError->mOrigBytes) { + hexStr[cnt++] = kHexMap[(byte >> 4) & 0x0f]; + hexStr[cnt++] = kHexMap[byte & 0x0f]; + } + hexStr[cnt] = 0; + aJson.StringProperty("detour_orig_bytes", hexStr); + } +# endif // defined(NIGHTLY_BUILD) + + aJson.EndObject(); + +# if !defined(__MINGW32__) + if (!AddWscInfo(aJson)) { + return false; + } +# endif // !defined(__MINGW32__) + + if (!AddModuleInfo(aContext.mModulesSnapshot, aJson)) { + return false; + } + + aJson.End(); + + return true; +} + +static bool DoSendPing(const PingThreadContext& aContext) { + auto writeFunc = mozilla::MakeUnique<TempFileWriter>(); + if (!(*writeFunc)) { + return false; + } + + mozilla::JSONWriter json(std::move(writeFunc)); + + UUID uuid; + if (::UuidCreate(&uuid) != RPC_S_OK) { + return false; + } + + wchar_t guidBuf[kGuidCharLenWithNul] = {}; + if (::StringFromGUID2(uuid, guidBuf, kGuidCharLenWithNul) != + kGuidCharLenWithNul) { + return false; + } + + // Strip the curly braces off of the guid + std::wstring guidNoBraces(guidBuf + 1, kGuidCharLenNoBracesNoNul); + + // Populate json with the ping information + if (!PrepPing(aContext, guidNoBraces, json)) { + return false; + } + + // Obtain the name of the temp file that we have written + TempFileWriter& tempFile = *static_cast<TempFileWriter*>(json.WriteFunc()); + if (!tempFile) { + return false; + } + + const std::wstring& fileName = tempFile.GetFileName(); + + // Using the path to our executable binary, construct the path to + // pingsender.exe + mozilla::UniquePtr<wchar_t[]> exePath(mozilla::GetFullBinaryPath()); + + wchar_t drive[_MAX_DRIVE] = {}; + wchar_t dir[_MAX_DIR] = {}; + if (_wsplitpath_s(exePath.get(), drive, mozilla::ArrayLength(drive), dir, + mozilla::ArrayLength(dir), nullptr, 0, nullptr, 0)) { + return false; + } + + wchar_t pingSenderPath[MAX_PATH + 1] = {}; + if (_wmakepath_s(pingSenderPath, mozilla::ArrayLength(pingSenderPath), drive, + dir, L"pingsender", L"exe")) { + return false; + } + + // Construct the telemetry URL + wchar_t urlBuf[mozilla::ArrayLength(kUrl) + kGuidCharLenNoBracesNoNul] = {}; + if (wcscpy_s(urlBuf, kUrl)) { + return false; + } + + if (wcscat_s(urlBuf, guidNoBraces.c_str())) { + return false; + } + + // Now build the command line arguments to pingsender + wchar_t* pingSenderArgv[] = {pingSenderPath, urlBuf, + const_cast<wchar_t*>(fileName.c_str())}; + + mozilla::UniquePtr<wchar_t[]> pingSenderCmdLine(mozilla::MakeCommandLine( + mozilla::ArrayLength(pingSenderArgv), pingSenderArgv)); + + // Now start pingsender to handle the rest + PROCESS_INFORMATION pi; + + STARTUPINFOW si = {sizeof(si)}; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + if (!::CreateProcessW(pingSenderPath, pingSenderCmdLine.get(), nullptr, + nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + return false; + } + + tempFile.SetSuccessfulHandoff(); + + nsAutoHandle proc(pi.hProcess); + nsAutoHandle thread(pi.hThread); + + return true; +} + +static unsigned __stdcall SendPingThread(void* aContext) { + mozilla::UniquePtr<PingThreadContext> context( + reinterpret_cast<PingThreadContext*>(aContext)); + + if (!DoSendPing(*context) || gForceEventLog) { + PostErrorToLog(context->mLauncherError); + } + + return 0; +} + +#endif // defined(MOZ_TELEMETRY_REPORTING) + +static bool SendPing(const mozilla::LauncherError& aError, + const char* aProcessType) { +#if defined(MOZ_TELEMETRY_REPORTING) +# if defined(MOZ_LAUNCHER_PROCESS) + mozilla::LauncherRegistryInfo regInfo; + mozilla::LauncherResult<bool> telemetryEnabled = regInfo.IsTelemetryEnabled(); + if (telemetryEnabled.isErr() || !telemetryEnabled.unwrap()) { + // Do not send anything if telemetry has been opted out + return false; + } +# endif // defined(MOZ_LAUNCHER_PROCESS) + + // We send this ping when the launcher process fails. After we start the + // SendPingThread, this thread falls back from running as the launcher process + // to running as the browser main thread. Once this happens, it will be unsafe + // to set up PoisonIOInterposer (since we have already spun up a background + // thread). + mozilla::SaveToEnv("MOZ_DISABLE_POISON_IO_INTERPOSER=1"); + + // Capture aError and our module list into context for processing on another + // thread. + auto thdParam = mozilla::MakeUnique<PingThreadContext>(aError, aProcessType); + + // The ping does a lot of file I/O. Since we want this thread to continue + // executing browser startup, we should gather that information on a + // background thread. + uintptr_t thdHandle = + _beginthreadex(nullptr, 0, &SendPingThread, thdParam.get(), + STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr); + if (!thdHandle) { + return false; + } + + // We have handed off thdParam to the background thread + mozilla::Unused << thdParam.release(); + + ::CloseHandle(reinterpret_cast<HANDLE>(thdHandle)); + return true; +#else + return false; +#endif +} + +namespace mozilla { + +void HandleLauncherError(const LauncherError& aError, + const char* aProcessType) { +#if defined(MOZ_LAUNCHER_PROCESS) + LauncherRegistryInfo regInfo; + Unused << regInfo.DisableDueToFailure(); +#endif // defined(MOZ_LAUNCHER_PROCESS) + + if (SendPing(aError, aProcessType) && !gForceEventLog) { + return; + } + + PostErrorToLog(aError); +} + +void SetLauncherErrorAppData(const StaticXREAppData& aAppData) { + gAppData = &aAppData; +} + +void SetLauncherErrorForceEventLog() { gForceEventLog = true; } + +} // namespace mozilla diff --git a/browser/app/winlauncher/ErrorHandler.h b/browser/app/winlauncher/ErrorHandler.h new file mode 100644 index 0000000000..71fe72cf07 --- /dev/null +++ b/browser/app/winlauncher/ErrorHandler.h @@ -0,0 +1,54 @@ +/* -*- 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_ErrorHandler_h +#define mozilla_ErrorHandler_h + +#include "mozilla/Assertions.h" +#include "mozilla/WinHeaderOnlyUtils.h" + +namespace mozilla { + +/** + * All launcher process error handling should live in the implementation of + * this function. + */ +void HandleLauncherError(const LauncherError& aError, + const char* aProcessType = nullptr); + +// This function is simply a convenience overload that automatically unwraps +// the LauncherError from the provided LauncherResult and then forwards it to +// the main implementation. +template <typename T> +inline void HandleLauncherError(const LauncherResult<T>& aResult, + const char* aProcessType = nullptr) { + MOZ_ASSERT(aResult.isErr()); + if (aResult.isOk()) { + return; + } + + HandleLauncherError(aResult.inspectErr(), aProcessType); +} + +// This function is simply a convenience overload that unwraps the provided +// GenericErrorResult<LauncherError> and forwards it to the main implementation. +inline void HandleLauncherError( + const GenericErrorResult<LauncherError>& aResult, + const char* aProcessType = nullptr) { + LauncherVoidResult r(aResult); + HandleLauncherError(r, aProcessType); +} + +// Forward declaration +struct StaticXREAppData; + +void SetLauncherErrorAppData(const StaticXREAppData& aAppData); + +void SetLauncherErrorForceEventLog(); + +} // namespace mozilla + +#endif // mozilla_ErrorHandler_h diff --git a/browser/app/winlauncher/LaunchUnelevated.cpp b/browser/app/winlauncher/LaunchUnelevated.cpp new file mode 100644 index 0000000000..41f0d377f3 --- /dev/null +++ b/browser/app/winlauncher/LaunchUnelevated.cpp @@ -0,0 +1,243 @@ +/* -*- 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/. */ + +#define MOZ_USE_LAUNCHER_ERROR + +#include "LaunchUnelevated.h" + +#include "mozilla/Assertions.h" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/mscom/ProcessRuntime.h" +#include "mozilla/RefPtr.h" +#include "mozilla/ShellHeaderOnlyUtils.h" +#include "mozilla/WinHeaderOnlyUtils.h" +#include "nsWindowsHelpers.h" + +#include <windows.h> + +static mozilla::LauncherResult<bool> IsHighIntegrity( + const nsAutoHandle& aToken) { + DWORD reqdLen; + if (!::GetTokenInformation(aToken.get(), TokenIntegrityLevel, nullptr, 0, + &reqdLen)) { + DWORD err = ::GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) { + return LAUNCHER_ERROR_FROM_WIN32(err); + } + } + + auto buf = mozilla::MakeUnique<char[]>(reqdLen); + + if (!::GetTokenInformation(aToken.get(), TokenIntegrityLevel, buf.get(), + reqdLen, &reqdLen)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + auto tokenLabel = reinterpret_cast<PTOKEN_MANDATORY_LABEL>(buf.get()); + + DWORD subAuthCount = *::GetSidSubAuthorityCount(tokenLabel->Label.Sid); + DWORD integrityLevel = + *::GetSidSubAuthority(tokenLabel->Label.Sid, subAuthCount - 1); + return integrityLevel > SECURITY_MANDATORY_MEDIUM_RID; +} + +static mozilla::LauncherResult<HANDLE> GetMediumIntegrityToken( + const nsAutoHandle& aProcessToken) { + HANDLE rawResult; + if (!::DuplicateTokenEx(aProcessToken.get(), 0, nullptr, + SecurityImpersonation, TokenPrimary, &rawResult)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + nsAutoHandle result(rawResult); + + BYTE mediumIlSid[SECURITY_MAX_SID_SIZE]; + DWORD mediumIlSidSize = sizeof(mediumIlSid); + if (!::CreateWellKnownSid(WinMediumLabelSid, nullptr, mediumIlSid, + &mediumIlSidSize)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + TOKEN_MANDATORY_LABEL integrityLevel = {}; + integrityLevel.Label.Attributes = SE_GROUP_INTEGRITY; + integrityLevel.Label.Sid = reinterpret_cast<PSID>(mediumIlSid); + + if (!::SetTokenInformation(rawResult, TokenIntegrityLevel, &integrityLevel, + sizeof(integrityLevel))) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + return result.disown(); +} + +static mozilla::LauncherResult<bool> IsAdminByAppCompat( + HKEY aRootKey, const wchar_t* aExecutablePath) { + static const wchar_t kPathToLayers[] = + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\" + L"AppCompatFlags\\Layers"; + + DWORD dataLength = 0; + LSTATUS status = ::RegGetValueW(aRootKey, kPathToLayers, aExecutablePath, + RRF_RT_REG_SZ | RRF_SUBKEY_WOW6464KEY, + nullptr, nullptr, &dataLength); + if (status == ERROR_FILE_NOT_FOUND) { + return false; + } else if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); + } + + auto valueData = mozilla::MakeUnique<wchar_t[]>(dataLength); + if (!valueData) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_OUTOFMEMORY); + } + + status = ::RegGetValueW(aRootKey, kPathToLayers, aExecutablePath, + RRF_RT_REG_SZ | RRF_SUBKEY_WOW6464KEY, nullptr, + valueData.get(), &dataLength); + if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); + } + + const wchar_t kRunAsAdmin[] = L"RUNASADMIN"; + const wchar_t kDelimiters[] = L" "; + wchar_t* tokenContext = nullptr; + const wchar_t* token = wcstok_s(valueData.get(), kDelimiters, &tokenContext); + while (token) { + if (!_wcsnicmp(token, kRunAsAdmin, mozilla::ArrayLength(kRunAsAdmin))) { + return true; + } + token = wcstok_s(nullptr, kDelimiters, &tokenContext); + } + + return false; +} + +namespace mozilla { + +// If we're running at an elevated integrity level, re-run ourselves at the +// user's normal integrity level. We do this by locating the active explorer +// shell, and then asking it to do a ShellExecute on our behalf. We do it this +// way to ensure that the child process runs as the original user in the active +// session; an elevated process could be running with different credentials than +// those of the session. +// See https://blogs.msdn.microsoft.com/oldnewthing/20131118-00/?p=2643 + +LauncherVoidResult LaunchUnelevated(int aArgc, wchar_t* aArgv[]) { + // We need COM to talk to Explorer. Using ProcessRuntime so that + // process-global COM configuration is done correctly + mozilla::mscom::ProcessRuntime mscom(GeckoProcessType_Default); + if (!mscom) { + return LAUNCHER_ERROR_FROM_HRESULT(mscom.GetHResult()); + } + + // Omit argv[0] because ShellExecute doesn't need it in params + UniquePtr<wchar_t[]> cmdLine(MakeCommandLine(aArgc - 1, aArgv + 1)); + if (!cmdLine) { + return LAUNCHER_ERROR_GENERIC(); + } + + _bstr_t exe(aArgv[0]); + _variant_t args(cmdLine.get()); + _variant_t operation(L"open"); + _variant_t directory; + _variant_t showCmd(SW_SHOWNORMAL); + return ShellExecuteByExplorer(exe, args, operation, directory, showCmd); +} + +LauncherResult<ElevationState> GetElevationState( + const wchar_t* aExecutablePath, mozilla::LauncherFlags aFlags, + nsAutoHandle& aOutMediumIlToken) { + aOutMediumIlToken.reset(); + + const DWORD tokenFlags = TOKEN_QUERY | TOKEN_DUPLICATE | + TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY; + HANDLE rawToken; + if (!::OpenProcessToken(::GetCurrentProcess(), tokenFlags, &rawToken)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + nsAutoHandle token(rawToken); + + LauncherResult<TOKEN_ELEVATION_TYPE> elevationType = GetElevationType(token); + if (elevationType.isErr()) { + return elevationType.propagateErr(); + } + + Maybe<ElevationState> elevationState; + switch (elevationType.unwrap()) { + case TokenElevationTypeLimited: + return ElevationState::eNormalUser; + case TokenElevationTypeFull: + elevationState = Some(ElevationState::eElevated); + break; + case TokenElevationTypeDefault: { + // In this case, UAC is disabled. We do not yet know whether or not we + // are running at high integrity. If we are at high integrity, we can't + // relaunch ourselves in a non-elevated state via Explorer, as we would + // just end up in an infinite loop of launcher processes re-launching + // themselves. + LauncherResult<bool> isHighIntegrity = IsHighIntegrity(token); + if (isHighIntegrity.isErr()) { + return isHighIntegrity.propagateErr(); + } + + if (!isHighIntegrity.unwrap()) { + return ElevationState::eNormalUser; + } + + elevationState = Some(ElevationState::eHighIntegrityNoUAC); + break; + } + default: + MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?"); + return LAUNCHER_ERROR_GENERIC(); + } + + MOZ_ASSERT(elevationState.isSome() && + elevationState.value() != ElevationState::eNormalUser, + "Should have returned earlier for the eNormalUser case."); + + LauncherResult<bool> isAdminByAppCompat = + IsAdminByAppCompat(HKEY_CURRENT_USER, aExecutablePath); + if (isAdminByAppCompat.isErr()) { + return isAdminByAppCompat.propagateErr(); + } + + if (isAdminByAppCompat.unwrap()) { + elevationState = Some(ElevationState::eHighIntegrityByAppCompat); + } else { + isAdminByAppCompat = + IsAdminByAppCompat(HKEY_LOCAL_MACHINE, aExecutablePath); + if (isAdminByAppCompat.isErr()) { + return isAdminByAppCompat.propagateErr(); + } + + if (isAdminByAppCompat.unwrap()) { + elevationState = Some(ElevationState::eHighIntegrityByAppCompat); + } + } + + // A medium IL token is not needed in the following cases. + // 1) We keep the process elevated (= LauncherFlags::eNoDeelevate) + // 2) The process was elevated by UAC (= ElevationState::eElevated) + // AND the launcher process doesn't wait for the browser process + if ((aFlags & mozilla::LauncherFlags::eNoDeelevate) || + (elevationState.value() == ElevationState::eElevated && + !(aFlags & mozilla::LauncherFlags::eWaitForBrowser))) { + return elevationState.value(); + } + + LauncherResult<HANDLE> tokenResult = GetMediumIntegrityToken(token); + if (tokenResult.isOk()) { + aOutMediumIlToken.own(tokenResult.unwrap()); + } else { + return tokenResult.propagateErr(); + } + + return elevationState.value(); +} + +} // namespace mozilla diff --git a/browser/app/winlauncher/LaunchUnelevated.h b/browser/app/winlauncher/LaunchUnelevated.h new file mode 100644 index 0000000000..8c6679edf3 --- /dev/null +++ b/browser/app/winlauncher/LaunchUnelevated.h @@ -0,0 +1,32 @@ +/* -*- 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_LaunchUnelevated_h +#define mozilla_LaunchUnelevated_h + +#include "LauncherProcessWin.h" +#include "mozilla/WinHeaderOnlyUtils.h" +#include "mozilla/Maybe.h" +#include "nsWindowsHelpers.h" + +namespace mozilla { + +enum class ElevationState { + eNormalUser = 0, + eElevated = (1 << 0), + eHighIntegrityNoUAC = (1 << 1), + eHighIntegrityByAppCompat = (1 << 2), +}; + +LauncherResult<ElevationState> GetElevationState( + const wchar_t* aExecutablePath, LauncherFlags aFlags, + nsAutoHandle& aOutMediumIlToken); + +LauncherVoidResult LaunchUnelevated(int aArgc, wchar_t* aArgv[]); + +} // namespace mozilla + +#endif // mozilla_LaunchUnelevated_h diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp new file mode 100644 index 0000000000..faf53eff9a --- /dev/null +++ b/browser/app/winlauncher/LauncherProcessWin.cpp @@ -0,0 +1,412 @@ +/* -*- 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/. */ + +#define MOZ_USE_LAUNCHER_ERROR + +#include "LauncherProcessWin.h" + +#include <string.h> + +#include "mozilla/Attributes.h" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/DynamicallyLinkedFunctionPtr.h" +#include "mozilla/glue/Debug.h" +#include "mozilla/Maybe.h" +#include "mozilla/SafeMode.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/WindowsConsole.h" +#include "mozilla/WindowsVersion.h" +#include "mozilla/WinHeaderOnlyUtils.h" +#include "nsWindowsHelpers.h" + +#include <windows.h> +#include <processthreadsapi.h> + +#include "DllBlocklistInit.h" +#include "ErrorHandler.h" +#include "LaunchUnelevated.h" +#include "ProcThreadAttributes.h" + +#if defined(MOZ_LAUNCHER_PROCESS) +# include "mozilla/LauncherRegistryInfo.h" +# include "SameBinary.h" +#endif // defined(MOZ_LAUNCHER_PROCESS) + +/** + * At this point the child process has been created in a suspended state. Any + * additional startup work (eg, blocklist setup) should go here. + * + * @return Ok if browser startup should proceed + */ +static mozilla::LauncherVoidResult PostCreationSetup( + const wchar_t* aFullImagePath, HANDLE aChildProcess, + HANDLE aChildMainThread, const bool aIsSafeMode) { + return mozilla::InitializeDllBlocklistOOPFromLauncher(aFullImagePath, + aChildProcess); +} + +#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) + +#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) + +#if (_WIN32_WINNT < 0x0602) +BOOL WINAPI +SetProcessMitigationPolicy(PROCESS_MITIGATION_POLICY aMitigationPolicy, + PVOID aBuffer, SIZE_T aBufferLen); +#endif // (_WIN32_WINNT >= 0x0602) + +/** + * Any mitigation policies that should be set on the browser process should go + * here. + */ +static void SetMitigationPolicies(mozilla::ProcThreadAttributes& aAttrs, + const bool aIsSafeMode) { + if (mozilla::IsWin10AnniversaryUpdateOrLater()) { + aAttrs.AddMitigationPolicy( + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON); + } + +#if defined(_M_ARM64) + // Disable CFG on older versions of ARM64 Windows to avoid a crash in COM. + if (!mozilla::IsWin10Sep2018UpdateOrLater()) { + aAttrs.AddMitigationPolicy( + PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF); + } +#endif // defined(_M_ARM64) +} + +static mozilla::LauncherFlags ProcessCmdLine(int& aArgc, wchar_t* aArgv[]) { + mozilla::LauncherFlags result = mozilla::LauncherFlags::eNone; + + if (mozilla::CheckArg(aArgc, aArgv, L"wait-for-browser", + static_cast<const wchar_t**>(nullptr), + mozilla::CheckArgFlag::RemoveArg) == + mozilla::ARG_FOUND || + mozilla::CheckArg(aArgc, aArgv, L"marionette", + static_cast<const wchar_t**>(nullptr), + mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND || + mozilla::CheckArg(aArgc, aArgv, L"headless", + static_cast<const wchar_t**>(nullptr), + mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND || + mozilla::EnvHasValue("MOZ_AUTOMATION") || + mozilla::EnvHasValue("MOZ_HEADLESS")) { + result |= mozilla::LauncherFlags::eWaitForBrowser; + } + + if (mozilla::CheckArg(aArgc, aArgv, L"no-deelevate") == mozilla::ARG_FOUND) { + result |= mozilla::LauncherFlags::eNoDeelevate; + } + + return result; +} + +static void MaybeBreakForBrowserDebugging() { + if (mozilla::EnvHasValue("MOZ_DEBUG_BROWSER_PROCESS")) { + ::DebugBreak(); + return; + } + + const wchar_t* pauseLenS = _wgetenv(L"MOZ_DEBUG_BROWSER_PAUSE"); + if (!pauseLenS || !(*pauseLenS)) { + return; + } + + DWORD pauseLenMs = wcstoul(pauseLenS, nullptr, 10) * 1000; + printf_stderr("\n\nBROWSERBROWSERBROWSERBROWSER\n debug me @ %lu\n\n", + ::GetCurrentProcessId()); + ::Sleep(pauseLenMs); +} + +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) + // We still prefer to compare file ids. Comparing NT paths i.e. passing + // CompareNtPathsOnly to IsSameBinaryAsParentProcess is much faster, but + // we're not 100% sure that NT path comparison perfectly prevents the + // launching loop of the launcher process. + mozilla::LauncherResult<bool> isSame = mozilla::IsSameBinaryAsParentProcess(); + if (isSame.isOk()) { + result = !isSame.unwrap(); + } else { + HandleLauncherError(isSame.unwrapErr()); + } +#endif // defined(MOZ_LAUNCHER_PROCESS) + + if (mozilla::EnvHasValue("MOZ_LAUNCHER_PROCESS")) { + mozilla::SaveToEnv("MOZ_LAUNCHER_PROCESS="); + result = true; + } + + result |= mozilla::CheckArg( + argc, argv, L"launcher", static_cast<const wchar_t**>(nullptr), + mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND; + + return result; +} + +#if defined(MOZ_LAUNCHER_PROCESS) +static mozilla::Maybe<bool> RunAsLauncherProcess( + mozilla::LauncherRegistryInfo& aRegInfo, int& argc, wchar_t** argv) { +#else +static mozilla::Maybe<bool> RunAsLauncherProcess(int& argc, wchar_t** argv) { +#endif // defined(MOZ_LAUNCHER_PROCESS) + // return fast when we're a child process. + // (The remainder of this function has some side effects that are + // undesirable for content processes) + if (mozilla::CheckArg(argc, argv, L"contentproc", + static_cast<const wchar_t**>(nullptr), + mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND) { + return mozilla::Some(false); + } + + bool runAsLauncher = DoLauncherProcessChecks(argc, argv); + +#if defined(MOZ_LAUNCHER_PROCESS) + bool forceLauncher = + runAsLauncher && + mozilla::CheckArg(argc, argv, L"force-launcher", + static_cast<const wchar_t**>(nullptr), + mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND; + + mozilla::LauncherRegistryInfo::ProcessType desiredType = + runAsLauncher ? mozilla::LauncherRegistryInfo::ProcessType::Launcher + : mozilla::LauncherRegistryInfo::ProcessType::Browser; + + mozilla::LauncherRegistryInfo::CheckOption checkOption = + forceLauncher ? mozilla::LauncherRegistryInfo::CheckOption::Force + : mozilla::LauncherRegistryInfo::CheckOption::Default; + + mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> + runAsType = aRegInfo.Check(desiredType, checkOption); + + if (runAsType.isErr()) { + mozilla::HandleLauncherError(runAsType); + return mozilla::Nothing(); + } + + runAsLauncher = runAsType.unwrap() == + mozilla::LauncherRegistryInfo::ProcessType::Launcher; +#endif // defined(MOZ_LAUNCHER_PROCESS) + + if (!runAsLauncher) { + // In this case, we will be proceeding to run as the browser. + // We should check MOZ_DEBUG_BROWSER_* env vars. + MaybeBreakForBrowserDebugging(); + } + + return mozilla::Some(runAsLauncher); +} + +namespace mozilla { + +Maybe<int> LauncherMain(int& argc, wchar_t* argv[], + const StaticXREAppData& aAppData) { + // Note: keep in sync with nsBrowserApp. + const wchar_t* acceptableParams[] = {L"url", nullptr}; + EnsureCommandlineSafe(argc, argv, acceptableParams); + + SetLauncherErrorAppData(aAppData); + + if (CheckArg(argc, argv, L"log-launcher-error", + static_cast<const wchar_t**>(nullptr), + mozilla::CheckArgFlag::RemoveArg) == ARG_FOUND) { + SetLauncherErrorForceEventLog(); + } + +#if defined(MOZ_LAUNCHER_PROCESS) + LauncherRegistryInfo regInfo; + Maybe<bool> runAsLauncher = RunAsLauncherProcess(regInfo, argc, argv); +#else + Maybe<bool> runAsLauncher = RunAsLauncherProcess(argc, argv); +#endif // defined(MOZ_LAUNCHER_PROCESS) + if (!runAsLauncher || !runAsLauncher.value()) { +#if defined(MOZ_LAUNCHER_PROCESS) + // Update the registry as Browser + LauncherVoidResult commitResult = regInfo.Commit(); + if (commitResult.isErr()) { + mozilla::HandleLauncherError(commitResult); + } +#endif // defined(MOZ_LAUNCHER_PROCESS) + 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); + } + } + + mozilla::UseParentConsole(); + + if (!SetArgv0ToFullBinaryPath(argv)) { + HandleLauncherError(LAUNCHER_ERROR_GENERIC()); + return Nothing(); + } + + LauncherFlags flags = ProcessCmdLine(argc, argv); + + nsAutoHandle mediumIlToken; + LauncherResult<ElevationState> elevationState = + GetElevationState(argv[0], flags, mediumIlToken); + if (elevationState.isErr()) { + HandleLauncherError(elevationState); + return Nothing(); + } + + // If we're elevated, we should relaunch ourselves as a normal user. + // Note that we only call LaunchUnelevated when we don't need to wait for the + // browser process. + if (elevationState.unwrap() == ElevationState::eElevated && + !(flags & + (LauncherFlags::eWaitForBrowser | LauncherFlags::eNoDeelevate)) && + !mediumIlToken.get()) { + LauncherVoidResult launchedUnelevated = LaunchUnelevated(argc, argv); + bool failed = launchedUnelevated.isErr(); + if (failed) { + HandleLauncherError(launchedUnelevated); + return Nothing(); + } + + return Some(0); + } + +#if defined(MOZ_LAUNCHER_PROCESS) + // Update the registry as Launcher + LauncherVoidResult commitResult = regInfo.Commit(); + if (commitResult.isErr()) { + mozilla::HandleLauncherError(commitResult); + return Nothing(); + } +#endif // defined(MOZ_LAUNCHER_PROCESS) + + // Now proceed with setting up the parameters for process creation + UniquePtr<wchar_t[]> cmdLine(MakeCommandLine(argc, argv)); + if (!cmdLine) { + HandleLauncherError(LAUNCHER_ERROR_GENERIC()); + return Nothing(); + } + + const Maybe<bool> isSafeMode = + IsSafeModeRequested(argc, argv, SafeModeFlag::NoKeyPressCheck); + if (!isSafeMode) { + HandleLauncherError(LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_PARAMETER)); + return Nothing(); + } + + ProcThreadAttributes attrs; + SetMitigationPolicies(attrs, isSafeMode.value()); + + HANDLE stdHandles[] = {::GetStdHandle(STD_INPUT_HANDLE), + ::GetStdHandle(STD_OUTPUT_HANDLE), + ::GetStdHandle(STD_ERROR_HANDLE)}; + + attrs.AddInheritableHandles(stdHandles); + + DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT; + + STARTUPINFOEXW siex; + LauncherResult<bool> attrsOk = attrs.AssignTo(siex); + if (attrsOk.isErr()) { + HandleLauncherError(attrsOk); + return Nothing(); + } + + BOOL inheritHandles = FALSE; + + if (attrsOk.unwrap()) { + creationFlags |= EXTENDED_STARTUPINFO_PRESENT; + + if (attrs.HasInheritableHandles()) { + siex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; + siex.StartupInfo.hStdInput = stdHandles[0]; + siex.StartupInfo.hStdOutput = stdHandles[1]; + siex.StartupInfo.hStdError = stdHandles[2]; + + // Since attrsOk == true, we have successfully set the handle inheritance + // whitelist policy, so only the handles added to attrs will be inherited. + inheritHandles = TRUE; + } + } + + PROCESS_INFORMATION pi = {}; + BOOL createOk; + + if (mediumIlToken.get()) { + createOk = + ::CreateProcessAsUserW(mediumIlToken.get(), argv[0], cmdLine.get(), + nullptr, nullptr, inheritHandles, creationFlags, + nullptr, nullptr, &siex.StartupInfo, &pi); + } else { + createOk = ::CreateProcessW(argv[0], cmdLine.get(), nullptr, nullptr, + inheritHandles, creationFlags, nullptr, nullptr, + &siex.StartupInfo, &pi); + } + + if (!createOk) { + HandleLauncherError(LAUNCHER_ERROR_FROM_LAST()); + return Nothing(); + } + + nsAutoHandle process(pi.hProcess); + nsAutoHandle mainThread(pi.hThread); + + LauncherVoidResult setupResult = PostCreationSetup( + argv[0], process.get(), mainThread.get(), isSafeMode.value()); + if (setupResult.isErr()) { + HandleLauncherError(setupResult); + ::TerminateProcess(process.get(), 1); + return Nothing(); + } + + if (::ResumeThread(mainThread.get()) == static_cast<DWORD>(-1)) { + HandleLauncherError(LAUNCHER_ERROR_FROM_LAST()); + ::TerminateProcess(process.get(), 1); + return Nothing(); + } + + if (flags & LauncherFlags::eWaitForBrowser) { + DWORD exitCode; + if (::WaitForSingleObject(process.get(), INFINITE) == WAIT_OBJECT_0 && + ::GetExitCodeProcess(process.get(), &exitCode)) { + // Propagate the browser process's exit code as our exit code. + return Some(static_cast<int>(exitCode)); + } + } else { + const DWORD timeout = + ::IsDebuggerPresent() ? INFINITE : kWaitForInputIdleTimeoutMS; + + // Keep the current process around until the callback process has created + // its message queue, to avoid the launched process's windows being forced + // into the background. + mozilla::WaitForInputIdle(process.get(), timeout); + } + + return Some(0); +} + +} // namespace mozilla diff --git a/browser/app/winlauncher/LauncherProcessWin.h b/browser/app/winlauncher/LauncherProcessWin.h new file mode 100644 index 0000000000..4fc819f658 --- /dev/null +++ b/browser/app/winlauncher/LauncherProcessWin.h @@ -0,0 +1,39 @@ +/* -*- 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_LauncherProcessWin_h +#define mozilla_LauncherProcessWin_h + +#include "mozilla/Maybe.h" +#include "mozilla/TypedEnumBits.h" + +#include <stdint.h> + +namespace mozilla { + +// Forward declaration +struct StaticXREAppData; + +/** + * Determine whether or not the current process should be run as the launcher + * process, and run if so. If we are not supposed to run as the launcher + * process, or in the event of a launcher process failure, return Nothing, thus + * indicating that we should continue on the original startup code path. + */ +Maybe<int> LauncherMain(int& argc, wchar_t* argv[], + const StaticXREAppData& aAppData); + +enum class LauncherFlags : uint32_t { + eNone = 0, + eWaitForBrowser = (1 << 0), // Launcher should block until browser finishes + eNoDeelevate = (1 << 1), // If elevated, do not attempt to de-elevate +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LauncherFlags) + +} // namespace mozilla + +#endif // mozilla_LauncherProcessWin_h diff --git a/browser/app/winlauncher/NtLoaderAPI.cpp b/browser/app/winlauncher/NtLoaderAPI.cpp new file mode 100644 index 0000000000..805fdc881e --- /dev/null +++ b/browser/app/winlauncher/NtLoaderAPI.cpp @@ -0,0 +1,30 @@ +/* -*- 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/LoaderAPIInterfaces.h" + +#include "freestanding/CheckForCaller.h" +#include "freestanding/LoaderPrivateAPI.h" + +namespace mozilla { + +extern "C" MOZ_EXPORT nt::LoaderAPI* GetNtLoaderAPI( + nt::LoaderObserver* aNewObserver) { + const bool isCallerMozglue = + CheckForAddress(RETURN_ADDRESS(), L"mozglue.dll"); + MOZ_ASSERT(isCallerMozglue); + if (!isCallerMozglue) { + return nullptr; + } + + freestanding::EnsureInitialized(); + freestanding::LoaderPrivateAPI& api = freestanding::gLoaderPrivateAPI; + api.SetObserver(aNewObserver); + + return &api; +} + +} // namespace mozilla diff --git a/browser/app/winlauncher/ProcThreadAttributes.h b/browser/app/winlauncher/ProcThreadAttributes.h new file mode 100644 index 0000000000..74d5fee06c --- /dev/null +++ b/browser/app/winlauncher/ProcThreadAttributes.h @@ -0,0 +1,159 @@ +/* -*- 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_ProcThreadAttributes_h +#define mozilla_ProcThreadAttributes_h + +#include <windows.h> + +#include <utility> + +#include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" + +namespace mozilla { + +class MOZ_RAII ProcThreadAttributes final { + struct ProcThreadAttributeListDeleter { + void operator()(LPPROC_THREAD_ATTRIBUTE_LIST aList) { + ::DeleteProcThreadAttributeList(aList); + delete[] reinterpret_cast<char*>(aList); + } + }; + + using ProcThreadAttributeListPtr = + UniquePtr<_PROC_THREAD_ATTRIBUTE_LIST, ProcThreadAttributeListDeleter>; + + public: + ProcThreadAttributes() : mMitigationPolicies(0) {} + + ~ProcThreadAttributes() = default; + + ProcThreadAttributes(const ProcThreadAttributes&) = delete; + ProcThreadAttributes(ProcThreadAttributes&&) = delete; + ProcThreadAttributes& operator=(const ProcThreadAttributes&) = delete; + ProcThreadAttributes& operator=(ProcThreadAttributes&&) = delete; + + void AddMitigationPolicy(DWORD64 aPolicy) { mMitigationPolicies |= aPolicy; } + + bool AddInheritableHandle(HANDLE aHandle) { + DWORD type = ::GetFileType(aHandle); + if (type != FILE_TYPE_DISK && type != FILE_TYPE_PIPE) { + return false; + } + + if (!::SetHandleInformation(aHandle, HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT)) { + return false; + } + + return mInheritableHandles.append(aHandle); + } + + template <size_t N> + bool AddInheritableHandles(HANDLE (&aHandles)[N]) { + bool ok = true; + for (auto handle : aHandles) { + ok &= AddInheritableHandle(handle); + } + + return ok; + } + + bool HasMitigationPolicies() const { return !!mMitigationPolicies; } + + bool HasInheritableHandles() const { return !mInheritableHandles.empty(); } + + /** + * @return false if the STARTUPINFOEXW::lpAttributeList was set to null + * as expected based on the state of |this|; + * true if the STARTUPINFOEXW::lpAttributeList was set to + * non-null; + */ + LauncherResult<bool> AssignTo(STARTUPINFOEXW& aSiex) { + ZeroMemory(&aSiex, sizeof(STARTUPINFOEXW)); + + // We'll set the size to sizeof(STARTUPINFOW) until we determine whether the + // extended fields will be used. + aSiex.StartupInfo.cb = sizeof(STARTUPINFOW); + + DWORD numAttributes = 0; + if (HasMitigationPolicies()) { + ++numAttributes; + } + + if (HasInheritableHandles()) { + ++numAttributes; + } + + if (!numAttributes) { + return false; + } + + SIZE_T listSize = 0; + if (!::InitializeProcThreadAttributeList(nullptr, numAttributes, 0, + &listSize)) { + DWORD err = ::GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) { + return LAUNCHER_ERROR_FROM_WIN32(err); + } + } + + auto buf = MakeUnique<char[]>(listSize); + + LPPROC_THREAD_ATTRIBUTE_LIST tmpList = + reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(buf.get()); + + if (!::InitializeProcThreadAttributeList(tmpList, numAttributes, 0, + &listSize)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + // Transfer buf to a ProcThreadAttributeListPtr - now that the list is + // initialized, we are no longer dealing with a plain old char array. We + // must now deinitialize the attribute list before deallocating the + // underlying buffer. + ProcThreadAttributeListPtr attrList( + reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(buf.release())); + + if (mMitigationPolicies) { + if (!::UpdateProcThreadAttribute( + attrList.get(), 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, + &mMitigationPolicies, sizeof(mMitigationPolicies), nullptr, + nullptr)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + } + + if (!mInheritableHandles.empty()) { + if (!::UpdateProcThreadAttribute( + attrList.get(), 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + mInheritableHandles.begin(), + mInheritableHandles.length() * sizeof(HANDLE), nullptr, + nullptr)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + } + + mAttrList = std::move(attrList); + aSiex.lpAttributeList = mAttrList.get(); + aSiex.StartupInfo.cb = sizeof(STARTUPINFOEXW); + return true; + } + + private: + static const uint32_t kNumInline = 3; // Inline storage for the std handles + + DWORD64 mMitigationPolicies; + Vector<HANDLE, kNumInline> mInheritableHandles; + ProcThreadAttributeListPtr mAttrList; +}; + +} // namespace mozilla + +#endif // mozilla_ProcThreadAttributes_h diff --git a/browser/app/winlauncher/SameBinary.h b/browser/app/winlauncher/SameBinary.h new file mode 100644 index 0000000000..f382e69572 --- /dev/null +++ b/browser/app/winlauncher/SameBinary.h @@ -0,0 +1,142 @@ +/* -*- 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_SameBinary_h +#define mozilla_SameBinary_h + +#include "mozilla/WinHeaderOnlyUtils.h" +#include "mozilla/NativeNt.h" +#include "nsWindowsHelpers.h" + +namespace mozilla { + +class ProcessImagePath final { + PathType mType; + LauncherVoidResult mLastError; + + // Using a larger buffer because an NT path may exceed MAX_PATH. + WCHAR mPathBuffer[(MAX_PATH * 2) + 1]; + + public: + // Initialize with an NT path string of a given process handle + explicit ProcessImagePath(const nsAutoHandle& aProcess) + : mType(PathType::eNtPath), mLastError(Ok()) { + DWORD len = mozilla::ArrayLength(mPathBuffer); + if (!::QueryFullProcessImageNameW(aProcess.get(), PROCESS_NAME_NATIVE, + mPathBuffer, &len)) { + mLastError = LAUNCHER_ERROR_FROM_LAST(); + return; + } + } + + // Initizlize with a DOS path string of a given imagebase address + explicit ProcessImagePath(HMODULE aImageBase) + : mType(PathType::eDosPath), mLastError(Ok()) { + DWORD len = ::GetModuleFileNameW(aImageBase, mPathBuffer, + mozilla::ArrayLength(mPathBuffer)); + if (!len || len == mozilla::ArrayLength(mPathBuffer)) { + mLastError = LAUNCHER_ERROR_FROM_LAST(); + return; + } + } + + bool IsError() const { return mLastError.isErr(); } + + const WindowsErrorType& GetError() const { return mLastError.inspectErr(); } + + FileUniqueId GetId() const { return FileUniqueId(mPathBuffer, mType); } + + bool CompareNtPaths(const ProcessImagePath& aOther) const { + if (mLastError.isErr() || aOther.mLastError.isErr() || + mType != PathType::eNtPath || aOther.mType != PathType::eNtPath) { + return false; + } + + UNICODE_STRING path1, path2; + ::RtlInitUnicodeString(&path1, mPathBuffer); + ::RtlInitUnicodeString(&path2, aOther.mPathBuffer); + return !!::RtlEqualUnicodeString(&path1, &path2, TRUE); + } +}; + +enum class ImageFileCompareOption { + Default, + CompareNtPathsOnly, +}; + +static inline mozilla::LauncherResult<bool> IsSameBinaryAsParentProcess( + ImageFileCompareOption aOption = ImageFileCompareOption::Default) { + mozilla::LauncherResult<DWORD> parentPid = mozilla::nt::GetParentProcessId(); + if (parentPid.isErr()) { + return parentPid.propagateErr(); + } + + nsAutoHandle parentProcess(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, + FALSE, parentPid.unwrap())); + if (!parentProcess.get()) { + DWORD err = ::GetLastError(); + if (err == ERROR_INVALID_PARAMETER) { + // The process identified by parentPid has already exited. This is a + // common case when the parent process is not Firefox, thus we should + // return false instead of erroring out. + return false; + } + + return LAUNCHER_ERROR_FROM_WIN32(err); + } + + ProcessImagePath parentExe(parentProcess); + if (parentExe.IsError()) { + return ::mozilla::Err(parentExe.GetError()); + } + + if (aOption == ImageFileCompareOption::Default) { + bool skipFileIdComparison = false; + + FileUniqueId id1 = parentExe.GetId(); + if (id1.IsError()) { + // We saw a number of Win7 users failed to call NtOpenFile with + // STATUS_OBJECT_PATH_NOT_FOUND for an unknown reason. In this + // particular case, we fall back to the logic to compare NT path + // strings instead of a file id which will not fail because we don't + // need to open a file handle. +#if !defined(STATUS_OBJECT_PATH_NOT_FOUND) + constexpr NTSTATUS STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a; +#endif + const LauncherError& err = id1.GetError(); + if (err.mError != + WindowsError::FromNtStatus(STATUS_OBJECT_PATH_NOT_FOUND)) { + return ::mozilla::Err(err); + } + + skipFileIdComparison = true; + } + + if (!skipFileIdComparison) { + ProcessImagePath ourExe(nullptr); + if (ourExe.IsError()) { + return ::mozilla::Err(ourExe.GetError()); + } + + FileUniqueId id2 = ourExe.GetId(); + if (id2.IsError()) { + return ::mozilla::Err(id2.GetError()); + } + return id1 == id2; + } + } + + nsAutoHandle ourProcess(::GetCurrentProcess()); + ProcessImagePath ourExeNt(ourProcess); + if (ourExeNt.IsError()) { + return ::mozilla::Err(ourExeNt.GetError()); + } + return parentExe.CompareNtPaths(ourExeNt); +} + +} // namespace mozilla + +#endif // mozilla_SameBinary_h diff --git a/browser/app/winlauncher/freestanding/CheckForCaller.h b/browser/app/winlauncher/freestanding/CheckForCaller.h new file mode 100644 index 0000000000..799d16cd1f --- /dev/null +++ b/browser/app/winlauncher/freestanding/CheckForCaller.h @@ -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 diff --git a/browser/app/winlauncher/freestanding/DllBlocklist.cpp b/browser/app/winlauncher/freestanding/DllBlocklist.cpp new file mode 100644 index 0000000000..2806cec87e --- /dev/null +++ b/browser/app/winlauncher/freestanding/DllBlocklist.cpp @@ -0,0 +1,483 @@ +/* -*- 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/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "mozilla/BinarySearch.h" +#include "mozilla/NativeNt.h" +#include "mozilla/Types.h" +#include "mozilla/WindowsDllBlocklist.h" + +#include "DllBlocklist.h" +#include "LoaderPrivateAPI.h" +#include "ModuleLoadFrame.h" +#include "SharedSection.h" + +// clang-format off +#define MOZ_LITERAL_UNICODE_STRING(s) \ + { \ + /* Length of the string in bytes, less the null terminator */ \ + sizeof(s) - sizeof(wchar_t), \ + /* Length of the string in bytes, including the null terminator */ \ + sizeof(s), \ + /* Pointer to the buffer */ \ + const_cast<wchar_t*>(s) \ + } +// clang-format on + +#define DLL_BLOCKLIST_ENTRY(name, ...) \ + {MOZ_LITERAL_UNICODE_STRING(L##name), __VA_ARGS__}, +#define DLL_BLOCKLIST_STRING_TYPE UNICODE_STRING + +#if defined(MOZ_LAUNCHER_PROCESS) || defined(NIGHTLY_BUILD) +# include "mozilla/WindowsDllBlocklistLauncherDefs.h" +#else +# include "mozilla/WindowsDllBlocklistCommon.h" +DLL_BLOCKLIST_DEFINITIONS_BEGIN +DLL_BLOCKLIST_DEFINITIONS_END +#endif + +class MOZ_STATIC_CLASS MOZ_TRIVIAL_CTOR_DTOR NativeNtBlockSet final { + struct NativeNtBlockSetEntry { + NativeNtBlockSetEntry() = default; + ~NativeNtBlockSetEntry() = default; + NativeNtBlockSetEntry(const UNICODE_STRING& aName, uint64_t aVersion, + NativeNtBlockSetEntry* aNext) + : mName(aName), mVersion(aVersion), mNext(aNext) {} + UNICODE_STRING mName; + uint64_t mVersion; + NativeNtBlockSetEntry* mNext; + }; + + public: + // Constructor and destructor MUST be trivial + constexpr NativeNtBlockSet() : mFirstEntry(nullptr) {} + ~NativeNtBlockSet() = default; + + void Add(const UNICODE_STRING& aName, uint64_t aVersion); + void Write(HANDLE aFile); + + private: + static NativeNtBlockSetEntry* NewEntry(const UNICODE_STRING& aName, + uint64_t aVersion, + NativeNtBlockSetEntry* aNextEntry); + + private: + NativeNtBlockSetEntry* mFirstEntry; + mozilla::nt::SRWLock mLock; +}; + +NativeNtBlockSet::NativeNtBlockSetEntry* NativeNtBlockSet::NewEntry( + const UNICODE_STRING& aName, uint64_t aVersion, + NativeNtBlockSet::NativeNtBlockSetEntry* aNextEntry) { + return mozilla::freestanding::RtlNew<NativeNtBlockSetEntry>(aName, aVersion, + aNextEntry); +} + +void NativeNtBlockSet::Add(const UNICODE_STRING& aName, uint64_t aVersion) { + mozilla::nt::AutoExclusiveLock lock(mLock); + + for (NativeNtBlockSetEntry* entry = mFirstEntry; entry; + entry = entry->mNext) { + if (::RtlEqualUnicodeString(&entry->mName, &aName, TRUE) && + aVersion == entry->mVersion) { + return; + } + } + + // Not present, add it + NativeNtBlockSetEntry* newEntry = NewEntry(aName, aVersion, mFirstEntry); + if (newEntry) { + mFirstEntry = newEntry; + } +} + +void NativeNtBlockSet::Write(HANDLE aFile) { + // NB: If this function is called, it is long after kernel32 is initialized, + // so it is safe to use Win32 calls here. + DWORD nBytes; + char buf[MAX_PATH]; + + // It would be nicer to use RAII here. However, its destructor + // might not run if an exception occurs, in which case we would never release + // the lock (MSVC warns about this possibility). So we acquire and release + // manually. + ::AcquireSRWLockExclusive(&mLock); + + MOZ_SEH_TRY { + for (auto entry = mFirstEntry; entry; entry = entry->mNext) { + int convOk = ::WideCharToMultiByte(CP_UTF8, 0, entry->mName.Buffer, + entry->mName.Length / sizeof(wchar_t), + buf, sizeof(buf), nullptr, nullptr); + if (!convOk) { + continue; + } + + // write name[,v.v.v.v]; + if (!WriteFile(aFile, buf, convOk, &nBytes, nullptr)) { + continue; + } + + if (entry->mVersion != DllBlockInfo::ALL_VERSIONS) { + WriteFile(aFile, ",", 1, &nBytes, nullptr); + uint16_t parts[4]; + parts[0] = entry->mVersion >> 48; + parts[1] = (entry->mVersion >> 32) & 0xFFFF; + parts[2] = (entry->mVersion >> 16) & 0xFFFF; + parts[3] = entry->mVersion & 0xFFFF; + for (size_t p = 0; p < mozilla::ArrayLength(parts); ++p) { + ltoa(parts[p], buf, 10); + WriteFile(aFile, buf, strlen(buf), &nBytes, nullptr); + if (p != mozilla::ArrayLength(parts) - 1) { + WriteFile(aFile, ".", 1, &nBytes, nullptr); + } + } + } + WriteFile(aFile, ";", 1, &nBytes, nullptr); + } + } + MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {} + + ::ReleaseSRWLockExclusive(&mLock); +} + +static NativeNtBlockSet gBlockSet; + +extern "C" void MOZ_EXPORT NativeNtBlockSet_Write(HANDLE aHandle) { + gBlockSet.Write(aHandle); +} + +enum class BlockAction { + Allow, + SubstituteLSP, + Error, + Deny, + NoOpEntryPoint, +}; + +static BlockAction CheckBlockInfo(const DllBlockInfo* aInfo, + const mozilla::nt::PEHeaders& aHeaders, + uint64_t& aVersion) { + aVersion = DllBlockInfo::ALL_VERSIONS; + + if (aInfo->mFlags & (DllBlockInfo::BLOCK_WIN8_AND_OLDER | + DllBlockInfo::BLOCK_WIN7_AND_OLDER)) { + RTL_OSVERSIONINFOW osv = {sizeof(osv)}; + NTSTATUS ntStatus = ::RtlGetVersion(&osv); + if (!NT_SUCCESS(ntStatus)) { + return BlockAction::Error; + } + + if ((aInfo->mFlags & DllBlockInfo::BLOCK_WIN8_AND_OLDER) && + (osv.dwMajorVersion > 6 || + (osv.dwMajorVersion == 6 && osv.dwMinorVersion > 2))) { + return BlockAction::Allow; + } + + if ((aInfo->mFlags & DllBlockInfo::BLOCK_WIN7_AND_OLDER) && + (osv.dwMajorVersion > 6 || + (osv.dwMajorVersion == 6 && osv.dwMinorVersion > 1))) { + return BlockAction::Allow; + } + } + + if ((aInfo->mFlags & DllBlockInfo::CHILD_PROCESSES_ONLY) && + !(gBlocklistInitFlags & eDllBlocklistInitFlagIsChildProcess)) { + return BlockAction::Allow; + } + + if ((aInfo->mFlags & DllBlockInfo::BROWSER_PROCESS_ONLY) && + (gBlocklistInitFlags & eDllBlocklistInitFlagIsChildProcess)) { + return BlockAction::Allow; + } + + if (aInfo->mMaxVersion == DllBlockInfo::ALL_VERSIONS) { + return BlockAction::Deny; + } + + if (!aHeaders) { + return BlockAction::Error; + } + + if (aInfo->mFlags & DllBlockInfo::USE_TIMESTAMP) { + DWORD timestamp; + if (!aHeaders.GetTimeStamp(timestamp)) { + return BlockAction::Error; + } + + if (timestamp > aInfo->mMaxVersion) { + return BlockAction::Allow; + } + + return BlockAction::Deny; + } + + // Else we try to get the file version information. Note that we don't have + // access to GetFileVersionInfo* APIs. + if (!aHeaders.GetVersionInfo(aVersion)) { + return BlockAction::Error; + } + + if (aInfo->IsVersionBlocked(aVersion)) { + return BlockAction::Deny; + } + + return BlockAction::Allow; +} + +struct DllBlockInfoComparator { + explicit DllBlockInfoComparator(const UNICODE_STRING& aTarget) + : mTarget(&aTarget) {} + + int operator()(const DllBlockInfo& aVal) const { + return static_cast<int>( + ::RtlCompareUnicodeString(mTarget, &aVal.mName, TRUE)); + } + + PCUNICODE_STRING mTarget; +}; + +static BOOL WINAPI NoOp_DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; } + +// This helper function checks whether a given module is included +// in the executable's Import Table. Because an injected module's +// DllMain may revert the Import Table to the original state, we parse +// the Import Table every time a module is loaded without creating a cache. +static bool IsDependentModule( + const UNICODE_STRING& aModuleLeafName, + mozilla::freestanding::Kernel32ExportsSolver& aK32Exports) { + // We enable automatic DLL blocking only in Nightly for now because it caused + // a compat issue (bug 1682304). +#if defined(NIGHTLY_BUILD) + aK32Exports.Resolve(mozilla::freestanding::gK32ExportsResolveOnce); + if (!aK32Exports.IsResolved()) { + return false; + } + + mozilla::nt::PEHeaders exeHeaders(aK32Exports.mGetModuleHandleW(nullptr)); + if (!exeHeaders || !exeHeaders.IsImportDirectoryTampered()) { + // If no tampering is detected, no need to enumerate the Import Table. + return false; + } + + bool isDependent = false; + exeHeaders.EnumImportChunks( + [&isDependent, &aModuleLeafName, &exeHeaders](const char* aDepModule) { + // If |aDepModule| is within the PE image, it's not an injected module + // but a legitimate dependent module. + if (isDependent || exeHeaders.IsWithinImage(aDepModule)) { + return; + } + + UNICODE_STRING depModuleLeafName; + mozilla::nt::AllocatedUnicodeString depModuleName(aDepModule); + mozilla::nt::GetLeafName(&depModuleLeafName, depModuleName); + isDependent = (::RtlCompareUnicodeString( + &aModuleLeafName, &depModuleLeafName, TRUE) == 0); + }); + return isDependent; +#else + return false; +#endif +} + +// Allowing a module to be loaded but detour the entrypoint to NoOp_DllMain +// so that the module has no chance to interact with our code. We need this +// technique to safely block a module injected by IAT tampering because +// blocking such a module makes a process fail to launch. +static bool RedirectToNoOpEntryPoint( + const mozilla::nt::PEHeaders& aModule, + mozilla::freestanding::Kernel32ExportsSolver& aK32Exports) { + aK32Exports.Resolve(mozilla::freestanding::gK32ExportsResolveOnce); + if (!aK32Exports.IsResolved()) { + return false; + } + + mozilla::interceptor::WindowsDllEntryPointInterceptor interceptor( + aK32Exports); + if (!interceptor.Set(aModule, NoOp_DllMain)) { + return false; + } + + return true; +} + +static BlockAction DetermineBlockAction( + const UNICODE_STRING& aLeafName, void* aBaseAddress, + mozilla::freestanding::Kernel32ExportsSolver* aK32Exports) { + if (mozilla::nt::Contains12DigitHexString(aLeafName) || + mozilla::nt::IsFileNameAtLeast16HexDigits(aLeafName)) { + return BlockAction::Deny; + } + + DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(info); + DECLARE_DLL_BLOCKLIST_NUM_ENTRIES(infoNumEntries); + + DllBlockInfoComparator comp(aLeafName); + + size_t match; + if (!BinarySearchIf(info, 0, infoNumEntries, comp, &match)) { + return BlockAction::Allow; + } + + const DllBlockInfo& entry = info[match]; + + mozilla::nt::PEHeaders headers(aBaseAddress); + uint64_t version; + BlockAction checkResult = CheckBlockInfo(&entry, headers, version); + if (checkResult == BlockAction::Allow) { + return checkResult; + } + + gBlockSet.Add(entry.mName, version); + + if ((entry.mFlags & DllBlockInfo::REDIRECT_TO_NOOP_ENTRYPOINT) && + aK32Exports && RedirectToNoOpEntryPoint(headers, *aK32Exports)) { + return BlockAction::NoOpEntryPoint; + } + + return checkResult; +} + +namespace mozilla { +namespace freestanding { + +CrossProcessDllInterceptor::FuncHookType<LdrLoadDllPtr> stub_LdrLoadDll; +RTL_RUN_ONCE gK32ExportsResolveOnce = RTL_RUN_ONCE_INIT; + +NTSTATUS NTAPI patched_LdrLoadDll(PWCHAR aDllPath, PULONG aFlags, + PUNICODE_STRING aDllName, + PHANDLE aOutHandle) { + ModuleLoadFrame frame(aDllName); + + NTSTATUS ntStatus = stub_LdrLoadDll(aDllPath, aFlags, aDllName, aOutHandle); + + return frame.SetLoadStatus(ntStatus, aOutHandle); +} + +CrossProcessDllInterceptor::FuncHookType<NtMapViewOfSectionPtr> + stub_NtMapViewOfSection; + +NTSTATUS NTAPI patched_NtMapViewOfSection( + HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits, + SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize, + SECTION_INHERIT aInheritDisposition, ULONG aAllocationType, + ULONG aProtectionFlags) { + // We always map first, then we check for additional info after. + NTSTATUS stubStatus = stub_NtMapViewOfSection( + aSection, aProcess, aBaseAddress, aZeroBits, aCommitSize, aSectionOffset, + aViewSize, aInheritDisposition, aAllocationType, aProtectionFlags); + if (!NT_SUCCESS(stubStatus)) { + return stubStatus; + } + + if (aProcess != nt::kCurrentProcess) { + // We're only interested in mapping for the current process. + return stubStatus; + } + + // Do a query to see if the memory is MEM_IMAGE. If not, continue + MEMORY_BASIC_INFORMATION mbi; + NTSTATUS ntStatus = + ::NtQueryVirtualMemory(aProcess, *aBaseAddress, MemoryBasicInformation, + &mbi, sizeof(mbi), nullptr); + if (!NT_SUCCESS(ntStatus)) { + ::NtUnmapViewOfSection(aProcess, *aBaseAddress); + return STATUS_ACCESS_DENIED; + } + + // We don't care about mappings that aren't MEM_IMAGE + if (!(mbi.Type & MEM_IMAGE)) { + return stubStatus; + } + + // Get the section name + nt::MemorySectionNameBuf sectionFileName( + gLoaderPrivateAPI.GetSectionNameBuffer(*aBaseAddress)); + if (sectionFileName.IsEmpty()) { + ::NtUnmapViewOfSection(aProcess, *aBaseAddress); + return STATUS_ACCESS_DENIED; + } + + // Find the leaf name + UNICODE_STRING leafOnStack; + nt::GetLeafName(&leafOnStack, sectionFileName); + + bool isDependent = false; + auto resultView = freestanding::gSharedSection.GetView(); + // Small optimization: Since loading a dependent module does not involve + // LdrLoadDll, we know isDependent is false if we hold a top frame. + if (resultView.isOk() && !ModuleLoadFrame::ExistsTopFrame()) { + isDependent = + IsDependentModule(leafOnStack, resultView.inspect()->mK32Exports); + } + + BlockAction blockAction; + if (isDependent) { + // Add an NT dv\path to the shared section so that a sandbox process can + // use it to bypass CIG. In a sandbox process, this addition fails + // because we cannot map the section to a writable region, but it's + // ignorable because the paths have been added by the browser process. + Unused << freestanding::gSharedSection.AddDepenentModule(sectionFileName); + + // For a dependent module, try redirection instead of blocking it. + // If we fail, we reluctantly allow the module for free. + mozilla::nt::PEHeaders headers(*aBaseAddress); + blockAction = + RedirectToNoOpEntryPoint(headers, resultView.inspect()->mK32Exports) + ? BlockAction::NoOpEntryPoint + : BlockAction::Allow; + } else { + // Check blocklist + blockAction = DetermineBlockAction( + leafOnStack, *aBaseAddress, + resultView.isOk() ? &resultView.inspect()->mK32Exports : nullptr); + } + + ModuleLoadInfo::Status loadStatus = ModuleLoadInfo::Status::Blocked; + + switch (blockAction) { + case BlockAction::Allow: + loadStatus = ModuleLoadInfo::Status::Loaded; + break; + + case BlockAction::NoOpEntryPoint: + loadStatus = ModuleLoadInfo::Status::Redirected; + break; + + case BlockAction::SubstituteLSP: + // The process heap needs to be available here because + // NotifyLSPSubstitutionRequired below copies a given string into + // the heap. We use a soft assert here, assuming LSP load always + // occurs after the heap is initialized. + MOZ_ASSERT(nt::RtlGetProcessHeap()); + + // Notify patched_LdrLoadDll that it will be necessary to perform + // a substitution before returning. + ModuleLoadFrame::NotifyLSPSubstitutionRequired(&leafOnStack); + break; + + default: + break; + } + + if (nt::RtlGetProcessHeap()) { + ModuleLoadFrame::NotifySectionMap( + nt::AllocatedUnicodeString(sectionFileName), *aBaseAddress, stubStatus, + loadStatus); + } + + if (loadStatus == ModuleLoadInfo::Status::Loaded || + loadStatus == ModuleLoadInfo::Status::Redirected) { + return stubStatus; + } + + ::NtUnmapViewOfSection(aProcess, *aBaseAddress); + return STATUS_ACCESS_DENIED; +} + +} // namespace freestanding +} // namespace mozilla diff --git a/browser/app/winlauncher/freestanding/DllBlocklist.h b/browser/app/winlauncher/freestanding/DllBlocklist.h new file mode 100644 index 0000000000..153a1dfb28 --- /dev/null +++ b/browser/app/winlauncher/freestanding/DllBlocklist.h @@ -0,0 +1,38 @@ +/* -*- 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_DllBlocklist_h +#define mozilla_freestanding_DllBlocklist_h + +#include "mozilla/NativeNt.h" +#include "nsWindowsDllInterceptor.h" +#include "mozilla/WinHeaderOnlyUtils.h" + +namespace mozilla { +namespace freestanding { + +NTSTATUS NTAPI patched_LdrLoadDll(PWCHAR aDllPath, PULONG aFlags, + PUNICODE_STRING aDllName, PHANDLE aOutHandle); + +NTSTATUS NTAPI patched_NtMapViewOfSection( + HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits, + SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize, + SECTION_INHERIT aInheritDisposition, ULONG aAllocationType, + ULONG aProtectionFlags); + +using LdrLoadDllPtr = decltype(&::LdrLoadDll); + +extern CrossProcessDllInterceptor::FuncHookType<LdrLoadDllPtr> stub_LdrLoadDll; + +using NtMapViewOfSectionPtr = decltype(&::NtMapViewOfSection); + +extern CrossProcessDllInterceptor::FuncHookType<NtMapViewOfSectionPtr> + stub_NtMapViewOfSection; + +} // namespace freestanding +} // namespace mozilla + +#endif // mozilla_freestanding_DllBlocklist_h diff --git a/browser/app/winlauncher/freestanding/Freestanding.h b/browser/app/winlauncher/freestanding/Freestanding.h new file mode 100644 index 0000000000..03999f231e --- /dev/null +++ b/browser/app/winlauncher/freestanding/Freestanding.h @@ -0,0 +1,67 @@ +/* -*- 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_Freestanding_h +#define mozilla_freestanding_Freestanding_h + +/** + * This header is automatically included in all source code residing in the + * /browser/app/winlauncher/freestanding directory. + */ + +#if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1 +# error "This header should only be included by freestanding code" +#endif // defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1 + +#define MOZ_USE_LAUNCHER_ERROR +#include "mozilla/NativeNt.h" + +namespace mozilla { +namespace freestanding { + +/** + * Since this library is the only part of firefox.exe that needs special + * treatment with respect to the heap, we implement |RtlNew| and |RtlDelete| + * to be used instead of |new| and |delete| for any heap allocations inside + * the freestanding library. + */ +template <typename T, typename... Args> +inline static T* RtlNew(Args&&... aArgs) { + HANDLE processHeap = nt::RtlGetProcessHeap(); + if (!processHeap) { + // Handle the case where the process heap is not initialized because + // passing nullptr to RtlAllocateHeap crashes the process. + return nullptr; + } + + void* ptr = ::RtlAllocateHeap(processHeap, 0, sizeof(T)); + if (!ptr) { + return nullptr; + } + + return new (ptr) T(std::forward<Args>(aArgs)...); +} + +template <typename T> +inline static void RtlDelete(T* aPtr) { + if (!aPtr) { + return; + } + + aPtr->~T(); + ::RtlFreeHeap(nt::RtlGetProcessHeap(), 0, aPtr); +} + +} // namespace freestanding +} // namespace mozilla + +// Initialization code for all statically-allocated data in freestanding is +// placed into a separate section. This allows us to initialize any +// freestanding statics without needing to initialize everything else in this +// binary. +#pragma init_seg(".freestd$g") + +#endif // mozilla_freestanding_Freestanding_h diff --git a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp new file mode 100644 index 0000000000..527b6556a6 --- /dev/null +++ b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp @@ -0,0 +1,287 @@ +/* -*- 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 "LoaderPrivateAPI.h" + +#include "mozilla/Assertions.h" +#include "mozilla/Types.h" +#include "mozilla/Unused.h" +#include "../DllBlocklistInit.h" +#include "../ErrorHandler.h" + +using GlobalInitializerFn = void(__cdecl*)(void); + +// Allocation of static initialization section for the freestanding library +#pragma section(".freestd$a", read) +__declspec(allocate(".freestd$a")) static const GlobalInitializerFn + FreeStdStart = reinterpret_cast<GlobalInitializerFn>(0); + +#pragma section(".freestd$z", read) +__declspec(allocate(".freestd$z")) static const GlobalInitializerFn FreeStdEnd = + reinterpret_cast<GlobalInitializerFn>(0); + +namespace mozilla { +namespace freestanding { + +static RTL_RUN_ONCE gRunOnce = RTL_RUN_ONCE_INIT; + +// The contract for this callback is identical to the InitOnceCallback from +// Win32 land; we're just using ntdll-layer types instead. +static ULONG NTAPI DoOneTimeInit(PRTL_RUN_ONCE aRunOnce, PVOID aParameter, + PVOID* aContext) { + // Invoke every static initializer in the .freestd section + const GlobalInitializerFn* cur = &FreeStdStart + 1; + while (cur < &FreeStdEnd) { + if (*cur) { + (*cur)(); + } + + ++cur; + } + + return TRUE; +} + +/** + * This observer is only used until the mozglue observer connects itself. + * All we do here is accumulate the module loads into a vector. + * As soon as mozglue connects, we call |Forward| on mozglue's LoaderObserver + * to pass our vector on for further processing. This object then becomes + * defunct. + */ +class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS DefaultLoaderObserver final + : public nt::LoaderObserver { + public: + constexpr DefaultLoaderObserver() : mModuleLoads(nullptr) {} + + void OnBeginDllLoad(void** aContext, + PCUNICODE_STRING aRequestedDllName) final {} + bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) final { + return false; + } + void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) final; + void Forward(nt::LoaderObserver* aNext) final; + void OnForward(ModuleLoadInfoVec&& aInfo) final { + MOZ_ASSERT_UNREACHABLE("Not valid in freestanding::DefaultLoaderObserver"); + } + + private: + mozilla::nt::SRWLock mLock; + ModuleLoadInfoVec* mModuleLoads; +}; + +class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderPrivateAPIImp final + : public LoaderPrivateAPI { + public: + // LoaderAPI + ModuleLoadInfo ConstructAndNotifyBeginDllLoad( + void** aContext, PCUNICODE_STRING aRequestedDllName) final; + bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) final; + void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) final; + nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final; + nt::LoaderAPI::InitDllBlocklistOOPFnPtr GetDllBlocklistInitFn() final; + nt::LoaderAPI::HandleLauncherErrorFnPtr GetHandleLauncherErrorFn() final; + + // LoaderPrivateAPI + void NotifyBeginDllLoad(void** aContext, + PCUNICODE_STRING aRequestedDllName) final; + void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, void** aContext, + PCUNICODE_STRING aRequestedDllName) final; + void SetObserver(nt::LoaderObserver* aNewObserver) final; + bool IsDefaultObserver() const final; + nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final; +}; + +static void Init() { + DebugOnly<NTSTATUS> ntStatus = + ::RtlRunOnceExecuteOnce(&gRunOnce, &DoOneTimeInit, nullptr, nullptr); + MOZ_ASSERT(NT_SUCCESS(ntStatus)); +} + +} // namespace freestanding +} // namespace mozilla + +static mozilla::freestanding::DefaultLoaderObserver gDefaultObserver; +static mozilla::freestanding::LoaderPrivateAPIImp gPrivateAPI; + +static mozilla::nt::SRWLock gLoaderObserverLock; +static mozilla::nt::LoaderObserver* gLoaderObserver = &gDefaultObserver; + +namespace mozilla { +namespace freestanding { + +LoaderPrivateAPI& gLoaderPrivateAPI = gPrivateAPI; + +void DefaultLoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) { + // If the DLL load failed, or if the DLL was loaded by a previous request + // and thus was not mapped by this request, we do not save the ModuleLoadInfo. + if (!NT_SUCCESS(aNtStatus) || !aModuleLoadInfo.WasMapped()) { + return; + } + + nt::AutoExclusiveLock lock(mLock); + if (!mModuleLoads) { + mModuleLoads = RtlNew<ModuleLoadInfoVec>(); + if (!mModuleLoads) { + return; + } + } + + Unused << mModuleLoads->emplaceBack( + std::forward<ModuleLoadInfo>(aModuleLoadInfo)); +} + +/** + * Pass mModuleLoads's data off to |aNext| for further processing. + */ +void DefaultLoaderObserver::Forward(nt::LoaderObserver* aNext) { + MOZ_ASSERT(aNext); + if (!aNext) { + return; + } + + ModuleLoadInfoVec* moduleLoads = nullptr; + + { // Scope for lock + nt::AutoExclusiveLock lock(mLock); + moduleLoads = mModuleLoads; + mModuleLoads = nullptr; + } + + if (!moduleLoads) { + return; + } + + aNext->OnForward(std::move(*moduleLoads)); + RtlDelete(moduleLoads); +} + +ModuleLoadInfo LoaderPrivateAPIImp::ConstructAndNotifyBeginDllLoad( + void** aContext, PCUNICODE_STRING aRequestedDllName) { + ModuleLoadInfo loadInfo(aRequestedDllName); + + NotifyBeginDllLoad(loadInfo, aContext, aRequestedDllName); + + return loadInfo; +} + +bool LoaderPrivateAPIImp::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) { + nt::AutoSharedLock lock(gLoaderObserverLock); + return gLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle); +} + +void LoaderPrivateAPIImp::NotifyEndDllLoad(void* aContext, + NTSTATUS aLoadNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) { + aModuleLoadInfo.SetEndLoadTimeStamp(); + + if (NT_SUCCESS(aLoadNtStatus)) { + aModuleLoadInfo.CaptureBacktrace(); + } + + nt::AutoSharedLock lock(gLoaderObserverLock); + + // We need to notify the observer that the DLL load has ended even when + // |aLoadNtStatus| indicates a failure. This is to ensure that any resources + // acquired by the observer during OnBeginDllLoad are cleaned up. + gLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus, + std::move(aModuleLoadInfo)); +} + +nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName( + void* aSectionAddr) { + const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1); + + nt::MemorySectionNameBuf buf; + NTSTATUS ntStatus = + ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName, + &buf, sizeof(buf), nullptr); + if (!NT_SUCCESS(ntStatus)) { + return nt::AllocatedUnicodeString(); + } + + return nt::AllocatedUnicodeString(&buf.mSectionFileName); +} + +nt::LoaderAPI::InitDllBlocklistOOPFnPtr +LoaderPrivateAPIImp::GetDllBlocklistInitFn() { + return &InitializeDllBlocklistOOP; +} + +nt::LoaderAPI::HandleLauncherErrorFnPtr +LoaderPrivateAPIImp::GetHandleLauncherErrorFn() { + return &HandleLauncherError; +} + +nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer( + void* aSectionAddr) { + const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1); + + nt::MemorySectionNameBuf buf; + NTSTATUS ntStatus = + ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName, + &buf, sizeof(buf), nullptr); + if (!NT_SUCCESS(ntStatus)) { + return nt::MemorySectionNameBuf(); + } + + return buf; +} + +void LoaderPrivateAPIImp::NotifyBeginDllLoad( + void** aContext, PCUNICODE_STRING aRequestedDllName) { + nt::AutoSharedLock lock(gLoaderObserverLock); + gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName); +} + +void LoaderPrivateAPIImp::NotifyBeginDllLoad( + ModuleLoadInfo& aModuleLoadInfo, void** aContext, + PCUNICODE_STRING aRequestedDllName) { + NotifyBeginDllLoad(aContext, aRequestedDllName); + aModuleLoadInfo.SetBeginLoadTimeStamp(); +} + +void LoaderPrivateAPIImp::SetObserver(nt::LoaderObserver* aNewObserver) { + nt::LoaderObserver* prevLoaderObserver = nullptr; + + nt::AutoExclusiveLock lock(gLoaderObserverLock); + + MOZ_ASSERT(aNewObserver); + if (!aNewObserver) { + // This is unlikely, but we always want a valid observer, so use the + // gDefaultObserver if necessary. + gLoaderObserver = &gDefaultObserver; + return; + } + + prevLoaderObserver = gLoaderObserver; + gLoaderObserver = aNewObserver; + + MOZ_ASSERT(prevLoaderObserver); + if (!prevLoaderObserver) { + return; + } + + // Now that we have a new observer, the previous observer must forward its + // data on to the new observer for processing. + prevLoaderObserver->Forward(aNewObserver); +} + +bool LoaderPrivateAPIImp::IsDefaultObserver() const { + nt::AutoSharedLock lock(gLoaderObserverLock); + return gLoaderObserver == &gDefaultObserver; +} + +void EnsureInitialized() { Init(); } + +} // namespace freestanding +} // namespace mozilla diff --git a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h new file mode 100644 index 0000000000..f21472d689 --- /dev/null +++ b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h @@ -0,0 +1,62 @@ +/* -*- 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_LoaderPrivateAPI_h +#define mozilla_freestanding_LoaderPrivateAPI_h + +#include "mozilla/LoaderAPIInterfaces.h" + +namespace mozilla { +namespace freestanding { + +/** + * This part of the API is available only to the launcher process. + */ +class NS_NO_VTABLE LoaderPrivateAPI : public nt::LoaderAPI { + public: + /** + * Notify the nt::LoaderObserver that a module load is beginning + */ + virtual void NotifyBeginDllLoad(void** aContext, + PCUNICODE_STRING aRequestedDllName) = 0; + /** + * Notify the nt::LoaderObserver that a module load is beginning and set the + * begin load timestamp on |aModuleLoadInfo|. + */ + virtual void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, + void** aContext, + PCUNICODE_STRING aRequestedDllName) = 0; + + /** + * Set a new nt::LoaderObserver to be used by the launcher process. NB: This + * should only happen while the current process is still single-threaded! + */ + virtual void SetObserver(nt::LoaderObserver* aNewObserver) = 0; + + /** + * Returns true if the current nt::LoaderObserver is the launcher process's + * built-in observer. + */ + virtual bool IsDefaultObserver() const = 0; + + /** + * Returns the name of a given mapped section address as a local instance of + * nt::MemorySectionNameBuf. This does not involve heap allocation. + */ + virtual nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) = 0; +}; + +/** + * Ensures that any statics in the freestanding library are initialized. + */ +void EnsureInitialized(); + +extern LoaderPrivateAPI& gLoaderPrivateAPI; + +} // namespace freestanding +} // namespace mozilla + +#endif // mozilla_freestanding_LoaderPrivateAPI_h diff --git a/browser/app/winlauncher/freestanding/ModuleLoadFrame.cpp b/browser/app/winlauncher/freestanding/ModuleLoadFrame.cpp new file mode 100644 index 0000000000..c734e70ed5 --- /dev/null +++ b/browser/app/winlauncher/freestanding/ModuleLoadFrame.cpp @@ -0,0 +1,139 @@ +/* -*- 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 "ModuleLoadFrame.h" + +#include "LoaderPrivateAPI.h" + +namespace mozilla { +namespace freestanding { + +ModuleLoadFrame::ModuleLoadFrame(PCUNICODE_STRING aRequestedDllName) + : mPrev(sTopFrame.get()), + mContext(nullptr), + mLSPSubstitutionRequired(false), + mLoadNtStatus(STATUS_UNSUCCESSFUL), + mLoadInfo(aRequestedDllName) { + EnsureInitialized(); + sTopFrame.set(this); + + gLoaderPrivateAPI.NotifyBeginDllLoad(mLoadInfo, &mContext, aRequestedDllName); +} + +ModuleLoadFrame::ModuleLoadFrame(nt::AllocatedUnicodeString&& aSectionName, + const void* aMapBaseAddr, NTSTATUS aNtStatus, + ModuleLoadInfo::Status aLoadStatus) + : mPrev(sTopFrame.get()), + mContext(nullptr), + mLSPSubstitutionRequired(false), + mLoadNtStatus(aNtStatus), + mLoadInfo(std::move(aSectionName), aMapBaseAddr, aLoadStatus) { + sTopFrame.set(this); + + gLoaderPrivateAPI.NotifyBeginDllLoad(&mContext, mLoadInfo.mSectionName); +} + +ModuleLoadFrame::~ModuleLoadFrame() { + gLoaderPrivateAPI.NotifyEndDllLoad(mContext, mLoadNtStatus, + std::move(mLoadInfo)); + sTopFrame.set(mPrev); +} + +/* static */ +void ModuleLoadFrame::NotifyLSPSubstitutionRequired( + PCUNICODE_STRING aLeafName) { + ModuleLoadFrame* topFrame = sTopFrame.get(); + if (!topFrame) { + return; + } + + topFrame->SetLSPSubstitutionRequired(aLeafName); +} + +void ModuleLoadFrame::SetLSPSubstitutionRequired(PCUNICODE_STRING aLeafName) { + MOZ_ASSERT(!mLoadInfo.mBaseAddr); + if (mLoadInfo.mBaseAddr) { + // If mBaseAddr is not null then |this| has already seen a module load. This + // should not be the case for a LSP substitution, so we bail. + return; + } + + // Save aLeafName, as it will be used by SetLoadStatus when invoking + // SubstituteForLSP + mLoadInfo.mRequestedDllName = aLeafName; + mLSPSubstitutionRequired = true; +} + +/* static */ +void ModuleLoadFrame::NotifySectionMap( + nt::AllocatedUnicodeString&& aSectionName, const void* aMapBaseAddr, + NTSTATUS aMapNtStatus, ModuleLoadInfo::Status aLoadStatus) { + ModuleLoadFrame* topFrame = sTopFrame.get(); + if (!topFrame) { + // The only time that this data is useful is during initial mapping of + // the executable's dependent DLLs. If mozglue is present then + // IsDefaultObserver will return false, indicating that we are beyond + // initial process startup. + if (gLoaderPrivateAPI.IsDefaultObserver()) { + OnBareSectionMap(std::move(aSectionName), aMapBaseAddr, aMapNtStatus, + aLoadStatus); + } + return; + } + + topFrame->OnSectionMap(std::move(aSectionName), aMapBaseAddr, aMapNtStatus, + aLoadStatus); +} + +/* static */ +bool ModuleLoadFrame::ExistsTopFrame() { return !!sTopFrame.get(); } + +void ModuleLoadFrame::OnSectionMap(nt::AllocatedUnicodeString&& aSectionName, + const void* aMapBaseAddr, + NTSTATUS aMapNtStatus, + ModuleLoadInfo::Status aLoadStatus) { + if (mLoadInfo.mBaseAddr) { + // If mBaseAddr is not null then |this| has already seen a module load. This + // means that we are witnessing a bare section map. + OnBareSectionMap(std::move(aSectionName), aMapBaseAddr, aMapNtStatus, + aLoadStatus); + return; + } + + mLoadInfo.mSectionName = std::move(aSectionName); + mLoadInfo.mBaseAddr = aMapBaseAddr; + mLoadInfo.mStatus = aLoadStatus; +} + +/* static */ +void ModuleLoadFrame::OnBareSectionMap( + nt::AllocatedUnicodeString&& aSectionName, const void* aMapBaseAddr, + NTSTATUS aMapNtStatus, ModuleLoadInfo::Status aLoadStatus) { + // We call the special constructor variant that is used for bare mappings. + ModuleLoadFrame frame(std::move(aSectionName), aMapBaseAddr, aMapNtStatus, + aLoadStatus); +} + +NTSTATUS ModuleLoadFrame::SetLoadStatus(NTSTATUS aNtStatus, + PHANDLE aOutHandle) { + mLoadNtStatus = aNtStatus; + + if (!mLSPSubstitutionRequired) { + return aNtStatus; + } + + if (!gLoaderPrivateAPI.SubstituteForLSP(mLoadInfo.mRequestedDllName, + aOutHandle)) { + return aNtStatus; + } + + return STATUS_SUCCESS; +} + +SafeThreadLocal<ModuleLoadFrame*> ModuleLoadFrame::sTopFrame; + +} // namespace freestanding +} // namespace mozilla diff --git a/browser/app/winlauncher/freestanding/ModuleLoadFrame.h b/browser/app/winlauncher/freestanding/ModuleLoadFrame.h new file mode 100644 index 0000000000..a0dfc01b57 --- /dev/null +++ b/browser/app/winlauncher/freestanding/ModuleLoadFrame.h @@ -0,0 +1,95 @@ +/* -*- 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_ModuleLoadFrame_h +#define mozilla_freestanding_ModuleLoadFrame_h + +#include "mozilla/LoaderAPIInterfaces.h" +#include "mozilla/NativeNt.h" +#include "mozilla/ThreadLocal.h" + +#include "SafeThreadLocal.h" + +namespace mozilla { +namespace freestanding { + +/** + * This class holds information about a DLL load at a particular frame in the + * current thread's stack. Each instance adds itself to a thread-local linked + * list of ModuleLoadFrames, enabling us to query information about the + * previous module load on the stack. + */ +class MOZ_RAII ModuleLoadFrame final { + public: + /** + * This constructor is for use by the LdrLoadDll hook. + */ + explicit ModuleLoadFrame(PCUNICODE_STRING aRequestedDllName); + ~ModuleLoadFrame(); + + static void NotifyLSPSubstitutionRequired(PCUNICODE_STRING aLeafName); + + /** + * This static method is called by the NtMapViewOfSection hook. + */ + static void NotifySectionMap(nt::AllocatedUnicodeString&& aSectionName, + const void* aMapBaseAddr, NTSTATUS aMapNtStatus, + ModuleLoadInfo::Status aLoadStatus); + static bool ExistsTopFrame(); + + /** + * Called by the LdrLoadDll hook to indicate the status of the load and for + * us to provide a substitute output handle if necessary. + */ + NTSTATUS SetLoadStatus(NTSTATUS aNtStatus, PHANDLE aOutHandle); + + ModuleLoadFrame(const ModuleLoadFrame&) = delete; + ModuleLoadFrame(ModuleLoadFrame&&) = delete; + ModuleLoadFrame& operator=(const ModuleLoadFrame&) = delete; + ModuleLoadFrame& operator=(ModuleLoadFrame&&) = delete; + + private: + /** + * Called by OnBareSectionMap to construct a frame for a bare load. + */ + ModuleLoadFrame(nt::AllocatedUnicodeString&& aSectionName, + const void* aMapBaseAddr, NTSTATUS aNtStatus, + ModuleLoadInfo::Status aLoadStatus); + + void SetLSPSubstitutionRequired(PCUNICODE_STRING aLeafName); + void OnSectionMap(nt::AllocatedUnicodeString&& aSectionName, + const void* aMapBaseAddr, NTSTATUS aMapNtStatus, + ModuleLoadInfo::Status aLoadStatus); + + /** + * A "bare" section mapping is one that was mapped without the code passing + * through a call to ntdll!LdrLoadDll. This method is invoked when we detect + * that condition. + */ + static void OnBareSectionMap(nt::AllocatedUnicodeString&& aSectionName, + const void* aMapBaseAddr, NTSTATUS aMapNtStatus, + ModuleLoadInfo::Status aLoadStatus); + + private: + // Link to the previous frame + ModuleLoadFrame* mPrev; + // Pointer to context managed by the nt::LoaderObserver implementation + void* mContext; + // Set to |true| when we need to block a WinSock LSP + bool mLSPSubstitutionRequired; + // NTSTATUS code from the |LdrLoadDll| call + NTSTATUS mLoadNtStatus; + // Telemetry information that will be forwarded to the nt::LoaderObserver + ModuleLoadInfo mLoadInfo; + + // Head of the linked list + static SafeThreadLocal<ModuleLoadFrame*> sTopFrame; +}; + +} // namespace freestanding +} // namespace mozilla + +#endif // mozilla_freestanding_ModuleLoadFrame_h diff --git a/browser/app/winlauncher/freestanding/SafeThreadLocal.h b/browser/app/winlauncher/freestanding/SafeThreadLocal.h new file mode 100644 index 0000000000..e4b869f649 --- /dev/null +++ b/browser/app/winlauncher/freestanding/SafeThreadLocal.h @@ -0,0 +1,96 @@ +/* -*- 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_SafeThreadLocal_h +#define mozilla_freestanding_SafeThreadLocal_h + +#include <type_traits> + +#include "mozilla/NativeNt.h" +#include "mozilla/ThreadLocal.h" + +namespace mozilla { +namespace freestanding { + +// We cannot fall back to the Tls* APIs because kernel32 might not have been +// loaded yet. +#if defined(__MINGW32__) && !defined(HAVE_THREAD_TLS_KEYWORD) +# error "This code requires the compiler to have native TLS support" +#endif // defined(__MINGW32__) && !defined(HAVE_THREAD_TLS_KEYWORD) + +/** + * This class holds data as a thread-local variable, or as a global variable + * if the thread local storage is not initialized yet. It should be safe + * because in that early stage we assume there is no more than a single thread. + */ +template <typename T> +class SafeThreadLocal final { + static MOZ_THREAD_LOCAL(T) sThreadLocal; + static T sGlobal; + static bool sIsTlsUsed; + + // In normal cases, TLS is always available and the class uses sThreadLocal + // without changing sMainThreadId. So sMainThreadId is likely to be 0. + // + // If TLS is not available, we use sGlobal instead and update sMainThreadId + // so that that thread keeps using sGlobal even after TLS is initialized + // later. + static DWORD sMainThreadId; + + // Need non-inline accessors to prevent the compiler from generating code + // accessing sThreadLocal before checking a condition. + MOZ_NEVER_INLINE static void SetGlobalValue(T aValue) { sGlobal = aValue; } + MOZ_NEVER_INLINE static T GetGlobalValue() { return sGlobal; } + + public: + static void set(T aValue) { + static_assert(std::is_pointer_v<T>, + "SafeThreadLocal must be used with a pointer"); + + if (sMainThreadId == mozilla::nt::RtlGetCurrentThreadId()) { + SetGlobalValue(aValue); + } else if (sIsTlsUsed) { + MOZ_ASSERT(mozilla::nt::RtlGetThreadLocalStoragePointer(), + "Once TLS is used, TLS should be available till the end."); + sThreadLocal.set(aValue); + } else if (mozilla::nt::RtlGetThreadLocalStoragePointer()) { + sIsTlsUsed = true; + sThreadLocal.set(aValue); + } else { + MOZ_ASSERT(sMainThreadId == 0, + "A second thread cannot be created before TLS is available."); + sMainThreadId = mozilla::nt::RtlGetCurrentThreadId(); + SetGlobalValue(aValue); + } + } + + static T get() { + if (sMainThreadId == mozilla::nt::RtlGetCurrentThreadId()) { + return GetGlobalValue(); + } else if (sIsTlsUsed) { + return sThreadLocal.get(); + } + return GetGlobalValue(); + } +}; + +template <typename T> +MOZ_THREAD_LOCAL(T) +SafeThreadLocal<T>::sThreadLocal; + +template <typename T> +T SafeThreadLocal<T>::sGlobal = nullptr; + +template <typename T> +bool SafeThreadLocal<T>::sIsTlsUsed = false; + +template <typename T> +DWORD SafeThreadLocal<T>::sMainThreadId = 0; + +} // namespace freestanding +} // namespace mozilla + +#endif // mozilla_freestanding_SafeThreadLocal_h diff --git a/browser/app/winlauncher/freestanding/SharedSection.cpp b/browser/app/winlauncher/freestanding/SharedSection.cpp new file mode 100644 index 0000000000..66891ac80f --- /dev/null +++ b/browser/app/winlauncher/freestanding/SharedSection.cpp @@ -0,0 +1,267 @@ +/* -*- 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 "SharedSection.h" + +#include "CheckForCaller.h" + +namespace { + +bool AddString(void* aBuffer, size_t aBufferSize, const UNICODE_STRING& aStr) { + size_t offset = 0; + while (offset < aBufferSize) { + UNICODE_STRING uniStr; + ::RtlInitUnicodeString(&uniStr, + reinterpret_cast<wchar_t*>( + reinterpret_cast<uintptr_t>(aBuffer) + offset)); + + if (uniStr.Length == 0) { + // Reached to the array's last item. + break; + } + + if (::RtlCompareUnicodeString(&uniStr, &aStr, TRUE) == 0) { + // Already included in the array. + return true; + } + + // Go to the next string. + offset += uniStr.MaximumLength; + } + + // Ensure enough space including the last empty string at the end. + if (offset + aStr.MaximumLength + sizeof(wchar_t) > aBufferSize) { + return false; + } + + auto newStr = reinterpret_cast<uint8_t*>(aBuffer) + offset; + memcpy(newStr, aStr.Buffer, aStr.Length); + memset(newStr + aStr.Length, 0, sizeof(wchar_t)); + return true; +} + +} // anonymous namespace + +namespace mozilla { +namespace freestanding { + +SharedSection gSharedSection; + +bool Kernel32ExportsSolver::IsInitialized() const { + return mState == State::Initialized || IsResolved(); +} + +bool Kernel32ExportsSolver::IsResolved() const { + return mState == State::Resolved; +} + +// Why don't we use ::GetProcAddress? +// If the export table of kernel32.dll is tampered in the current process, +// we cannot transfer an RVA because the function pointed by the RVA may not +// exist in a target process. +// We can use ::GetProcAddress with additional check to detect tampering, but +// FindExportAddressTableEntry fits perfectly here because it returns nullptr +// if the target entry is outside the image, which means it's tampered or +// forwarded to another DLL. +#define INIT_FUNCTION(exports, name) \ + do { \ + auto rvaToFunction = exports.FindExportAddressTableEntry(#name); \ + if (!rvaToFunction) { \ + return; \ + } \ + m##name = reinterpret_cast<decltype(m##name)>(*rvaToFunction); \ + } while (0) + +#define RESOLVE_FUNCTION(base, name) \ + m##name = reinterpret_cast<decltype(m##name)>( \ + base + reinterpret_cast<uintptr_t>(m##name)) + +void Kernel32ExportsSolver::Init() { + if (mState == State::Initialized || mState == State::Resolved) { + return; + } + + interceptor::MMPolicyInProcess policy; + auto k32Exports = nt::PEExportSection<interceptor::MMPolicyInProcess>::Get( + ::GetModuleHandleW(L"kernel32.dll"), policy); + if (!k32Exports) { + return; + } + + // Please make sure these functions are not forwarded to another DLL. + INIT_FUNCTION(k32Exports, FlushInstructionCache); + INIT_FUNCTION(k32Exports, GetModuleHandleW); + INIT_FUNCTION(k32Exports, GetSystemInfo); + INIT_FUNCTION(k32Exports, VirtualProtect); + + mState = State::Initialized; +} + +void Kernel32ExportsSolver::ResolveInternal() { + if (mState == State::Resolved) { + return; + } + + MOZ_RELEASE_ASSERT(mState == State::Initialized); + + UNICODE_STRING k32Name; + ::RtlInitUnicodeString(&k32Name, L"kernel32.dll"); + + // We cannot use GetModuleHandleW because this code can be called + // before IAT is resolved. + auto k32Module = nt::GetModuleHandleFromLeafName(k32Name); + MOZ_RELEASE_ASSERT(k32Module.isOk()); + + uintptr_t k32Base = + nt::PEHeaders::HModuleToBaseAddr<uintptr_t>(k32Module.unwrap()); + + RESOLVE_FUNCTION(k32Base, FlushInstructionCache); + RESOLVE_FUNCTION(k32Base, GetModuleHandleW); + RESOLVE_FUNCTION(k32Base, GetSystemInfo); + RESOLVE_FUNCTION(k32Base, VirtualProtect); + + mState = State::Resolved; +} + +/* static */ +ULONG NTAPI Kernel32ExportsSolver::ResolveOnce(PRTL_RUN_ONCE aRunOnce, + PVOID aParameter, PVOID*) { + reinterpret_cast<Kernel32ExportsSolver*>(aParameter)->ResolveInternal(); + return TRUE; +} + +void Kernel32ExportsSolver::Resolve(RTL_RUN_ONCE& aRunOnce) { + ::RtlRunOnceExecuteOnce(&aRunOnce, &ResolveOnce, this, nullptr); +} + +HANDLE SharedSection::sSectionHandle = nullptr; +void* SharedSection::sWriteCopyView = nullptr; + +void SharedSection::Reset(HANDLE aNewSecionObject) { + if (sWriteCopyView) { + nt::AutoMappedView view(sWriteCopyView); + sWriteCopyView = nullptr; + } + + if (sSectionHandle != aNewSecionObject) { + if (sSectionHandle) { + ::CloseHandle(sSectionHandle); + } + sSectionHandle = aNewSecionObject; + } +} + +void SharedSection::ConvertToReadOnly() { + if (!sSectionHandle) { + return; + } + + HANDLE readonlyHandle; + if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle, + nt::kCurrentProcess, &readonlyHandle, GENERIC_READ, + FALSE, 0)) { + return; + } + + Reset(readonlyHandle); +} + +LauncherVoidResult SharedSection::Init(const nt::PEHeaders& aPEHeaders) { + static_assert( + kSharedViewSize >= sizeof(Layout), + "kSharedViewSize is too small to represent SharedSection::Layout."); + + HANDLE section = + ::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, + kSharedViewSize, nullptr); + if (!section) { + return LAUNCHER_ERROR_FROM_LAST(); + } + Reset(section); + + // The initial contents of the pages in a file mapping object backed by + // the operating system paging file are 0 (zero). No need to zero it out + // ourselves. + // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw + nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE); + if (!writableView) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + SharedSection::Layout* view = writableView.as<SharedSection::Layout>(); + view->mK32Exports.Init(); + + return Ok(); +} + +LauncherVoidResult SharedSection::AddDepenentModule(PCUNICODE_STRING aNtPath) { + nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE); + if (!writableView) { + return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error()); + } + + SharedSection::Layout* view = writableView.as<SharedSection::Layout>(); + if (!AddString(view->mModulePathArray, + kSharedViewSize - sizeof(Kernel32ExportsSolver), *aNtPath)) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + return Ok(); +} + +LauncherResult<SharedSection::Layout*> SharedSection::GetView() { + if (!sWriteCopyView) { + nt::AutoMappedView view(sSectionHandle, PAGE_WRITECOPY); + if (!view) { + return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error()); + } + sWriteCopyView = view.release(); + } + return reinterpret_cast<SharedSection::Layout*>(sWriteCopyView); +} + +LauncherVoidResult SharedSection::TransferHandle( + nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess, + HANDLE* aDestinationAddress) { + HANDLE remoteHandle; + if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle, + aTransferMgr.RemoteProcess(), &remoteHandle, + aDesiredAccess, FALSE, 0)) { + return LAUNCHER_ERROR_FROM_LAST(); + } + + return aTransferMgr.Transfer(aDestinationAddress, &remoteHandle, + sizeof(remoteHandle)); +} + +// This exported function is invoked by SandboxBroker of xul.dll +// in order to add dependent modules to the CIG exception list. +extern "C" MOZ_EXPORT const wchar_t* GetDependentModulePaths() { + // We enable pre-spawn CIG only in Nightly for now because it caused + // a compat issue (bug 1682304). +#if defined(NIGHTLY_BUILD) + const bool isCallerXul = CheckForAddress(RETURN_ADDRESS(), L"xul.dll"); + MOZ_ASSERT(isCallerXul); + if (!isCallerXul) { + return nullptr; + } + + // Remap a write-copy section to take the latest update in mModulePathArray. + gSharedSection.Reset(); + + LauncherResult<SharedSection::Layout*> resultView = gSharedSection.GetView(); + if (resultView.isErr()) { + return nullptr; + } + + return resultView.inspect()->mModulePathArray; +#else + return nullptr; +#endif +} + +} // namespace freestanding +} // namespace mozilla diff --git a/browser/app/winlauncher/freestanding/SharedSection.h b/browser/app/winlauncher/freestanding/SharedSection.h new file mode 100644 index 0000000000..0724cfc05d --- /dev/null +++ b/browser/app/winlauncher/freestanding/SharedSection.h @@ -0,0 +1,118 @@ +/* -*- 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_SharedSection_h +#define mozilla_freestanding_SharedSection_h + +#include "mozilla/NativeNt.h" +#include "mozilla/interceptor/MMPolicies.h" + +namespace mozilla { +namespace freestanding { + +// This class calculates RVAs of kernel32's functions and transfers them +// to a target process, where the transferred RVAs are resolved into +// function addresses so that the target process can use them after +// kernel32.dll is loaded and before IAT is resolved. +class MOZ_TRIVIAL_CTOR_DTOR Kernel32ExportsSolver final + : public interceptor::MMPolicyInProcessEarlyStage::Kernel32Exports { + enum class State { + Uninitialized, + Initialized, + Resolved, + } mState; + + static ULONG NTAPI ResolveOnce(PRTL_RUN_ONCE aRunOnce, PVOID aParameter, + PVOID*); + void ResolveInternal(); + + public: + Kernel32ExportsSolver() = default; + + Kernel32ExportsSolver(const Kernel32ExportsSolver&) = delete; + Kernel32ExportsSolver(Kernel32ExportsSolver&&) = delete; + Kernel32ExportsSolver& operator=(const Kernel32ExportsSolver&) = delete; + Kernel32ExportsSolver& operator=(Kernel32ExportsSolver&&) = delete; + + bool IsInitialized() const; + bool IsResolved() const; + + void Init(); + void Resolve(RTL_RUN_ONCE& aRunOnce); +}; + +// This class manages a section which is created in the launcher process and +// mapped in the browser process and the sandboxed processes. The section's +// layout is represented as SharedSection::Layout. +// +// (1) Kernel32's functions required for MMPolicyInProcessEarlyStage +// Formatted as Kernel32ExportsSolver. +// +// (2) Array of NT paths of the executable's dependent modules +// Formatted as a null-delimited wide-character string set ending with +// an empty string. +// +// +--------------------------------------------------------------+ +// | (1) | FlushInstructionCache | +// | | GetModuleHandleW | +// | | GetSystemInfo | +// | | VirtualProtect | +// | | State [Uninitialized|Initialized|Resolved] | +// +--------------------------------------------------------------+ +// | (2) | L"NT path 1" | +// | | L"NT path 2" | +// | | ... | +// | | L"" | +// +--------------------------------------------------------------+ +class MOZ_TRIVIAL_CTOR_DTOR SharedSection final { + // As we define a global variable of this class and use it in our blocklist + // which is excuted in a process's early stage. If we have a complex dtor, + // the static initializer tries to register that dtor with onexit() of + // ucrtbase.dll which is not loaded yet, resulting in crash. Thus, we have + // a raw handle and a pointer as a static variable and manually release them + // by calling Reset() where possible. + static HANDLE sSectionHandle; + static void* sWriteCopyView; + + static constexpr size_t kSharedViewSize = 0x1000; + + public: + struct Layout final { + Kernel32ExportsSolver mK32Exports; + wchar_t mModulePathArray[1]; + + Layout() = delete; // disallow instantiation + }; + + // Replace |sSectionHandle| with a given handle. + static void Reset(HANDLE aNewSecionObject = sSectionHandle); + + // Replace |sSectionHandle| with a new readonly handle. + static void ConvertToReadOnly(); + + // Create a new writable section and initialize the Kernel32ExportsSolver + // part. + static LauncherVoidResult Init(const nt::PEHeaders& aPEHeaders); + + // Append a new string to the |sSectionHandle| + static LauncherVoidResult AddDepenentModule(PCUNICODE_STRING aNtPath); + + // Map |sSectionHandle| to a copy-on-write page and return its address. + static LauncherResult<Layout*> GetView(); + + // Transfer |sSectionHandle| to a process associated with |aTransferMgr|. + static LauncherVoidResult TransferHandle( + nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess, + HANDLE* aDestinationAddress = &sSectionHandle); +}; + +extern SharedSection gSharedSection; +extern RTL_RUN_ONCE gK32ExportsResolveOnce; + +} // namespace freestanding +} // namespace mozilla + +#endif // mozilla_freestanding_SharedSection_h diff --git a/browser/app/winlauncher/freestanding/gen_ntdll_freestanding_lib.py b/browser/app/winlauncher/freestanding/gen_ntdll_freestanding_lib.py new file mode 100644 index 0000000000..a69261cc3a --- /dev/null +++ b/browser/app/winlauncher/freestanding/gen_ntdll_freestanding_lib.py @@ -0,0 +1,30 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +from __future__ import absolute_import + +import os +import subprocess +import tempfile + + +def main(output_fd, def_file, llvm_dlltool, *llvm_dlltool_args): + # llvm-dlltool can't output to stdout, so we create a temp file, use that + # to write out the lib, and then copy it over to output_fd + (tmp_fd, tmp_output) = tempfile.mkstemp() + os.close(tmp_fd) + + try: + cmd = [llvm_dlltool] + cmd.extend(llvm_dlltool_args) + cmd += ["-d", def_file, "-l", tmp_output] + + subprocess.check_call(cmd) + + with open(tmp_output, "rb") as tmplib: + output_fd.write(tmplib.read()) + finally: + os.remove(tmp_output) diff --git a/browser/app/winlauncher/freestanding/moz.build b/browser/app/winlauncher/freestanding/moz.build new file mode 100644 index 0000000000..7a43d9a104 --- /dev/null +++ b/browser/app/winlauncher/freestanding/moz.build @@ -0,0 +1,56 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Library("winlauncher-freestanding") + +FORCE_STATIC_LIB = True + +# Our patched NtMapViewOfSection can be called before the process's import +# table is populated. Don't let the compiler insert any instrumentation +# that might call an import. +NO_PGO = True + +UNIFIED_SOURCES += [ + "DllBlocklist.cpp", + "LoaderPrivateAPI.cpp", + "ModuleLoadFrame.cpp", + "SharedSection.cpp", +] + +# This library must be compiled in a freestanding environment, as its code must +# not assume that it has access to any runtime libraries. +if CONFIG["CC_TYPE"] == "clang-cl": + CXXFLAGS += ["-Xclang"] + +CXXFLAGS += [ + "-ffreestanding", +] + +# Forcibly include Freestanding.h into all source files in this library. +if CONFIG["CC_TYPE"] == "clang-cl": + CXXFLAGS += ["-FI"] +else: + CXXFLAGS += ["-include"] + +CXXFLAGS += [SRCDIR + "/Freestanding.h"] + +OS_LIBS += [ + "ntdll", + "ntdll_freestanding", +] + +if CONFIG["COMPILE_ENVIRONMENT"] and CONFIG["LLVM_DLLTOOL"]: + GeneratedFile( + "%sntdll_freestanding.%s" % (CONFIG["LIB_PREFIX"], CONFIG["LIB_SUFFIX"]), + script="gen_ntdll_freestanding_lib.py", + inputs=["ntdll_freestanding.def"], + flags=[CONFIG["LLVM_DLLTOOL"]] + CONFIG["LLVM_DLLTOOL_FLAGS"], + ) + +DisableStlWrapping() + +with Files("**"): + BUG_COMPONENT = ("Firefox", "Launcher Process") diff --git a/browser/app/winlauncher/freestanding/ntdll_freestanding.def b/browser/app/winlauncher/freestanding/ntdll_freestanding.def new file mode 100644 index 0000000000..6e5e2685fe --- /dev/null +++ b/browser/app/winlauncher/freestanding/ntdll_freestanding.def @@ -0,0 +1,25 @@ +; 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/. + +LIBRARY ntdll + +; When we compile with -freestanding, the compiler still requires implementation +; of the four functions listed below. +; +; We could implement our own naive versions of these functions, but that +; solution is less than ideal since the implementations must be extern and are +; thus picked up by the entire firefox.exe binary. This denies the rest of +; firefox.exe the benefit of optimized implementations. On Windows the +; sandbox is linked into firefox.exe, so we cannot just shrug and +; assume that a naive implementation will not have any effect on anything. +; +; There are, however, optimized implementations of these functions that are +; exported by ntdll.dll. OTOH, they are not included in the ntdll.lib +; import library. This .def file is used to build an import library that "fills +; in the blanks" and allows us to link into the ntdll implementations. +EXPORTS + memcmp + memcpy + memmove + memset diff --git a/browser/app/winlauncher/moz.build b/browser/app/winlauncher/moz.build new file mode 100644 index 0000000000..585e883547 --- /dev/null +++ b/browser/app/winlauncher/moz.build @@ -0,0 +1,51 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Library("winlauncher") + +FORCE_STATIC_LIB = True + +UNIFIED_SOURCES += [ + "/ipc/mscom/ProcessRuntime.cpp", + "/widget/windows/WindowsConsole.cpp", + "DllBlocklistInit.cpp", + "ErrorHandler.cpp", + "LauncherProcessWin.cpp", + "LaunchUnelevated.cpp", + "NtLoaderAPI.cpp", +] + +OS_LIBS += [ + "oleaut32", + "ole32", + "rpcrt4", + "version", +] + +DIRS += [ + "freestanding", +] + +USE_LIBS += [ + "winlauncher-freestanding", +] + +TEST_DIRS += [ + "test", +] + +if CONFIG["MOZ_LAUNCHER_PROCESS"]: + UNIFIED_SOURCES += [ + "/toolkit/xre/LauncherRegistryInfo.cpp", + "/toolkit/xre/WinTokenUtils.cpp", + ] + for var in ("MOZ_APP_BASENAME", "MOZ_APP_VENDOR"): + DEFINES[var] = '"%s"' % CONFIG[var] + +DisableStlWrapping() + +with Files("**"): + BUG_COMPONENT = ("Firefox", "Launcher Process") diff --git a/browser/app/winlauncher/test/TestCrossProcessWin.cpp b/browser/app/winlauncher/test/TestCrossProcessWin.cpp new file mode 100644 index 0000000000..0c5c57aaaa --- /dev/null +++ b/browser/app/winlauncher/test/TestCrossProcessWin.cpp @@ -0,0 +1,365 @@ +/* -*- 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/. */ + +#define MOZ_USE_LAUNCHER_ERROR + +#include "freestanding/SharedSection.cpp" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/NativeNt.h" + +const wchar_t kChildArg[] = L"--child"; +const char kTestStrings[][6] = { + "a b c", "A B C", "a b c", "A B C", "X Y Z", +}; +const wchar_t kTestStringsMerged[] = + L"a b c" + L"\0" + L"X Y Z" + L"\0"; + +using namespace mozilla; +using namespace mozilla::freestanding; + +template <typename T, int N> +void PrintLauncherError(const LauncherResult<T>& aResult, + const char (&aMsg)[N]) { + const LauncherError& err = aResult.inspectErr(); + printf("TEST-FAILED | TestCrossProcessWin | %s - %lx at %s:%d\n", aMsg, + err.mError.AsHResult(), err.mFile, err.mLine); +} + +#define VERIFY_FUNCTION_RESOLVED(mod, exports, name) \ + do { \ + if (reinterpret_cast<FARPROC>(exports.m##name) != \ + ::GetProcAddress(mod, #name)) { \ + printf( \ + "TEST-FAILED | TestCrossProcessWin | " \ + "Kernel32ExportsSolver::" #name " did not match.\n"); \ + return false; \ + } \ + } while (0) + +static bool VerifySharedSection(SharedSection& aSharedSection) { + LauncherResult<SharedSection::Layout*> resultView = aSharedSection.GetView(); + if (resultView.isErr()) { + PrintLauncherError(resultView, "Failed to map a shared section"); + return false; + } + + SharedSection::Layout* view = resultView.unwrap(); + + // Use a local variable of RTL_RUN_ONCE to resolve Kernel32Exports every time + RTL_RUN_ONCE sRunEveryTime = RTL_RUN_ONCE_INIT; + view->mK32Exports.Resolve(sRunEveryTime); + + HMODULE k32mod = ::GetModuleHandleW(L"kernel32.dll"); + VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, FlushInstructionCache); + VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetModuleHandleW); + VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetSystemInfo); + VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, VirtualProtect); + + bool matched = memcmp(view->mModulePathArray, kTestStringsMerged, + sizeof(kTestStringsMerged)) == 0; + if (!matched) { + // Print actual strings on error + for (const wchar_t* p = view->mModulePathArray; *p;) { + printf("%p: %ls\n", p, p); + while (*p) { + ++p; + } + ++p; + } + return false; + } + + return true; +} + +static bool TestAddString() { + wchar_t testBuffer[3] = {0}; + UNICODE_STRING ustr; + + // This makes |testBuffer| full. + ::RtlInitUnicodeString(&ustr, L"a"); + if (!AddString(testBuffer, sizeof(testBuffer), ustr)) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "AddString failed.\n"); + return false; + } + + // Adding a string to a full buffer should fail. + ::RtlInitUnicodeString(&ustr, L"b"); + if (AddString(testBuffer, sizeof(testBuffer), ustr)) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "AddString caused OOB memory access.\n"); + return false; + } + + bool matched = memcmp(testBuffer, L"a\0", sizeof(testBuffer)) == 0; + if (!matched) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "AddString wrote wrong values.\n"); + return false; + } + + return true; +} + +class ChildProcess final { + nsAutoHandle mChildProcess; + nsAutoHandle mChildMainThread; + DWORD mProcessId; + + public: + // The following variables are updated from the parent process via + // WriteProcessMemory while the process is suspended as a part of + // TestWithChildProcess(). + // + // Having both a non-const and a const is important because a constant + // is separately placed in the .rdata section which is read-only, so + // the region's attribute needs to be changed before modifying data via + // WriteProcessMemory. + // The keyword "volatile" is needed for a constant, otherwise the compiler + // evaluates a constant as a literal without fetching data from memory. + static HMODULE sExecutableImageBase; + static volatile const DWORD sReadOnlyProcessId; + + static int Main() { + if (sExecutableImageBase != ::GetModuleHandle(nullptr)) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "sExecutableImageBase is expected to be %p, but actually was %p.\n", + ::GetModuleHandle(nullptr), sExecutableImageBase); + return 1; + } + + if (sReadOnlyProcessId != ::GetCurrentProcessId()) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "sReadOnlyProcessId is expected to be %lx, but actually was %lx.\n", + ::GetCurrentProcessId(), sReadOnlyProcessId); + return 1; + } + + auto getDependentModulePaths = + reinterpret_cast<const wchar_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. + if (getDependentModulePaths()) { + 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; + } + + // Test a scenario to transfer a transferred section as a readonly handle + static HANDLE copiedHandle = nullptr; + nt::CrossExecTransferManager tansferToSelf(::GetCurrentProcess()); + LauncherVoidResult result = gSharedSection.TransferHandle( + tansferToSelf, GENERIC_READ, &copiedHandle); + if (result.isErr()) { + PrintLauncherError(result, "SharedSection::TransferHandle(self) failed"); + return 1; + } + + gSharedSection.Reset(copiedHandle); + + UNICODE_STRING ustr; + ::RtlInitUnicodeString(&ustr, L"test"); + result = gSharedSection.AddDepenentModule(&ustr); + if (result.inspectErr() != + WindowsError::FromWin32Error(ERROR_ACCESS_DENIED)) { + PrintLauncherError(result, "The readonly section was writable"); + return 1; + } + + if (!VerifySharedSection(gSharedSection)) { + return 1; + } + + return 0; + } + + ChildProcess(const wchar_t* aExecutable, const wchar_t* aOption) + : mProcessId(0) { + const wchar_t* childArgv[] = {aExecutable, aOption}; + auto cmdLine( + mozilla::MakeCommandLine(mozilla::ArrayLength(childArgv), childArgv)); + + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi; + BOOL ok = + ::CreateProcessW(aExecutable, cmdLine.get(), nullptr, nullptr, FALSE, + CREATE_SUSPENDED, nullptr, nullptr, &si, &pi); + if (!ok) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "CreateProcessW falied - %08lx.\n", + GetLastError()); + return; + } + + mProcessId = pi.dwProcessId; + + mChildProcess.own(pi.hProcess); + mChildMainThread.own(pi.hThread); + } + + ~ChildProcess() { ::TerminateProcess(mChildProcess, 0); } + + operator HANDLE() const { return mChildProcess; } + DWORD GetProcessId() const { return mProcessId; } + + bool ResumeAndWaitUntilExit() { + if (::ResumeThread(mChildMainThread) == 0xffffffff) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "ResumeThread failed - %08lx\n", + GetLastError()); + return false; + } + + if (::WaitForSingleObject(mChildProcess, 60000) != WAIT_OBJECT_0) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "Unexpected result from WaitForSingleObject\n"); + return false; + } + + DWORD exitCode; + if (!::GetExitCodeProcess(mChildProcess, &exitCode)) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "GetExitCodeProcess failed - %08lx\n", + GetLastError()); + return false; + } + + return exitCode == 0; + } +}; + +HMODULE ChildProcess::sExecutableImageBase = 0; +volatile const DWORD ChildProcess::sReadOnlyProcessId = 0; + +int wmain(int argc, wchar_t* argv[]) { + printf("Process: %-8lx Base: %p\n", ::GetCurrentProcessId(), + ::GetModuleHandle(nullptr)); + + if (argc == 2 && wcscmp(argv[1], kChildArg) == 0) { + return ChildProcess::Main(); + } + + ChildProcess childProcess(argv[0], kChildArg); + if (!childProcess) { + return 1; + } + + if (!TestAddString()) { + return 1; + } + + LauncherResult<HMODULE> remoteImageBase = + nt::GetProcessExeModule(childProcess); + if (remoteImageBase.isErr()) { + PrintLauncherError(remoteImageBase, "nt::GetProcessExeModule failed"); + return 1; + } + + nt::CrossExecTransferManager transferMgr(childProcess); + if (!transferMgr) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "CrossExecTransferManager instantiation failed.\n"); + return 1; + } + + LauncherVoidResult result = + transferMgr.Transfer(&ChildProcess::sExecutableImageBase, + &remoteImageBase.inspect(), sizeof(HMODULE)); + if (result.isErr()) { + PrintLauncherError(result, "ChildProcess::WriteData(Imagebase) failed"); + return 1; + } + + DWORD childPid = childProcess.GetProcessId(); + + DWORD* readOnlyData = const_cast<DWORD*>(&ChildProcess::sReadOnlyProcessId); + result = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD)); + if (result.isOk()) { + printf( + "TEST-UNEXPECTED | TestCrossProcessWin | " + "A constant was located in a writable section."); + return 1; + } + + AutoVirtualProtect prot = + transferMgr.Protect(readOnlyData, sizeof(uint32_t), PAGE_READWRITE); + if (!prot) { + printf( + "TEST-FAILED | TestCrossProcessWin | " + "VirtualProtect failed - %08lx\n", + prot.GetError().AsHResult()); + return 1; + } + + result = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD)); + if (result.isErr()) { + PrintLauncherError(result, "ChildProcess::WriteData(PID) failed"); + return 1; + } + + { + // Define a scope for |sharedSection| to resume the child process after + // the section is deleted in the parent process. + result = gSharedSection.Init(transferMgr.LocalPEHeaders()); + if (result.isErr()) { + PrintLauncherError(result, "SharedSection::Init failed"); + return 1; + } + + for (const auto& testString : kTestStrings) { + nt::AllocatedUnicodeString ustr(testString); + result = gSharedSection.AddDepenentModule(ustr); + if (result.isErr()) { + PrintLauncherError(result, "SharedSection::AddDepenentModule failed"); + return 1; + } + } + + result = gSharedSection.TransferHandle(transferMgr, + GENERIC_READ | GENERIC_WRITE); + if (result.isErr()) { + PrintLauncherError(result, "SharedSection::TransferHandle failed"); + return 1; + } + } + + if (!childProcess.ResumeAndWaitUntilExit()) { + return 1; + } + + return 0; +} diff --git a/browser/app/winlauncher/test/TestSafeThreadLocal.cpp b/browser/app/winlauncher/test/TestSafeThreadLocal.cpp new file mode 100644 index 0000000000..5aa122e16e --- /dev/null +++ b/browser/app/winlauncher/test/TestSafeThreadLocal.cpp @@ -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/. */ + +#define MOZ_USE_LAUNCHER_ERROR + +#include "freestanding/SafeThreadLocal.h" + +#include "mozilla/NativeNt.h" +#include "nsWindowsHelpers.h" + +#include <process.h> +#include <stdio.h> + +// Need a non-inline function to bypass compiler optimization that the thread +// local storage pointer is cached in a register before accessing a thread-local +// variable. +MOZ_NEVER_INLINE PVOID SwapThreadLocalStoragePointer(PVOID aNewValue) { + auto oldValue = mozilla::nt::RtlGetThreadLocalStoragePointer(); + mozilla::nt::RtlSetThreadLocalStoragePointerForTestingOnly(aNewValue); + return oldValue; +} + +static mozilla::freestanding::SafeThreadLocal<int*> gTheStorage; + +static unsigned int __stdcall TestNonMainThread(void* aArg) { + for (int i = 0; i < 100; ++i) { + gTheStorage.set(&i); + if (gTheStorage.get() != &i) { + printf( + "TEST-FAILED | TestSafeThreadLocal | " + "A value is not correctly stored in the thread-local storage.\n"); + return 1; + } + } + return 0; +} + +extern "C" int wmain(int argc, wchar_t* argv[]) { + int dummy = 0x1234; + + auto origHead = SwapThreadLocalStoragePointer(nullptr); + // Setting gTheStorage when TLS is null. + gTheStorage.set(&dummy); + SwapThreadLocalStoragePointer(origHead); + + nsAutoHandle handles[8]; + for (auto& handle : handles) { + handle.own(reinterpret_cast<HANDLE>( + ::_beginthreadex(nullptr, 0, TestNonMainThread, nullptr, 0, nullptr))); + } + + for (int i = 0; i < 100; ++i) { + if (gTheStorage.get() != &dummy) { + printf( + "TEST-FAILED | TestSafeThreadLocal | " + "A value is not correctly stored in the global scope.\n"); + return 1; + } + } + + for (auto& handle : handles) { + ::WaitForSingleObject(handle, INFINITE); + + DWORD exitCode; + if (!::GetExitCodeThread(handle, &exitCode) || exitCode) { + return 1; + } + } + + return 0; +} diff --git a/browser/app/winlauncher/test/TestSameBinary.cpp b/browser/app/winlauncher/test/TestSameBinary.cpp new file mode 100644 index 0000000000..2cb45f546f --- /dev/null +++ b/browser/app/winlauncher/test/TestSameBinary.cpp @@ -0,0 +1,255 @@ +/* -*- 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/. */ + +#define MOZ_USE_LAUNCHER_ERROR + +#include <stdio.h> +#include <stdlib.h> + +#include <utility> + +#include "SameBinary.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/NativeNt.h" +#include "mozilla/Unused.h" +#include "mozilla/Vector.h" +#include "mozilla/WinHeaderOnlyUtils.h" +#include "nsWindowsHelpers.h" + +#define EXPECT_SAMEBINARY_IS(expected, option, message) \ + do { \ + mozilla::LauncherResult<bool> isSame = \ + mozilla::IsSameBinaryAsParentProcess(option); \ + if (isSame.isErr()) { \ + PrintLauncherError(isSame, \ + "IsSameBinaryAsParentProcess returned error " \ + "when we were expecting success."); \ + return 1; \ + } \ + if (isSame.unwrap() != expected) { \ + PrintErrorMsg(message); \ + return 1; \ + } \ + } while (0) + +/** + * This test involves three processes: + * 1. The "Monitor" process, which is executed by |MonitorMain|. This process + * is responsible for integrating with the test harness, so it spawns the + * "Parent" process (2), and then waits for the other two processes to + * finish. + * 2. The "Parent" process, which is executed by |ParentMain|. This process + * creates the "Child" process (3) and then waits indefinitely. + * 3. The "Child" process, which is executed by |ChildMain| and carries out + * the actual test. It terminates the Parent process during its execution, + * using the Child PID as the Parent process's exit code. This serves as a + * hacky yet effective way to signal to the Monitor process which PID it + * should wait on to ensure that the Child process has exited. + */ + +static const char kMsgStart[] = "TEST-FAILED | SameBinary | "; + +inline void PrintErrorMsg(const char* aMsg) { + printf("%s%s\n", kMsgStart, aMsg); +} + +inline void PrintWinError(const char* aMsg) { + mozilla::WindowsError err(mozilla::WindowsError::FromLastError()); + printf("%s%s: %S\n", kMsgStart, aMsg, err.AsString().get()); +} + +template <typename T> +inline void PrintLauncherError(const mozilla::LauncherResult<T>& aResult, + const char* aMsg = nullptr) { + const char* const kSep = aMsg ? ": " : ""; + const char* msg = aMsg ? aMsg : ""; + const mozilla::LauncherError& err = aResult.inspectErr(); + printf("%s%s%s%S (%s:%d)\n", kMsgStart, msg, kSep, + err.mError.AsString().get(), err.mFile, err.mLine); +} + +static int ChildMain(DWORD aExpectedParentPid) { + mozilla::LauncherResult<DWORD> parentPid = mozilla::nt::GetParentProcessId(); + if (parentPid.isErr()) { + PrintLauncherError(parentPid); + return 1; + } + + if (parentPid.inspect() != aExpectedParentPid) { + PrintErrorMsg("Unexpected mismatch of parent PIDs"); + return 1; + } + + const DWORD kAccess = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE; + nsAutoHandle parentProcess( + ::OpenProcess(kAccess, FALSE, parentPid.inspect())); + if (!parentProcess) { + PrintWinError("Unexpectedly failed to call OpenProcess on parent"); + return 1; + } + + EXPECT_SAMEBINARY_IS( + true, mozilla::ImageFileCompareOption::Default, + "IsSameBinaryAsParentProcess returned incorrect result for identical " + "binaries"); + EXPECT_SAMEBINARY_IS( + true, mozilla::ImageFileCompareOption::CompareNtPathsOnly, + "IsSameBinaryAsParentProcess(CompareNtPathsOnly) returned incorrect " + "result for identical binaries"); + + // Total hack, but who cares? We'll set the parent's exit code as our PID + // so that the monitor process knows who to wait for! + if (!::TerminateProcess(parentProcess.get(), ::GetCurrentProcessId())) { + PrintWinError("Unexpected failure in TerminateProcess"); + return 1; + } + + // Close our handle to the parent process so that no references are held. + ::CloseHandle(parentProcess.disown()); + + // Querying a pid on a terminated process may still succeed some time after + // that process has been terminated. For the purposes of this test, we'll poll + // the OS until we cannot succesfully open the parentPid anymore. + const uint32_t kMaxAttempts = 100; + uint32_t curAttempt = 0; + while (HANDLE p = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, + parentPid.inspect())) { + ::CloseHandle(p); + ::Sleep(100); + ++curAttempt; + if (curAttempt >= kMaxAttempts) { + PrintErrorMsg( + "Exhausted retry attempts waiting for parent pid to become invalid"); + return 1; + } + } + + EXPECT_SAMEBINARY_IS( + false, mozilla::ImageFileCompareOption::Default, + "IsSameBinaryAsParentProcess returned incorrect result for dead parent " + "process"); + EXPECT_SAMEBINARY_IS( + false, mozilla::ImageFileCompareOption::CompareNtPathsOnly, + "IsSameBinaryAsParentProcess(CompareNtPathsOnly) returned incorrect " + "result for dead parent process"); + + return 0; +} + +static nsReturnRef<HANDLE> CreateSelfProcess(int argc, wchar_t* argv[]) { + nsAutoHandle empty; + + DWORD myPid = ::GetCurrentProcessId(); + + wchar_t strPid[11] = {}; +#if defined(__MINGW32__) + _ultow(myPid, strPid, 16); +#else + if (_ultow_s(myPid, strPid, 16)) { + PrintErrorMsg("_ultow_s failed"); + return empty.out(); + } +#endif // defined(__MINGW32__) + + wchar_t* extraArgs[] = {strPid}; + + auto cmdLine = mozilla::MakeCommandLine( + argc, argv, mozilla::ArrayLength(extraArgs), extraArgs); + if (!cmdLine) { + PrintErrorMsg("MakeCommandLine failed"); + return empty.out(); + } + + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi; + BOOL ok = + ::CreateProcessW(argv[0], cmdLine.get(), nullptr, nullptr, FALSE, + CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, &si, &pi); + if (!ok) { + PrintWinError("CreateProcess failed"); + return empty.out(); + } + + nsAutoHandle proc(pi.hProcess); + nsAutoHandle thd(pi.hThread); + + return proc.out(); +} + +static int ParentMain(int argc, wchar_t* argv[]) { + nsAutoHandle childProc(CreateSelfProcess(argc, argv)); + if (!childProc) { + return 1; + } + + if (::WaitForSingleObject(childProc.get(), INFINITE) != WAIT_OBJECT_0) { + PrintWinError( + "Unexpected result from WaitForSingleObject on child process"); + return 1; + } + + MOZ_ASSERT_UNREACHABLE("This process should be terminated by now"); + return 0; +} + +static int MonitorMain(int argc, wchar_t* argv[]) { + // In this process, "parent" means the process that will be running + // ParentMain, which is our child process (confusing, I know...) + nsAutoHandle parentProc(CreateSelfProcess(argc, argv)); + if (!parentProc) { + return 1; + } + + if (::WaitForSingleObject(parentProc.get(), 60000) != WAIT_OBJECT_0) { + PrintWinError("Unexpected result from WaitForSingleObject on parent"); + return 1; + } + + DWORD childPid; + if (!::GetExitCodeProcess(parentProc.get(), &childPid)) { + PrintWinError("GetExitCodeProcess failed"); + return 1; + } + + nsAutoHandle childProc(::OpenProcess(SYNCHRONIZE, FALSE, childPid)); + if (!childProc) { + // Nothing to wait on anymore, which is OK. + return 0; + } + + // We want no more references to parentProc + ::CloseHandle(parentProc.disown()); + + if (::WaitForSingleObject(childProc.get(), 60000) != WAIT_OBJECT_0) { + PrintWinError("Unexpected result from WaitForSingleObject on child"); + return 1; + } + + return 0; +} + +extern "C" int wmain(int argc, wchar_t* argv[]) { + if (argc == 3) { + return ChildMain(wcstoul(argv[2], nullptr, 16)); + } + + if (!mozilla::SetArgv0ToFullBinaryPath(argv)) { + return 1; + } + + if (argc == 1) { + return MonitorMain(argc, argv); + } + + if (argc == 2) { + return ParentMain(argc, argv); + } + + PrintErrorMsg("Unexpected argc"); + return 1; +} diff --git a/browser/app/winlauncher/test/moz.build b/browser/app/winlauncher/test/moz.build new file mode 100644 index 0000000000..a8503ddf55 --- /dev/null +++ b/browser/app/winlauncher/test/moz.build @@ -0,0 +1,30 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DisableStlWrapping() + +GeckoCppUnitTests( + [ + "TestCrossProcessWin", + "TestSafeThreadLocal", + "TestSameBinary", + ], + linkage=None, +) + +LOCAL_INCLUDES += [ + "/browser/app/winlauncher", +] + +OS_LIBS += [ + "ntdll", +] + +if CONFIG["CC_TYPE"] in ("gcc", "clang"): + # This allows us to use wmain as the entry point on mingw + LDFLAGS += [ + "-municode", + ] |