Bug 1392485 - Hook QueryDosDeviceW() in GMP process so it works inside the sandbox. r=bobowen, r=dmajor, a=lizzard
authorChris Pearce <cpearce@mozilla.com>
Wed, 23 Aug 2017 10:56:41 +1200
changeset 423895 956fb847d294678813d88551f87980151b5d9cf9
parent 423894 4b1ef71961265c0cac87732c2b09cf7861ba2176
child 423896 55b8650bcc3bdbaca143433c8ad121ec80bbfa34
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbobowen, dmajor, lizzard
bugs1392485
milestone56.0
Bug 1392485 - Hook QueryDosDeviceW() in GMP process so it works inside the sandbox. r=bobowen, r=dmajor, a=lizzard We hook in kernelbase.dll rather than kernel32.dll, as hooking QueryDosDeviceW kernel32.dll is failing on our Win8 tests, it seems because QueryDosDeviceW in kernel32.dll redirects to kernelbase32.dll, and the redirect has insufficient space for our hook in Win8. So hook in kernelbase.dll, where the redirect redirects to instead. MozReview-Commit-ID: JKRiKCd7Ibn
dom/media/gmp/ChromiumCDMAdapter.cpp
toolkit/xre/test/win/TestDllInterceptor.cpp
--- a/dom/media/gmp/ChromiumCDMAdapter.cpp
+++ b/dom/media/gmp/ChromiumCDMAdapter.cpp
@@ -10,30 +10,45 @@
 #include "gmp-api/gmp-entrypoints.h"
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-video-codec.h"
 #include "gmp-api/gmp-platform.h"
 #include "WidevineUtils.h"
 #include "GMPLog.h"
 #include "mozilla/Move.h"
 
