Bug 1561912, r=michal a=RyanVM
authorHonza Bambas <honzab.moz@firemni.cz>
Fri, 26 Jul 2019 22:58:39 +0000
changeset 544870 0ea23ef025191a46847a96f77706b2cfe3544179
parent 544869 7baf87d8c07f35f72587c81663e49ccc35887b93
child 544871 d6feaa7bd4aedf27188fe706c9b05da8b8788678
push id2131
push userffxbld-merge
push dateMon, 26 Aug 2019 18:30:20 +0000
treeherdermozilla-release@b19ffb3ca153 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal, RyanVM
bugs1561912
milestone69.0
Bug 1561912, r=michal a=RyanVM Differential Revision: https://phabricator.services.mozilla.com/D37297
xpcom/io/FilePreferences.cpp
--- a/xpcom/io/FilePreferences.cpp
+++ b/xpcom/io/FilePreferences.cpp
@@ -3,44 +3,53 @@
 /* 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 "FilePreferences.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Tokenizer.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace FilePreferences {
 
+static StaticMutex sMutex;
+
 static bool sBlockUNCPaths = false;
 typedef nsTArray<nsString> WinPaths;
 
 static WinPaths& PathWhitelist() {
+  sMutex.AssertCurrentThreadOwns();
+
   static WinPaths sPaths;
   return sPaths;
 }
 
 #ifdef XP_WIN
 typedef char16_t char_path_t;
 #else
 typedef char char_path_t;
 #endif
 
+// Initially false to make concurrent consumers acquire the lock and sync
+static bool sBlacklistEmpty = false;
+
 typedef nsTArray<nsTString<char_path_t>> Paths;
 static StaticAutoPtr<Paths> sBlacklist;
 
 static Paths& PathBlacklist() {
+  sMutex.AssertCurrentThreadOwns();
   if (!sBlacklist) {
     sBlacklist = new nsTArray<nsTString<char_path_t>>();
     ClearOnShutdown(&sBlacklist);
   }
   return *sBlacklist;
 }
 
 static void AllowUNCDirectory(char const* directory) {
@@ -57,43 +66,54 @@ static void AllowUNCDirectory(char const
 
   // The whitelist makes sense only for UNC paths, because this code is used
   // to block only UNC paths, hence, no need to add non-UNC directories here
   // as those would never pass the check.
   if (!StringBeginsWith(path, NS_LITERAL_STRING("\\\\"))) {
     return;
   }
 
+  StaticMutexAutoLock lock(sMutex);
+
   if (!PathWhitelist().Contains(path)) {
     PathWhitelist().AppendElement(path);
   }
 }
 
 void InitPrefs() {
   sBlockUNCPaths =
       Preferences::GetBool("network.file.disable_unc_paths", false);
 
-  PathBlacklist().Clear();
   nsTAutoString<char_path_t> blacklist;
 #ifdef XP_WIN
   Preferences::GetString("network.file.path_blacklist", blacklist);
 #else
   Preferences::GetCString("network.file.path_blacklist", blacklist);
 #endif
 
+  StaticMutexAutoLock lock(sMutex);
+
+  if (blacklist.IsEmpty()) {
+    sBlacklistEmpty = true;
+    return;
+  }
+
+  PathBlacklist().Clear();
   TTokenizer<char_path_t> p(blacklist);
   while (!p.CheckEOF()) {
     nsTString<char_path_t> path;
     Unused << p.ReadUntil(TTokenizer<char_path_t>::Token::Char(','), path);
     path.Trim(" ");
     if (!path.IsEmpty()) {
       PathBlacklist().AppendElement(path);
     }
     Unused << p.CheckChar(',');
   }
+
+  sBlacklistEmpty = PathBlacklist().Length() == 0;
 }
 
 void InitDirectoriesWhitelist() {
   // NS_GRE_DIR is the installation path where the binary resides.
   AllowUNCDirectory(NS_GRE_DIR);
   // NS_APP_USER_PROFILE_50_DIR and NS_APP_USER_PROFILE_LOCAL_50_DIR are the two
   // parts of the profile we store permanent and local-specific data.
   AllowUNCDirectory(NS_APP_USER_PROFILE_50_DIR);
@@ -160,17 +180,17 @@ class TNormalizer : public TTokenizer<TC
 
       mStack.RemoveLastElement();
       return true;
     }
 
     nsTDependentSubstring<TChar> name;
     if (base::ReadUntil(mSeparator, name, base::INCLUDE_LAST) &&
         name.Length() == 1) {
-      // this means and empty name (a lone slash), which is illegal
+      // this means an empty name (a lone slash), which is illegal
       return false;
     }
     mStack.AppendElement(name);
 
     return true;
   }
 
   bool CheckParentDir() {
@@ -212,16 +232,18 @@ bool IsBlockedUNCPath(const nsAString& a
   }
 
   nsAutoString normalized;
   if (!Normalizer(aFilePath, Normalizer::Token::Char('\\')).Get(normalized)) {
     // Broken paths are considered invalid and thus inaccessible
     return true;
   }
 
+  StaticMutexAutoLock lock(sMutex);
+
   for (const auto& allowedPrefix : PathWhitelist()) {
     if (StringBeginsWith(normalized, allowedPrefix)) {
       if (normalized.Length() == allowedPrefix.Length()) {
         return false;
       }
       if (normalized[allowedPrefix.Length()] == L'\\') {
         return false;
       }
@@ -240,26 +262,35 @@ bool IsBlockedUNCPath(const nsAString& a
 #ifdef XP_WIN
 const char kPathSeparator = '\\';
 #else
 const char kPathSeparator = '/';
 #endif
 
 bool IsAllowedPath(const nsTSubstring<char_path_t>& aFilePath) {
   typedef TNormalizer<char_path_t> Normalizer;
+
+  // A quick check out of the lock.
+  if (sBlacklistEmpty) {
+    return true;
+  }
+
+  StaticMutexAutoLock lock(sMutex);
+
+  // Recheck the flag under the lock to reload it.
+  if (sBlacklistEmpty) {
+    return true;
+  }
+
   // If sBlacklist has been cleared at shutdown, we must avoid calling
   // PathBlacklist() again, as that will recreate the array and we will leak.
   if (!sBlacklist) {
     return true;
   }
 
-  if (PathBlacklist().Length() == 0) {
-    return true;
-  }
-
   nsTAutoString<char_path_t> normalized;
   if (!Normalizer(aFilePath, Normalizer::Token::Char(kPathSeparator))
            .Get(normalized)) {
     // Broken paths are considered invalid and thus inaccessible
     return false;
   }
 
   for (const auto& prefix : PathBlacklist()) {
@@ -273,16 +304,17 @@ bool IsAllowedPath(const nsTSubstring<ch
   }
 
   return true;
 }
 
 void testing::SetBlockUNCPaths(bool aBlock) { sBlockUNCPaths = aBlock; }
 
 void testing::AddDirectoryToWhitelist(nsAString const& aPath) {
+  StaticMutexAutoLock lock(sMutex);
   PathWhitelist().AppendElement(aPath);
 }
 
 bool testing::NormalizePath(nsAString const& aPath, nsAString& aNormalized) {
   typedef TNormalizer<char16_t> Normalizer;
   Normalizer normalizer(aPath, Normalizer::Token::Char('\\'));
   return normalizer.Get(aNormalized);
 }