Bug 1471532 - Support Windows in ASan Nightly Reporter builds. r?froydnj draft
authorChristian Holler <choller@mozilla.com>
Wed, 27 Jun 2018 11:19:00 +0200
changeset 812855 362b864a0b83422d0104399b59b430e93b2bc1e6
parent 812847 aea3f3457f1531706923b8d4c595a1f271de83da
push id114695
push usercholler@mozilla.com
push dateSat, 30 Jun 2018 20:04:04 +0000
reviewersfroydnj
bugs1471532
milestone63.0a1
Bug 1471532 - Support Windows in ASan Nightly Reporter builds. r?froydnj MozReview-Commit-ID: AK2dBOgoazY
browser/extensions/asan-reporter/bootstrap.js
browser/extensions/asan-reporter/clone_asan_reporter.sh
js/xpconnect/src/XPCShellImpl.cpp
mozglue/build/AsanOptions.cpp
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsAppRunner.h
toolkit/xre/nsEmbedFunctions.cpp
--- a/browser/extensions/asan-reporter/bootstrap.js
+++ b/browser/extensions/asan-reporter/bootstrap.js
@@ -24,56 +24,52 @@ const PREF_LOG_LEVEL = "asanreporter.log
 
 // Setup logging
 const LOGGER_NAME = "extensions.asanreporter";
 let logger = Log.repository.getLogger(LOGGER_NAME);
 logger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
 logger.addAppender(new Log.DumpAppender(new Log.BasicFormatter()));
 logger.level = Preferences.get(PREF_LOG_LEVEL, Log.Level.Info);
 
