Bug 1746114 - Part1. Avoid delayloading shlwapi.dll in a sandboxed process. r=mhowell

While processing third-party module loading events, the process may delayload
shlwapi.dll.  This could be a problem if the process is sandboxed and the access
to local files is restricted.  Before enabling the third-party modules ping in
socket process, this patch avoids such delayloading.

The first path is `ParamTraits<mozilla::ModuleRecord>::Read` calls `NS_NewLocalFile`
that calls `PathGetDriveNumberW`.  This API is simple enough that we define our own
version.

The second path is the ctor of `ProcessedModuleLoadEvent` that is called from
`UntrustedModulesProcessor::CompleteProcessing` calls `PreparePathForTelemetry`
that calls `PathFindFileNameW` and `PathCanonicalizeW`.  For this path, instead
of sanitizing a requested name in `ProcessedModuleLoadEvent`'s ctor, we sanitize
it when transferring it to the main process.

Differential Revision: https://phabricator.services.mozilla.com/D134492
This commit is contained in:
Toshihito Kikuchi 2021-12-23 02:15:35 +00:00
Родитель 0ddbd1d7f7
Коммит 7d38851282
6 изменённых файлов: 94 добавлений и 8 удалений

Просмотреть файл

@ -220,13 +220,25 @@ ProcessedModuleLoadEvent::ProcessedModuleLoadEvent(
return;
}
// Sanitize the requested DLL name. It is not a critical failure if we
// cannot do so; we simply do not provide that field to Telemetry.
nsAutoString strRequested(
aModLoadInfo.mNtLoadInfo.mRequestedDllName.AsString());
if (!strRequested.IsEmpty() &&
widget::WinUtils::PreparePathForTelemetry(strRequested)) {
mRequestedDllName = strRequested;
mRequestedDllName = aModLoadInfo.mNtLoadInfo.mRequestedDllName.AsString();
// If we're in the main process, sanitize the requested DLL name here.
// If not, we cannot use PreparePathForTelemetry because it may try to
// delayload shlwapi.dll and could fail if the process is sandboxed.
// We leave mRequestedDllName unsanitized here and sanitize it when
// transferring it to the main process.
// (See ParamTraits<mozilla::UntrustedModulesData>::ReadEvent)
if (XRE_IsParentProcess()) {
SanitizeRequestedDllName();
}
}
void ProcessedModuleLoadEvent::SanitizeRequestedDllName() {
if (!mRequestedDllName.IsEmpty() &&
!widget::WinUtils::PreparePathForTelemetry(mRequestedDllName)) {
// If we cannot sanitize a path, we simply do not provide that field to
// Telemetry.
mRequestedDllName.Truncate();
}
}

Просмотреть файл

@ -152,6 +152,8 @@ class ProcessedModuleLoadEvent final {
ProcessedModuleLoadEvent(ProcessedModuleLoadEvent&&) = default;
ProcessedModuleLoadEvent& operator=(ProcessedModuleLoadEvent&&) = default;
void SanitizeRequestedDllName();
private:
static Maybe<LONGLONG> ComputeQPCTimeStampForProcessCreation();
static uint64_t QPCTimeStampToProcessUptimeMilliseconds(
@ -553,6 +555,10 @@ struct ParamTraits<mozilla::UntrustedModulesData> {
return false;
}
// When ProcessedModuleLoadEvent was constructed in a child process, we left
// mRequestedDllName unsanitized, so now is a good time to sanitize it.
aResult->SanitizeRequestedDllName();
if (!ReadParam(aMsg, aIter, &aResult->mBaseAddress)) {
return false;
}

Просмотреть файл

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/WinHeaderOnlyUtils.h"
#include <shlwapi.h>
TEST(WinHeaderOnlyUtils, MozPathGetDriveNumber)
{
constexpr auto TestPathGetDriveNumber = [](const wchar_t* aPath) {
return mozilla::MozPathGetDriveNumber(aPath) ==
::PathGetDriveNumberW(aPath);
};
EXPECT_TRUE(TestPathGetDriveNumber(nullptr));
EXPECT_TRUE(TestPathGetDriveNumber(L""));
EXPECT_TRUE(TestPathGetDriveNumber(L" :"));
EXPECT_TRUE(TestPathGetDriveNumber(L"a:"));
EXPECT_TRUE(TestPathGetDriveNumber(L"C:\\file"));
EXPECT_TRUE(TestPathGetDriveNumber(L"x:/file"));
EXPECT_TRUE(TestPathGetDriveNumber(L"@:\\\\"));
EXPECT_TRUE(TestPathGetDriveNumber(L"B"));
EXPECT_TRUE(TestPathGetDriveNumber(L"abc:\\file"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\:A"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\\\"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\\\\\"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\\\?"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\\\?\\"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\\\?\\\\"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\\\?\\c:\\"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\\\?\\A"));
EXPECT_TRUE(TestPathGetDriveNumber(L"\\\\?\\ \\"));
}

Просмотреть файл

@ -12,6 +12,7 @@ UNIFIED_SOURCES = [
if CONFIG["OS_ARCH"] == "WINNT":
UNIFIED_SOURCES += [
"MockWinWidget.cpp",
"TestWinHeaderOnlyUtils.cpp",
"TestWinWindowOcclusionTracker.cpp",
"TestWinWindowOcclusionTrackerInteractive.cpp",
]

Просмотреть файл

@ -789,6 +789,35 @@ inline UniquePtr<wchar_t[]> GetPackageFamilyName() {
return packageIdentity;
}
// This implementation is equivalent to PathGetDriveNumber[AW].
// We define our own version because using PathGetDriveNumber
// delay-loads shlwapi.dll, which may fail when the process is
// sandboxed.
template <typename T>
int MozPathGetDriveNumber(const T* aPath) {
const auto ToDriveNumber = [](const T* aPath) -> int {
if (*aPath == '\0' || *(aPath + 1) != ':') {
return -1;
}
T c = *aPath;
return (c >= 'A' && c <= 'Z') ? c - 'A'
: (c >= 'a' && c <= 'z') ? c - 'a'
: -1;
};
if (!aPath) {
return -1;
}
if (*aPath == '\\' && *(aPath + 1) == '\\' && *(aPath + 2) == '?' &&
*(aPath + 3) == '\\') {
return ToDriveNumber(aPath + 4);
}
return ToDriveNumber(aPath);
}
} // namespace mozilla
#endif // mozilla_WinHeaderOnlyUtils_h

Просмотреть файл

@ -10,6 +10,7 @@
#include "mozilla/TextUtils.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/Utf8.h"
#include "mozilla/WinHeaderOnlyUtils.h"
#include "nsCOMPtr.h"
#include "nsMemory.h"
@ -1053,7 +1054,7 @@ nsLocalFile::InitWithPath(const nsAString& aFilePath) {
if (secondChar == L':') {
// Make sure we have a valid drive, later code assumes the drive letter
// is a single char a-z or A-Z.
if (PathGetDriveNumberW(aFilePath.Data()) == -1) {
if (MozPathGetDriveNumber<wchar_t>(aFilePath.Data()) == -1) {
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
}