-#ifndef XP_WIN
+#ifdef XP_WIN
+#include "WinUtils.h"
+#include "nsWindowsDllInterceptor.h"
+#include <windows.h>
+#include <strsafe.h>
+#include <unordered_map>
+#include <vector>
+#else
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
 #endif
 
 // Declared in WidevineAdapter.cpp.
 extern const GMPPlatformAPI* sPlatform;
 
 namespace mozilla {
 
+#ifdef XP_WIN
+static void
+InitializeHooks();
+#endif
+
 ChromiumCDMAdapter::ChromiumCDMAdapter(nsTArray<Pair<nsCString, nsCString>>&& aHostPathPairs)
 {
+#ifdef XP_WIN
+  InitializeHooks();
+#endif
   PopulateHostFiles(Move(aHostPathPairs));
 }
 
 void
 ChromiumCDMAdapter::SetAdaptee(PRLibrary* aLib)
 {
   mLib = aLib;
 }
@@ -164,16 +179,109 @@ ChromiumCDMAdapter::Supports(int32_t aMo
                              int32_t aInterfaceVersion,
                              int32_t aHostVersion)
 {
   return aModuleVersion == CDM_MODULE_VERSION &&
          aInterfaceVersion == cdm::ContentDecryptionModule_8::kVersion &&
          aHostVersion == cdm::Host_8::kVersion;
 }
 
+#ifdef XP_WIN
+
+static WindowsDllInterceptor sKernel32Intercept;
+
+typedef DWORD(WINAPI* QueryDosDeviceWFnPtr)(_In_opt_ LPCWSTR lpDeviceName,
+                                            _Out_ LPWSTR lpTargetPath,
+                                            _In_ DWORD ucchMax);
+
+static QueryDosDeviceWFnPtr sOriginalQueryDosDeviceWFnPtr = nullptr;
+
+static std::unordered_map<std::wstring, std::wstring>* sDeviceNames = nullptr;
+
+DWORD WINAPI
+QueryDosDeviceWHook(LPCWSTR lpDeviceName, LPWSTR lpTargetPath, DWORD ucchMax)
+{
+  if (!sDeviceNames) {
+    return 0;
+  }
+  std::wstring name = std::wstring(lpDeviceName);
+  auto iter = sDeviceNames->find(name);
+  if (iter == sDeviceNames->end()) {
+    return 0;
+  }
+  const std::wstring& device = iter->second;
+  if (device.size() + 1 > ucchMax) {
+    return 0;
+  }
+  PodCopy(lpTargetPath, device.c_str(), device.size());
+  lpTargetPath[device.size()] = 0;
+  GMP_LOG("QueryDosDeviceWHook %S -> %S", lpDeviceName, lpTargetPath);
+  return name.size();
+}
+
+static std::vector<std::wstring>
+GetDosDeviceNames()
+{
+  std::vector<std::wstring> v;
+  std::vector<wchar_t> buf;
+  buf.resize(1024);
+  DWORD rv = GetLogicalDriveStringsW(buf.size(), buf.data());
+  if (rv == 0 || rv > buf.size()) {
+    return v;
+  }
+
+  // buf will be a list of null terminated strings, with the last string
+  // being 0 length.
+  const wchar_t* p = buf.data();
+  const wchar_t* end = &buf.back();
+  size_t l;
+  while (p < end && (l = wcsnlen_s(p, end - p)) > 0) {
+    // The string is of the form "C:\". We need to strip off the trailing
+    // backslash.
+    std::wstring drive = std::wstring(p, p + l);
+    if (drive.back() == '\\') {
+      drive.erase(drive.end() - 1);
+    }
+    v.push_back(move(drive));
+    p += l + 1;
+  }
+  return v;
+}
+
+static std::wstring
+GetDeviceMapping(const std::wstring& aDosDeviceName)
+{
+  wchar_t buf[MAX_PATH] = { 0 };
+  DWORD rv = QueryDosDeviceW(aDosDeviceName.c_str(), buf, MAX_PATH);
+  if (rv == 0) {
+    return std::wstring(L"");
+  }
+  return std::wstring(buf, buf + rv);
+}
+
+static void
+InitializeHooks()
+{
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+  sDeviceNames = new std::unordered_map<std::wstring, std::wstring>();
+  for (const std::wstring& name : GetDosDeviceNames()) {
+    sDeviceNames->emplace(name, GetDeviceMapping(name));
+  }
+
+  sKernel32Intercept.Init("kernelbase.dll");
+  sKernel32Intercept.AddHook("QueryDosDeviceW",
+                             reinterpret_cast<intptr_t>(QueryDosDeviceWHook),
+                             (void**)(&sOriginalQueryDosDeviceWFnPtr));
+}
+#endif
+
 HostFile::HostFile(HostFile&& aOther)
   : mPath(aOther.mPath)
   , mFile(aOther.TakePlatformFile())
 {
 }
 
 HostFile::~HostFile()
 {
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -311,16 +311,23 @@ bool TestCreateFileA(void* aFunc)
   auto patchedCreateFileA =
     reinterpret_cast<decltype(&CreateFileA)>(aFunc);
 //  return patchedCreateFileA(0, 0, 0, 0, 0, 0, 0) == INVALID_HANDLE_VALUE;
   printf("TEST-SKIPPED | WindowsDllInterceptor | "
           "Will not attempt to execute patched CreateFileA -- patched method is known to fail.\n");
   return true;
 }
 
+bool TestQueryDosDeviceW(void* aFunc)
+{
+  auto patchedQueryDosDeviceW =
+    reinterpret_cast<decltype(&QueryDosDeviceW)>(aFunc);
+  return patchedQueryDosDeviceW(nullptr, nullptr, 0) == 0;
+}
+
 bool TestInSendMessageEx(void* aFunc)
 {
   auto patchedInSendMessageEx =
     reinterpret_cast<decltype(&InSendMessageEx)>(aFunc);
   patchedInSendMessageEx(0);
   return true;
 }
 
@@ -504,16 +511,17 @@ int main()
 #ifdef _M_IX86
       // Bug 670967: xpcom/base/AvailableMemoryTracker.cpp
       TestHook(TestVirtualAlloc, "kernel32.dll", "VirtualAlloc") &&
       TestHook(TestMapViewOfFile, "kernel32.dll", "MapViewOfFile") &&
       TestHook(TestCreateDIBSection, "gdi32.dll", "CreateDIBSection") &&
       TestHook(TestCreateFileW, "kernel32.dll", "CreateFileW") &&    // see Bug 1316415
 #endif
       TestHook(TestCreateFileA, "kernel32.dll", "CreateFileA") &&
+      TestHook(TestQueryDosDeviceW, "kernelbase.dll", "QueryDosDeviceW") &&
       TestDetour("user32.dll", "CreateWindowExW") &&
       TestHook(TestInSendMessageEx, "user32.dll", "InSendMessageEx") &&
       TestHook(TestImmGetContext, "imm32.dll", "ImmGetContext") &&
       // TestHook("imm32.dll", "ImmReleaseContext") &&    // see Bug 1316415
       TestHook(TestImmGetCompositionStringW, "imm32.dll", "ImmGetCompositionStringW") &&
       TestHook(TestImmSetCandidateWindow, "imm32.dll", "ImmSetCandidateWindow") &&
       TestHook(TestImmNotifyIME, "imm32.dll", "ImmNotifyIME") &&
       TestHook(TestGetSaveFileNameW, "comdlg32.dll", "GetSaveFileNameW") &&