xpcom/base/nsDumpUtils.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsDumpUtils.h"
     8 #include "nsDirectoryServiceDefs.h"
     9 #include "nsDirectoryServiceUtils.h"
    10 #include "prenv.h"
    11 #include <errno.h>
    12 #include "mozilla/Services.h"
    13 #include "nsIObserverService.h"
    14 #include "mozilla/ClearOnShutdown.h"
    16 #ifdef XP_UNIX // {
    17 #include "mozilla/Preferences.h"
    18 #include <fcntl.h>
    19 #include <unistd.h>
    20 #include <sys/types.h>
    21 #include <sys/stat.h>
    23 using namespace mozilla;
    25 /*
    26  * The following code supports triggering a registered callback upon
    27  * receiving a specific signal.
    28  *
    29  * Take about:memory for example, we register
    30  * 1. doGCCCDump for doMemoryReport
    31  * 2. doMemoryReport for sDumpAboutMemorySignum(SIGRTMIN)
    32  *                       and sDumpAboutMemoryAfterMMUSignum(SIGRTMIN+1).
    33  *
    34  * When we receive one of these signals, we write the signal number to a pipe.
    35  * The IO thread then notices that the pipe has been written to, and kicks off
    36  * the appropriate task on the main thread.
    37  *
    38  * This scheme is similar to using signalfd(), except it's portable and it
    39  * doesn't require the use of sigprocmask, which is problematic because it
    40  * masks signals received by child processes.
    41  *
    42  * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
    43  * But that uses libevent, which does not handle the realtime signals (bug
    44  * 794074).
    45  */
    47 // This is the write-end of a pipe that we use to notice when a
    48 // specific signal occurs.
    49 static Atomic<int> sDumpPipeWriteFd(-1);
    51 const char* const FifoWatcher::kPrefName =
    52     "memory_info_dumper.watch_fifo.enabled";
    54 static void
    55 DumpSignalHandler(int aSignum)
    56 {
    57   // This is a signal handler, so everything in here needs to be
    58   // async-signal-safe.  Be careful!
    60   if (sDumpPipeWriteFd != -1) {
    61     uint8_t signum = static_cast<int>(aSignum);
    62     write(sDumpPipeWriteFd, &signum, sizeof(signum));
    63   }
    64 }
    66 NS_IMPL_ISUPPORTS(FdWatcher, nsIObserver);
    68 void FdWatcher::Init()
    69 {
    70   MOZ_ASSERT(NS_IsMainThread());
    72   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    73   os->AddObserver(this, "xpcom-shutdown", /* ownsWeak = */ false);
    75   XRE_GetIOMessageLoop()->PostTask(
    76       FROM_HERE,
    77       NewRunnableMethod(this, &FdWatcher::StartWatching));
    78 }
    80 // Implementations may call this function multiple times if they ensure that
    81 // it's safe to call OpenFd() multiple times and they call StopWatching()
    82 // first.
    83 void FdWatcher::StartWatching()
    84 {
    85   MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
    86   MOZ_ASSERT(mFd == -1);
    88   mFd = OpenFd();
    89   if (mFd == -1) {
    90     LOG("FdWatcher: OpenFd failed.");
    91     return;
    92   }
    94   MessageLoopForIO::current()->WatchFileDescriptor(
    95     mFd, /* persistent = */ true,
    96     MessageLoopForIO::WATCH_READ,
    97     &mReadWatcher, this);
    98 }
   100 // Since implementations can call StartWatching() multiple times, they can of
   101 // course call StopWatching() multiple times.
   102 void FdWatcher::StopWatching()
   103 {
   104   MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
   106   mReadWatcher.StopWatchingFileDescriptor();
   107   if (mFd != -1) {
   108     close(mFd);
   109     mFd = -1;
   110   }
   111 }
   113 StaticRefPtr<SignalPipeWatcher> SignalPipeWatcher::sSingleton;
   115 /* static */ SignalPipeWatcher*
   116 SignalPipeWatcher::GetSingleton()
   117 {
   118   if (!sSingleton) {
   119     sSingleton = new SignalPipeWatcher();
   120     sSingleton->Init();
   121     ClearOnShutdown(&sSingleton);
   122   }
   123   return sSingleton;
   124 }
   126 void
   127 SignalPipeWatcher::RegisterCallback(uint8_t aSignal,
   128                                     PipeCallback aCallback)
   129 {
   130   MutexAutoLock lock(mSignalInfoLock);
   132   for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++)
   133   {
   134     if (mSignalInfo[i].mSignal == aSignal) {
   135       LOG("Register Signal(%d) callback failed! (DUPLICATE)", aSignal);
   136       return;
   137     }
   138   }
   139   SignalInfo signalInfo = { aSignal, aCallback };
   140   mSignalInfo.AppendElement(signalInfo);
   141   RegisterSignalHandler(signalInfo.mSignal);
   142 }
   144 void
   145 SignalPipeWatcher::RegisterSignalHandler(uint8_t aSignal)
   146 {
   147   struct sigaction action;
   148   memset(&action, 0, sizeof(action));
   149   sigemptyset(&action.sa_mask);
   150   action.sa_handler = DumpSignalHandler;
   152   if (aSignal) {
   153     if (sigaction(aSignal, &action, nullptr)) {
   154       LOG("SignalPipeWatcher failed to register sig %d.", aSignal);
   155     }
   156   } else {
   157     MutexAutoLock lock(mSignalInfoLock);
   158     for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
   159       if (sigaction(mSignalInfo[i].mSignal, &action, nullptr)) {
   160         LOG("SignalPipeWatcher failed to register signal(%d) "
   161             "dump signal handler.", mSignalInfo[i].mSignal);
   162       }
   163     }
   164   }
   165 }
   167 SignalPipeWatcher::~SignalPipeWatcher()
   168 {
   169   if (sDumpPipeWriteFd != -1) {
   170     StopWatching();
   171   }
   172 }
   174 int SignalPipeWatcher::OpenFd()
   175 {
   176   MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
   178   // Create a pipe.  When we receive a signal in our signal handler, we'll
   179   // write the signum to the write-end of this pipe.
   180   int pipeFds[2];
   181   if (pipe(pipeFds)) {
   182     LOG("SignalPipeWatcher failed to create pipe.");
   183     return -1;
   184   }
   186   // Close this pipe on calls to exec().
   187   fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
   188   fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
   190   int readFd = pipeFds[0];
   191   sDumpPipeWriteFd = pipeFds[1];
   193   RegisterSignalHandler();
   194   return readFd;
   195 }
   197 void SignalPipeWatcher::StopWatching()
   198 {
   199   MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
   201   // Close sDumpPipeWriteFd /after/ setting the fd to -1.
   202   // Otherwise we have the (admittedly far-fetched) race where we
   203   //
   204   //  1) close sDumpPipeWriteFd
   205   //  2) open a new fd with the same number as sDumpPipeWriteFd
   206   //     had.
   207   //  3) receive a signal, then write to the fd.
   208   int pipeWriteFd = sDumpPipeWriteFd.exchange(-1);
   209   close(pipeWriteFd);
   211   FdWatcher::StopWatching();
   212 }
   214 void SignalPipeWatcher::OnFileCanReadWithoutBlocking(int aFd)
   215 {
   216   MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
   218   uint8_t signum;
   219   ssize_t numReceived = read(aFd, &signum, sizeof(signum));
   220   if (numReceived != sizeof(signum)) {
   221     LOG("Error reading from buffer in "
   222         "SignalPipeWatcher::OnFileCanReadWithoutBlocking.");
   223     return;
   224   }
   226   {
   227     MutexAutoLock lock(mSignalInfoLock);
   228     for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
   229       if(signum == mSignalInfo[i].mSignal) {
   230         mSignalInfo[i].mCallback(signum);
   231         return;
   232       }
   233     }
   234   }
   235   LOG("SignalPipeWatcher got unexpected signum.");
   236 }
   238 StaticRefPtr<FifoWatcher> FifoWatcher::sSingleton;
   240 /* static */ FifoWatcher*
   241 FifoWatcher::GetSingleton()
   242 {
   243   if (!sSingleton) {
   244     nsAutoCString dirPath;
   245     Preferences::GetCString(
   246       "memory_info_dumper.watch_fifo.directory", &dirPath);
   247     sSingleton = new FifoWatcher(dirPath);
   248     sSingleton->Init();
   249     ClearOnShutdown(&sSingleton);
   250   }
   251   return sSingleton;
   252 }
   254 /* static */ bool
   255 FifoWatcher::MaybeCreate()
   256 {
   257   MOZ_ASSERT(NS_IsMainThread());
   259   if (XRE_GetProcessType() != GeckoProcessType_Default) {
   260     // We want this to be main-process only, since two processes can't listen
   261     // to the same fifo.
   262     return false;
   263   }
   265   if (!Preferences::GetBool(kPrefName, false)) {
   266     LOG("Fifo watcher disabled via pref.");
   267     return false;
   268   }
   270   // The FifoWatcher is held alive by the observer service.
   271   if (!sSingleton) {
   272     GetSingleton();
   273   }
   274   return true;
   275 }
   277 void
   278 FifoWatcher::RegisterCallback(const nsCString& aCommand, FifoCallback aCallback)
   279 {
   280   MutexAutoLock lock(mFifoInfoLock);
   282   for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); i++)
   283   {
   284     if (mFifoInfo[i].mCommand.Equals(aCommand)) {
   285       LOG("Register command(%s) callback failed! (DUPLICATE)", aCommand.get());
   286       return;
   287     }
   288   }
   289   FifoInfo aFifoInfo = { aCommand, aCallback };
   290   mFifoInfo.AppendElement(aFifoInfo);
   291 }
   293 FifoWatcher::~FifoWatcher()
   294 {
   295 }
   297 int FifoWatcher::OpenFd()
   298 {
   299   // If the memory_info_dumper.directory pref is specified, put the fifo
   300   // there.  Otherwise, put it into the system's tmp directory.
   302   nsCOMPtr<nsIFile> file;
   304   nsresult rv;
   305   if (mDirPath.Length() > 0) {
   306     rv = XRE_GetFileFromPath(mDirPath.get(), getter_AddRefs(file));
   307     if (NS_FAILED(rv)) {
   308       LOG("FifoWatcher failed to open file \"%s\"", mDirPath.get());
   309       return -1;
   310     }
   311   } else {
   312     rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
   313     if (NS_WARN_IF(NS_FAILED(rv)))
   314       return -1;
   315   }
   317   rv = file->AppendNative(NS_LITERAL_CSTRING("debug_info_trigger"));
   318   if (NS_WARN_IF(NS_FAILED(rv)))
   319     return -1;
   321   nsAutoCString path;
   322   rv = file->GetNativePath(path);
   323   if (NS_WARN_IF(NS_FAILED(rv)))
   324     return -1;
   326   // unlink might fail because the file doesn't exist, or for other reasons.
   327   // But we don't care it fails; any problems will be detected later, when we
   328   // try to mkfifo or open the file.
   329   if (unlink(path.get())) {
   330     LOG("FifoWatcher::OpenFifo unlink failed; errno=%d.  "
   331         "Continuing despite error.", errno);
   332   }
   334   if (mkfifo(path.get(), 0766)) {
   335     LOG("FifoWatcher::OpenFifo mkfifo failed; errno=%d", errno);
   336     return -1;
   337   }
   339 #ifdef ANDROID
   340     // Android runs with a umask, so we need to chmod our fifo to make it
   341     // world-writable.
   342     chmod(path.get(), 0666);
   343 #endif
   345   int fd;
   346   do {
   347     // The fifo will block until someone else has written to it.  In
   348     // particular, open() will block until someone else has opened it for
   349     // writing!  We want open() to succeed and read() to block, so we open
   350     // with NONBLOCK and then fcntl that away.
   351     fd = open(path.get(), O_RDONLY | O_NONBLOCK);
   352   } while (fd == -1 && errno == EINTR);
   354   if (fd == -1) {
   355     LOG("FifoWatcher::OpenFifo open failed; errno=%d", errno);
   356     return -1;
   357   }
   359   // Make fd blocking now that we've opened it.
   360   if (fcntl(fd, F_SETFL, 0)) {
   361     close(fd);
   362     return -1;
   363   }
   365   return fd;
   366 }
   368 void FifoWatcher::OnFileCanReadWithoutBlocking(int aFd)
   369 {
   370   MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
   372   char buf[1024];
   373   int nread;
   374   do {
   375     // sizeof(buf) - 1 to leave space for the null-terminator.
   376     nread = read(aFd, buf, sizeof(buf));
   377   } while(nread == -1 && errno == EINTR);
   379   if (nread == -1) {
   380     // We want to avoid getting into a situation where
   381     // OnFileCanReadWithoutBlocking is called in an infinite loop, so when
   382     // something goes wrong, stop watching the fifo altogether.
   383     LOG("FifoWatcher hit an error (%d) and is quitting.", errno);
   384     StopWatching();
   385     return;
   386   }
   388   if (nread == 0) {
   389     // If we get EOF, that means that the other side closed the fifo.  We need
   390     // to close and re-open the fifo; if we don't,
   391     // OnFileCanWriteWithoutBlocking will be called in an infinite loop.
   393     LOG("FifoWatcher closing and re-opening fifo.");
   394     StopWatching();
   395     StartWatching();
   396     return;
   397   }
   399   nsAutoCString inputStr;
   400   inputStr.Append(buf, nread);
   402   // Trimming whitespace is important because if you do
   403   //   |echo "foo" >> debug_info_trigger|,
   404   // it'll actually write "foo\n" to the fifo.
   405   inputStr.Trim("\b\t\r\n");
   407   {
   408     MutexAutoLock lock(mFifoInfoLock);
   410     for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); i++) {
   411       const nsCString commandStr = mFifoInfo[i].mCommand;
   412       if(inputStr == commandStr.get()) {
   413         mFifoInfo[i].mCallback(inputStr);
   414         return;
   415       }
   416     }
   417   }
   418   LOG("Got unexpected value from fifo; ignoring it.");
   419 }
   421 #endif // XP_UNIX }
   423 // In Android case, this function will open a file named aFilename under
   424 // /data/local/tmp/"aFoldername".
   425 // Otherwise, it will open a file named aFilename under "NS_OS_TEMP_DIR".
   426 /* static */ nsresult
   427 nsDumpUtils::OpenTempFile(const nsACString& aFilename, nsIFile** aFile,
   428                           const nsACString& aFoldername)
   429 {
   430 #ifdef ANDROID
   431   // For Android, first try the downloads directory which is world-readable
   432   // rather than the temp directory which is not.
   433   if (!*aFile) {
   434     char *env = PR_GetEnv("DOWNLOADS_DIRECTORY");
   435     if (env) {
   436       NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile);
   437     }
   438   }
   439 #endif
   440   nsresult rv;
   441   if (!*aFile) {
   442     rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, aFile);
   443     if (NS_WARN_IF(NS_FAILED(rv)))
   444       return rv;
   445   }
   447 #ifdef ANDROID
   448   // /data/local/tmp is a true tmp directory; anyone can create a file there,
   449   // but only the user which created the file can remove it.  We want non-root
   450   // users to be able to remove these files, so we write them into a
   451   // subdirectory of the temp directory and chmod 777 that directory.
   452   if (aFoldername != EmptyCString()) {
   453     rv = (*aFile)->AppendNative(aFoldername);
   454     if (NS_WARN_IF(NS_FAILED(rv)))
   455       return rv;
   457     // It's OK if this fails; that probably just means that the directory already
   458     // exists.
   459     (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777);
   461     nsAutoCString dirPath;
   462     rv = (*aFile)->GetNativePath(dirPath);
   463     if (NS_WARN_IF(NS_FAILED(rv)))
   464       return rv;
   466     while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) {}
   467   }
   468 #endif
   470   nsCOMPtr<nsIFile> file(*aFile);
   472   rv = file->AppendNative(aFilename);
   473   if (NS_WARN_IF(NS_FAILED(rv)))
   474     return rv;
   476   rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
   477   if (NS_WARN_IF(NS_FAILED(rv)))
   478     return rv;
   480 #ifdef ANDROID
   481     // Make this file world-read/writable; the permissions passed to the
   482     // CreateUnique call above are not sufficient on Android, which runs with a
   483     // umask.
   484     nsAutoCString path;
   485     rv = file->GetNativePath(path);
   486     if (NS_WARN_IF(NS_FAILED(rv)))
   487       return rv;
   489     while (chmod(path.get(), 0666) == -1 && errno == EINTR) {}
   490 #endif
   492   return NS_OK;
   493 }

mercurial