Bug 1346620 - Resolve symlinks and junction points in path to GMP dir when loading GMP process. r=bobowen
authorChris Pearce <cpearce@mozilla.com>
Fri, 09 Jun 2017 15:29:46 +1200
changeset 413660 552aa9d9f4f956e0def642e7c61c3e57a57aa9a1
parent 413659 6d3603a87085e02c0123574d97a73b8baf1234d0
child 413661 df527bcc28dc918dc87914c9840cbde63bfdb7e0
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbobowen
bugs1346620
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1346620 - Resolve symlinks and junction points in path to GMP dir when loading GMP process. r=bobowen The sandbox blocks loading of GMPs when the GMP resides in a directory stored in a path which contains a symlink or junction point. So resolve GMP paths fully before instantiating the GMP process. MozReview-Commit-ID: EvPCpNIDNwg
dom/media/gmp/GMPProcessParent.cpp
widget/windows/WinUtils.cpp
widget/windows/WinUtils.h
--- a/dom/media/gmp/GMPProcessParent.cpp
+++ b/dom/media/gmp/GMPProcessParent.cpp
@@ -6,16 +6,17 @@
 
 #include "GMPProcessParent.h"
 #include "GMPUtils.h"
 #include "nsIFile.h"
 #include "nsIRunnable.h"
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 #include "WinUtils.h"
 #endif
+#include "GMPLog.h"
 
 #include "base/string_util.h"
 #include "base/process_util.h"
 
 #include <string>
 
 using std::vector;
 using std::string;