+// Determine the directory where ASan dumps will be located
+let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+let asanDumpDir = OS.Path.join(profileDir.path, "asan");
+
 this.TabCrashObserver = {
   init() {
     if (this.initialized)
       return;
     this.initialized = true;
 
     Services.obs.addObserver(this, "ipc:content-shutdown");
   },
 
   observe(aSubject, aTopic, aData) {
     if (aTopic == "ipc:content-shutdown") {
         aSubject.QueryInterface(Ci.nsIPropertyBag2);
         if (!aSubject.get("abnormal")) {
           return;
         }
-        processDirectory("/tmp");
+        processDirectory(asanDumpDir);
     }
   },
 };
 
 function install(aData, aReason) {}
 
 function uninstall(aData, aReason) {}
 
 function startup(aData, aReason) {
   logger.info("Starting up...");
 
   // Install a handler to observe tab crashes, so we can report those right
   // after they happen instead of relying on the user to restart the browser.
   TabCrashObserver.init();
 
-  // We could use OS.Constants.Path.tmpDir here, but unfortunately there is
-  // no way in C++ to get the same value *prior* to xpcom initialization.
-  // Since ASan needs its options, including the "log_path" option already
-  // at early startup, there is no way to pass this on to ASan.
-  //
-  // Instead, we hardcode the /tmp directory here, which should be fine in
-  // most cases, as long as we are on Linux and Mac (the main targets for
-  // this addon at the time of writing).
-  processDirectory("/tmp");
+  processDirectory(asanDumpDir);
 }
 
 function shutdown(aData, aReason) {
   logger.info("Shutting down...");
 }
 
 function processDirectory(pathString) {
   let iterator = new OS.File.DirectoryIterator(pathString);
--- a/browser/extensions/asan-reporter/clone_asan_reporter.sh
+++ b/browser/extensions/asan-reporter/clone_asan_reporter.sh
@@ -1,11 +1,11 @@
 #!/bin/sh
 
 mkdir tmp/
 git clone --no-checkout --depth 1 https://github.com/choller/firefox-asan-reporter tmp/
-(cd tmp && git reset --hard d508c6e3f5df752a9a7a2d6f1e4e7261ec2290e7)
+(cd tmp && git reset --hard c42a0b9c131c90cec2a2e93efb77e02e1673316f)
 
 # Copy only whitelisted files
 cp tmp/bootstrap.js tmp/install.rdf.in tmp/moz.build tmp/README.md tmp/LICENSE .
 
 # Remove the temporary directory
 rm -Rf tmp/
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1074,16 +1074,20 @@ XRE_XPCShellMain(int argc, char** argv, 
 
     mozilla::LogModule::Init(argc, argv);
 
 #ifdef MOZ_GECKO_PROFILER
     char aLocal;
     profiler_init(&aLocal);
 #endif
 
+#ifdef MOZ_ASAN_REPORTER
+    PR_SetEnv("MOZ_DISABLE_ASAN_REPORTER=1");
+#endif
+
     if (PR_GetEnv("MOZ_CHAOSMODE")) {
         ChaosFeature feature = ChaosFeature::Any;
         long featureInt = strtol(PR_GetEnv("MOZ_CHAOSMODE"), nullptr, 16);
         if (featureInt) {
             // NOTE: MOZ_CHAOSMODE=0 or a non-hex value maps to Any feature.
             feature = static_cast<ChaosFeature>(featureInt);
         }
         ChaosMode::SetChaosFeature(feature);
--- a/mozglue/build/AsanOptions.cpp
+++ b/mozglue/build/AsanOptions.cpp
@@ -27,24 +27,16 @@
 //   this will also likely require setting LSAN_OPTIONS with a suppression
 //   file, as in build/sanitizers/lsan_suppressions.txt.
 //
 //   allocator_may_return_null=1 - Tell ASan to return NULL when an allocation
 //   fails instead of aborting the program. This allows us to handle failing
 //   allocations the same way we would handle them with a regular allocator and
 //   also uncovers potential bugs that might occur in these situations.
 //
-//   log_path=/tmp/ff_asan_log - When running with the ASan reporter extension
-//   enabled (MOZ_ASAN_REPORTER), then we need to dump our logs to files
-//   instead of stderr so the reporter extension can find it. Unfortunately,
-//   this function is called so early at startup that we can't use the profile
-//   directory or even ask XPCOM for a temporary directory. Since the extension
-//   is only meant to run on Linux and Mac OSX for now, hardcoding /tmp is an
-//   option that should work for most standard environments.
-//
 //   max_malloc_fill_size - Tell ASan to initialize memory to a certain value
 //   when it is allocated. This option specifies the maximum allocation size
 //   for which ASan should still initialize the memory. The value we specify
 //   here is exactly 256MiB.
 //
 //   max_free_fill_size - Similar to max_malloc_fill_size, tell ASan to
 //   overwrite memory with a certain value when it is freed. Again, the value
 //   here specifies the maximum allocation size, larger allocations will
@@ -56,15 +48,13 @@
 //   are 0xe4 and 0xe5 to match the kAllocPoison and kAllocJunk constants used
 //   by mozjemalloc.
 //
 extern "C" MOZ_ASAN_BLACKLIST
 const char* __asan_default_options() {
     return "allow_user_segv_handler=1:alloc_dealloc_mismatch=0:detect_leaks=0"
            ":max_free_fill_size=268435456:max_malloc_fill_size=268435456"
            ":malloc_fill_byte=228:free_fill_byte=229"
-#ifdef MOZ_ASAN_REPORTER
-           ":log_path=/tmp/ff_asan_log"
-#endif
+	   ":handle_sigill=1"
            ":allocator_may_return_null=1";
 }
 
 #endif
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -336,17 +336,20 @@ SaveFileToEnv(const char *name, nsIFile 
 #else
   nsAutoCString path;
   file->GetNativePath(path);
   SaveWordToEnv(name, path);
 #endif
 }
 
 // Load the path of a file saved with SaveFileToEnv
-static already_AddRefed<nsIFile>
+#ifndef MOZ_ASAN_REPORTER
+static
+#endif
+already_AddRefed<nsIFile>
 GetFileFromEnv(const char *name)
 {
   nsresult rv;
   nsCOMPtr<nsIFile> file;
 
 #ifdef XP_WIN
   WCHAR path[_MAX_PATH];
   if (!GetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(),
@@ -4240,16 +4243,28 @@ XREMain::XRE_mainStartup(bool* aExitFlag
 
   mozilla::Telemetry::SetProfileDir(mProfD);
 
   if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
       MakeOrSetMinidumpPath(mProfD);
 
   CrashReporter::SetProfileDirectory(mProfD);
 
+#ifdef MOZ_ASAN_REPORTER
+  // In ASan reporter builds, we need to set ASan's log_path as early as
+  // possible, so it dumps its errors into files there instead of using
+  // the default stderr location. Since this is crucial for ASan reporter
+  // to work at all (and we don't want people to use a non-functional
+  // ASan reporter build), all failures while setting log_path are fatal.
+  setASanReporterPath(mProfD);
+
+  // Export to env for child processes
+  SaveFileToEnv("ASAN_REPORTER_PATH", mProfD);
+#endif
+
   nsAutoCString version;
   BuildVersion(version);
 
 #ifdef TARGET_OS_ABI
   NS_NAMED_LITERAL_CSTRING(osABI, TARGET_OS_ABI);
 #else
   // No TARGET_XPCOM_ABI, but at least the OS is known
   NS_NAMED_LITERAL_CSTRING(osABI, OS_TARGET "_UNKNOWN");
@@ -5289,8 +5304,37 @@ XRE_EnableSameExecutableForContentProc()
 }
 
 // Because rust doesn't handle weak symbols, this function wraps the weak
 // malloc_handle_oom for it.
 extern "C" void
 GeckoHandleOOM(size_t size) {
   mozalloc_handle_oom(size);
 }
+
+#ifdef MOZ_ASAN_REPORTER
+void setASanReporterPath(nsIFile* aDir) {
+  nsCOMPtr<nsIFile> dir;
+  aDir->Clone(getter_AddRefs(dir));
+
+  dir->Append(NS_LITERAL_STRING("asan"));
+  nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+  if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
+    MOZ_CRASH("[ASan Reporter] Unable to create crash directory.");
+  }
+
+  dir->Append(NS_LITERAL_STRING("ff_asan_log"));
+
+#ifdef XP_WIN
+  nsAutoString nspathW;
+  rv = dir->GetPath(nspathW);
+  NS_ConvertUTF16toUTF8 nspath(nspathW);
+#else
+  nsAutoCString nspath;
+  rv = dir->GetNativePath(nspath);
+#endif
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH("[ASan Reporter] Unable to get native path for crash directory.");
+  }
+
+  __sanitizer_set_report_path(nspath.get());
+}
+#endif
--- a/toolkit/xre/nsAppRunner.h
+++ b/toolkit/xre/nsAppRunner.h
@@ -124,9 +124,19 @@ const char* PlatformBuildID();
 } // namespace mozilla
 
 /**
  * Set up platform specific error handling such as suppressing DLL load dialog
  * and the JIT debugger on Windows, and install unix signal handlers.
  */
 void SetupErrorHandling(const char* progname);
 
+
+#ifdef MOZ_ASAN_REPORTER
+extern "C" {
+  void MOZ_EXPORT __sanitizer_set_report_path(const char *path);
+}
+void setASanReporterPath(nsIFile* aDir);
+
+already_AddRefed<nsIFile> GetFileFromEnv(const char *name);
+#endif
+
 #endif // nsAppRunner_h__
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -355,16 +355,40 @@ XRE_InitChildProcess(int aArgc,
                      char* aArgv[],
                      const XREChildData* aChildData)
 {
   NS_ENSURE_ARG_MIN(aArgc, 2);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
   MOZ_ASSERT(aChildData);
 
+#ifdef MOZ_ASAN_REPORTER
+  // In ASan reporter builds, we need to set ASan's log_path as early as
+  // possible, so it dumps its errors into files there instead of using
+  // the default stderr location. Since this is crucial for ASan reporter
+  // to work at all (and we don't want people to use a non-functional
+  // ASan reporter build), all failures while setting log_path are fatal.
+  //
+  // We receive this log_path via the ASAN_REPORTER_PATH environment variable
+  // because there is no other way to generically get the necessary profile
+  // directory in all child types without adding support for that in each
+  // child process type class (at the risk of missing this in a child).
+  //
+  // In certain cases (e.g. child startup through xpcshell or gtests), this
+  // code needs to remain disabled, as no ASAN_REPORTER_PATH would be available.
+  if (!PR_GetEnv("MOZ_DISABLE_ASAN_REPORTER") &&
+      !PR_GetEnv("MOZ_RUN_GTEST")) {
+    nsCOMPtr<nsIFile> asanReporterPath = GetFileFromEnv("ASAN_REPORTER_PATH");
+    if (!asanReporterPath) {
+      MOZ_CRASH("Child did not receive ASAN_REPORTER_PATH!");
+    }
+    setASanReporterPath(asanReporterPath);
+  }
+#endif
+
 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
   // This has to happen before glib thread pools are started.
   mozilla::SandboxEarlyInit();
 #endif
 
 #ifdef MOZ_JPROF
   // Call the code to install our handler
   setupProfilingStuff();