Bug 1240315: Add startup crash report annotation for AppInit_DLLs; r=jimm

This commit is contained in:
Aaron Klotz 2016-01-16 14:09:28 -07:00
parent 32b718d585
commit 898a717959
3 changed files with 188 additions and 0 deletions

View File

@ -159,6 +159,7 @@
#include "nsThreadUtils.h"
#include <comdef.h>
#include <wbemidl.h>
#include "WinUtils.h"
#endif
#ifdef XP_MACOSX
@ -3330,6 +3331,15 @@ XREMain::XRE_mainInit(bool* aExitFlag)
IsSignalHandlingBroken() ? NS_LITERAL_CSTRING("1")
: NS_LITERAL_CSTRING("0"));
#endif
#ifdef XP_WIN
nsAutoString appInitDLLs;
if (widget::WinUtils::GetAppInitDLLs(appInitDLLs)) {
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AppInitDLLs"),
NS_ConvertUTF16toUTF8(appInitDLLs));
}
#endif
CrashReporter::SetRestartArgs(gArgc, gArgv);
// annotate other data (user id etc)

View File

@ -48,6 +48,7 @@
#include "nsIThread.h"
#include "MainThreadUtils.h"
#include "nsLookAndFeel.h"
#include "nsUnicharUtils.h"
#include "nsWindowsHelpers.h"
#ifdef NS_ENABLE_TSF
@ -55,6 +56,8 @@
#include "TSFTextStore.h"
#endif // #ifdef NS_ENABLE_TSF
#include <shlwapi.h>
PRLogModuleInfo* gWindowsLog = nullptr;
using namespace mozilla::gfx;
@ -1901,5 +1904,165 @@ WinUtils::ResolveMovedUsersFolder(std::wstring& aPath)
return true;
}
/* static */
bool
WinUtils::SanitizePath(const wchar_t* aInputPath, nsAString& aOutput)
{
aOutput.Truncate();
wchar_t buffer[MAX_PATH + 1] = {0};
if (!PathCanonicalizeW(buffer, aInputPath)) {
return false;
}
wchar_t longBuffer[MAX_PATH + 1] = {0};
DWORD longResult = GetLongPathNameW(buffer, longBuffer, MAX_PATH);
if (longResult == 0 || longResult > MAX_PATH - 1) {
return false;
}
aOutput.SetLength(MAX_PATH + 1);
if (!PathUnExpandEnvStringsW(longBuffer, aOutput.BeginWriting(), MAX_PATH)) {
return false;
}
// Truncate to correct length
aOutput.Truncate(wcslen(aOutput.BeginReading()));
MOZ_ASSERT(aOutput.Length() <= MAX_PATH);
return true;
}
/**
* This function provides an array of (system path, substitution) pairs that are
* considered to be acceptable with respect to privacy, for the purposes of
* submitting within telemetry or crash reports.
*
* The substitution string's void flag may be set. If it is, no subsitution is
* necessary. Otherwise, the consumer should replace the system path with the
* substitution.
*
* @see GetAppInitDLLs for an example of its usage.
*/
/* static */
void
WinUtils::GetWhitelistedPaths(
nsTArray<mozilla::Pair<nsString,nsDependentString>>& aOutput)
{
aOutput.Clear();
aOutput.AppendElement(mozilla::MakePair(
nsString(NS_LITERAL_STRING("%ProgramFiles%")),
nsDependentString()));
// When no substitution is required, set the void flag
aOutput.LastElement().second().SetIsVoid(true);
wchar_t tmpPath[MAX_PATH + 1] = {0};
if (GetTempPath(MAX_PATH, tmpPath)) {
// GetTempPath's result always ends with a backslash, which we don't want
uint32_t tmpPathLen = wcslen(tmpPath);
if (tmpPathLen) {
tmpPath[tmpPathLen - 1] = 0;
}
nsAutoString cleanTmpPath;
if (SanitizePath(tmpPath, cleanTmpPath)) {
aOutput.AppendElement(mozilla::MakePair(nsString(cleanTmpPath),
nsDependentString(L"%TEMP%")));
}
}
}
/**
* This function is located here (as opposed to nsSystemInfo or elsewhere)
* because we need to gather this information as early as possible during
* startup.
*/
/* static */
bool
WinUtils::GetAppInitDLLs(nsAString& aOutput)
{
aOutput.Truncate();
HKEY hkey = NULL;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
0, KEY_QUERY_VALUE, &hkey)) {
return false;
}
nsAutoRegKey key(hkey);
LONG status;
if (IsVistaOrLater()) {
const wchar_t kLoadAppInitDLLs[] = L"LoadAppInit_DLLs";
DWORD loadAppInitDLLs = 0;
DWORD loadAppInitDLLsLen = sizeof(loadAppInitDLLs);
status = RegQueryValueExW(hkey, kLoadAppInitDLLs, nullptr,
nullptr, (LPBYTE)&loadAppInitDLLs,
&loadAppInitDLLsLen);
if (status != ERROR_SUCCESS) {
return false;
}
if (!loadAppInitDLLs) {
// If loadAppInitDLLs is zero then AppInit_DLLs is disabled.
// In this case we'll return true along with an empty output string.
return true;
}
}
DWORD numBytes = 0;
const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
// Query for required buffer size
status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr, nullptr,
&numBytes);
if (status != ERROR_SUCCESS) {
return false;
}
// Allocate the buffer and query for the actual data
mozilla::UniquePtr<wchar_t[]> data =
mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
nullptr, (LPBYTE)data.get(), &numBytes);
if (status != ERROR_SUCCESS) {
return false;
}
nsTArray<mozilla::Pair<nsString,nsDependentString>> whitelistedPaths;
GetWhitelistedPaths(whitelistedPaths);
// For each token, split up the filename components and then check the
// name of the file.
const wchar_t kDelimiters[] = L", ";
wchar_t* tokenContext = nullptr;
wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
while (token) {
nsAutoString cleanPath;
// Since these paths are short paths originating from the registry, we need
// to canonicalize them, lengthen them, and sanitize them before we can
// check them against the whitelist
if (SanitizePath(token, cleanPath)) {
bool needsStrip = true;
for (uint32_t i = 0; i < whitelistedPaths.Length(); ++i) {
const nsString& testPath = whitelistedPaths[i].first();
const nsDependentString& substitution = whitelistedPaths[i].second();
if (StringBeginsWith(cleanPath, testPath,
nsCaseInsensitiveStringComparator())) {
if (!substitution.IsVoid()) {
cleanPath.Replace(0, testPath.Length(), substitution);
}
// Whitelisted paths may be used as-is provided that they have been
// previously sanitized.
needsStrip = false;
break;
}
}
if (!aOutput.IsEmpty()) {
aOutput += L";";
}
// For non-whitelisted paths, we strip the path component and just leave
// the filename.
if (needsStrip) {
// nsLocalFile doesn't like non-absolute paths. Since these paths might
// contain environment variables instead of roots, we can't use it.
wchar_t tmpPath[MAX_PATH + 1] = {0};
wcsncpy(tmpPath, cleanPath.get(), cleanPath.Length());
PathStripPath(tmpPath);
aOutput += tmpPath;
} else {
aOutput += cleanPath;
}
}
token = wcstok_s(nullptr, kDelimiters, &tokenContext);
}
return true;
}
} // namespace widget
} // namespace mozilla

View File

@ -445,6 +445,18 @@ public:
static bool ShouldHideScrollbars();
/**
* This function normalizes the input path, converts short filenames to long
* filenames, and substitutes environment variables for system paths.
* The resulting output string length is guaranteed to be <= MAX_PATH.
*/
static bool SanitizePath(const wchar_t* aInputPath, nsAString& aOutput);
/**
* Retrieve a semicolon-delimited list of DLL files derived from AppInit_DLLs
*/
static bool GetAppInitDLLs(nsAString& aOutput);
private:
typedef HRESULT (WINAPI * SHCreateItemFromParsingNamePtr)(PCWSTR pszPath,
IBindCtx *pbc,
@ -456,6 +468,9 @@ private:
HANDLE hToken,
PWSTR *ppszPath);
static SHGetKnownFolderPathPtr sGetKnownFolderPath;
static void GetWhitelistedPaths(
nsTArray<mozilla::Pair<nsString,nsDependentString>>& aOutput);
};
#ifdef MOZ_PLACES