@@ -46,20 +47,23 @@ GMPProcessParent::Launch(int32_t aTimeou
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
   std::wstring wGMPPath = UTF8ToWide(mGMPPath.c_str());
 
   // The sandbox doesn't allow file system rules where the paths contain
   // symbolic links or junction points. Sometimes the Users folder has been
   // moved to another drive using a junction point, so allow for this specific
   // case. See bug 1236680 for details.
-  if (!widget::WinUtils::ResolveMovedUsersFolder(wGMPPath)) {
-    NS_WARNING("ResolveMovedUsersFolder failed for GMP path.");
+  if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(wGMPPath)) {
+    GMP_LOG("ResolveJunctionPointsAndSymLinks failed for GMP path=%S",
+            wGMPPath.c_str());
+    NS_WARNING("ResolveJunctionPointsAndSymLinks failed for GMP path.");
     return false;
   }
+  GMP_LOG("GMPProcessParent::Launch() resolved path to %S", wGMPPath.c_str());
 
   // If the GMP path is a network path that is not mapped to a drive letter,
   // then we need to fix the path format for the sandbox rule.
   wchar_t volPath[MAX_PATH];
   if (::GetVolumePathNameW(wGMPPath.c_str(), volPath, MAX_PATH) &&
       ::GetDriveTypeW(volPath) == DRIVE_REMOTE &&
       wGMPPath.compare(0, 2, L"\\\\") == 0) {
     std::wstring sandboxGMPPath(wGMPPath);
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -1800,121 +1800,51 @@ uint32_t
 WinUtils::GetMaxTouchPoints()
 {
   if (IsTouchDeviceSupportPresent()) {
     return GetSystemMetrics(SM_MAXIMUMTOUCHES);
   }
   return 0;
 }
 
-#pragma pack(push, 1)
-typedef struct REPARSE_DATA_BUFFER {
-  ULONG  ReparseTag;
-  USHORT ReparseDataLength;
-  USHORT Reserved;
-  union {
-    struct {
-      USHORT SubstituteNameOffset;
-      USHORT SubstituteNameLength;
-      USHORT PrintNameOffset;
-      USHORT PrintNameLength;
-      ULONG  Flags;
-      WCHAR  PathBuffer[1];
-    } SymbolicLinkReparseBuffer;
-    struct {
-      USHORT SubstituteNameOffset;
-      USHORT SubstituteNameLength;
-      USHORT PrintNameOffset;
-      USHORT PrintNameLength;
-      WCHAR  PathBuffer[1];
-    } MountPointReparseBuffer;
-    struct {
-      UCHAR DataBuffer[1];
-    } GenericReparseBuffer;
-  };
-} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
-#pragma pack(pop)
-
 /* static */
 bool
-WinUtils::ResolveMovedUsersFolder(std::wstring& aPath)
+WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath)
 {
-  wchar_t* usersPath;
-  if (FAILED(SHGetKnownFolderPath(FOLDERID_UserProfiles, 0, nullptr,
-                                  &usersPath))) {
-    return false;
-  }
+  wchar_t path[MAX_PATH] = { 0 };
 
-  // Ensure usersPath gets freed properly.
-  UniquePtr<wchar_t, CoTaskMemFreePolicy> autoFreePath(usersPath);
+  nsAutoHandle handle(
+    ::CreateFileW(aPath.c_str(),
+                  0,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  nullptr,
+                  OPEN_EXISTING,
+                  FILE_FLAG_BACKUP_SEMANTICS,
+                  nullptr));
 
-  // Is aPath in Users folder?
-  size_t usersLen = wcslen(usersPath);
-  if (_wcsnicmp(aPath.c_str(), usersPath, usersLen) != 0 ||
-      aPath[usersLen] != L'\\') {
-    return true;
-  }
-
-  DWORD attributes = ::GetFileAttributesW(usersPath);
-  if (attributes == INVALID_FILE_ATTRIBUTES) {
+  if (handle == INVALID_HANDLE_VALUE) {
     return false;
   }
 
-  // Junction points are implemented as reparse points, is the Users folder one?
-  if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
-    return true;
+  DWORD pathLen = GetFinalPathNameByHandleW(
+    handle, path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+  if (pathLen == 0 || pathLen >= MAX_PATH) {
+    return false;
   }
+  aPath = path;
 
-  // Get the reparse point data.
-  nsAutoHandle usersHandle(
-    ::CreateFileW(usersPath, 0,
-                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                  nullptr, OPEN_EXISTING,
-                  FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
-                  nullptr));
-
-  char maxReparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0};
-  REPARSE_DATA_BUFFER* reparseBuf = (REPARSE_DATA_BUFFER*)maxReparseBuf;
-  DWORD bytesReturned = 0;
-  if (!::DeviceIoControl(usersHandle, FSCTL_GET_REPARSE_POINT, nullptr, 0,
-                         reparseBuf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
-                         &bytesReturned, nullptr)) {
-    return false;
+  // GetFinalPathNameByHandle sticks a '\\?\' in front of the path,
+  // but that confuses some APIs so strip it off. It will also put
+  // '\\?\UNC\' in front of network paths, we convert that to '\\'.
+  if (aPath.compare(0, 7, L"\\\\?\\UNC") == 0) {
+    aPath.erase(2, 6);
+  } else if (aPath.compare(0, 4, L"\\\\?\\") == 0) {
+    aPath.erase(0, 4);
   }
 
-  // Check to see if the reparse point is a junction point.
-  if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
-    return true;
-  }
-
-  // The offset and length are in bytes. Length doesn't include null.
-  wchar_t* substituteName = reparseBuf->MountPointReparseBuffer.PathBuffer +
-    reparseBuf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
-  std::wstring::size_type substituteLen =
-    reparseBuf->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
-
-  // If the substitute path starts with the NT namespace then remove it.
-  if (wcsncmp(substituteName, kNTPrefix, kNTPrefixLen) == 0) {
-    substituteName += kNTPrefixLen;
-    substituteLen -= kNTPrefixLen;
-  }
-
-  // Check that what remains looks like a drive letter path.
-  if (substituteName[1] != L':' || substituteName[2] != L'\\') {
-    return false;
-  }
-
-  // The documentation for SHGetKnownFolderPath says that it doesn't return a
-  // trailing backslash. The REPARSE_DATA_BUFFER path doesn't seem to have one
-  // either, but the documentation doesn't mention it, so let's make sure.
-  if (substituteName[substituteLen - 1] == L'\\') {
-    --substituteLen;
-  }
-
-  aPath.replace(0, usersLen, substituteName, substituteLen);
   return true;
 }
 
 /* static */
 bool
 WinUtils::SanitizePath(const wchar_t* aInputPath, nsAString& aOutput)
 {
   aOutput.Truncate();
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -458,26 +458,26 @@ public:
   * The maximum number of simultaneous touch contacts supported by the device.
   * In the case of devices with multiple digitizers (e.g. multiple touch screens),
   * the value will be the maximum of the set of maximum supported contacts by
   * each individual digitizer.
   */
   static uint32_t GetMaxTouchPoints();
 
   /**
-   * Detect if path is within the Users folder and Users is actually a junction
-   * point to another folder.
-   * If this is detected it will change the path to the actual path.
+   * Fully resolves a path to its final path name. So if path contains
+   * junction points or symlinks to other folders, we'll resolve the path
+   * fully to the actual path that the links target.
    *
    * @param aPath path to be resolved.
    * @return true if successful, including if nothing needs to be changed.
    *         false if something failed or aPath does not exist, aPath will
    *               remain unchanged.
    */
-  static bool ResolveMovedUsersFolder(std::wstring& aPath);
+  static bool ResolveJunctionPointsAndSymLinks(std::wstring& aPath);
 
   static void Initialize();
 
   static bool ShouldHideScrollbars();
 
   /**
    * This function normalizes the input path, converts short filenames to long
    * filenames, and substitutes environment variables for system paths.