Sat, 03 Jan 2015 20:18:00 +0100
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 }