Bug 1460989 - Hold system linker lock while modifying debug map; r?glandium draft
authorJim Chen <nchen@mozilla.com>
Fri, 15 Jun 2018 04:24:10 -0400
changeset 807729 31281c8274df1dfd7c3ee0a6486676f10248f1f1
parent 805551 ea21bf3e665d10066b6dce39873de9b353a12e57
push id113188
push userbmo:nchen@mozilla.com
push dateFri, 15 Jun 2018 15:46:19 +0000
reviewersglandium
bugs1460989
milestone62.0a1
Bug 1460989 - Hold system linker lock while modifying debug map; r?glandium When we modify the debug map, we could be racing with the system linker, either when we modify the entries or when we change page protection flags. To fix the race, we need to take the system linker's internal lock when we perform any kind of modification on the debug map. One way to hold the system linker lock is to call dl_iterate_phdr, and perform our actions inside the callback, which is invoked with the locked being held. However, dl_iterate_phdr is only present on Android 5.0+, and even then, dl_iterate_phdr is only protected by the linker lock on Android 6.0+. This means that with this patch, we can only safely modify the debug map on Android 6.0+, which I think is acceptable for an operation that only benefits a debugger. MozReview-Commit-ID: BowBEO8tu8Z
mozglue/linker/ElfLoader.cpp
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -21,16 +21,17 @@
 
 using namespace mozilla;
 
 // From Utils.h
 Atomic<size_t, ReleaseAcquire> gPageSize;
 
 #if defined(ANDROID)
 #include <sys/syscall.h>
+#include <sys/system_properties.h>
 #include <math.h>
 
 #include <android/api-level.h>
 #if __ANDROID_API__ < 8
 /* Android API < 8 doesn't provide sigaltstack */
 
 extern "C" {
 
@@ -306,16 +307,66 @@ const char *
 LeafName(const char *path)
 {
   const char *lastSlash = strrchr(path, '/');
   if (lastSlash)
     return lastSlash + 1;
   return path;
 }
 
+#if defined(ANDROID)
+/**
+ * Return the current Android version, or 0 on failure.
+ */
+int
+GetAndroidSDKVersion() {
+  static int version;
+  if (version) {
+    return version;
+  }
+
+  char version_string[PROP_VALUE_MAX] = {'\0'};
+  int len = __system_property_get("ro.build.version.sdk", version_string);
+  if (len) {
+    version = static_cast<int>(strtol(version_string, nullptr, 10));
+  }
+  return version;
+}
+#endif
+
+/**
+ * Run the given lambda while holding the internal lock of the system linker.
+ * To take the lock, we call the system dl_iterate_phdr and invoke the lambda
+ * from the callback, which is called while the lock is held. Return true on
+ * success.
+ */
+template<class Lambda>
+static bool
+RunWithSystemLinkerLock(Lambda&& aLambda) {
+  if (!dl_iterate_phdr) {
+    // No dl_iterate_phdr support.
+    return false;
+  }
+
+#if defined(ANDROID)
+  if (GetAndroidSDKVersion() < 23) {
+    // dl_iterate_phdr is _not_ protected by a lock on Android < 23.
+    // Also return false here if we failed to get the version.
+    return false;
+  }
+#endif
+
+  dl_iterate_phdr([] (dl_phdr_info*, size_t, void* lambda) -> int {
+    (*static_cast<Lambda*>(lambda))();
+    // Return 1 to stop iterating.
+    return 1;
+  }, &aLambda);
+  return true;
+}
+
 } /* Anonymous namespace */
 
 /**
  * LibHandle
  */
 LibHandle::~LibHandle()
 {
   free(path);
@@ -571,17 +622,19 @@ ElfLoader::Register(LibHandle *handle)
   handles.push_back(handle);
 }
 
 void
 ElfLoader::Register(CustomElf *handle)
 {
   Register(static_cast<LibHandle *>(handle));
   if (dbg) {
-    dbg.Add(handle);
+    // We could race with the system linker when modifying the debug map, so
+    // only do so while holding the system linker's internal lock.
+    RunWithSystemLinkerLock([this, handle] { dbg.Add(handle); });
   }
 }
 
 void
 ElfLoader::Forget(LibHandle *handle)
 {
   /* Ensure logging is initialized or refresh if environment changed. */
   Logging::Init();
@@ -598,17 +651,19 @@ ElfLoader::Forget(LibHandle *handle)
   }
 }
 
 void
 ElfLoader::Forget(CustomElf *handle)
 {
   Forget(static_cast<LibHandle *>(handle));
   if (dbg) {
-    dbg.Remove(handle);
+    // We could race with the system linker when modifying the debug map, so
+    // only do so while holding the system linker's internal lock.
+    RunWithSystemLinkerLock([this, handle] { dbg.Remove(handle); });
   }
 }
 
 void
 ElfLoader::Init()
 {
   Dl_info info;
   /* On Android < 4.1 can't reenter dl* functions. So when the library