Bug 1451524: Add a VM policy to the DLL interceptor that allows multiple instances to share a single trampoline space; r=handyman
authorAaron Klotz <aklotz@mozilla.com>
Mon, 02 Apr 2018 17:04:17 -0600
changeset 468931 27d5c7a1d4c112687b78b4a9f86857378bfc67bb
parent 468930 3802f86e1bd10c3e8d5d810c18675aa999bdefa7
child 468932 b315cc407da4438cede0f2a88aeeb87a932cd325
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershandyman
bugs1451524
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1451524: Add a VM policy to the DLL interceptor that allows multiple instances to share a single trampoline space; r=handyman
mozglue/misc/interceptor/Trampoline.h
mozglue/misc/interceptor/VMSharingPolicies.h
--- a/mozglue/misc/interceptor/Trampoline.h
+++ b/mozglue/misc/interceptor/Trampoline.h
@@ -286,28 +286,30 @@ public:
 
   explicit TrampolineCollection(const MMPolicy& aMMPolicy)
     : mMMPolicy(aMMPolicy)
     , mLocalBase(0)
     , mRemoteBase(0)
     , mTrampSize(0)
     , mNumTramps(0)
     , mPrevProt(0)
+    , mCS(nullptr)
   {
   }
 
   TrampolineCollection(const MMPolicy& aMMPolicy, uint8_t* const aLocalBase,
                        const uintptr_t aRemoteBase, const uint32_t aTrampSize,
                        const uint32_t aNumTramps)
     : mMMPolicy(aMMPolicy)
     , mLocalBase(aLocalBase)
     , mRemoteBase(aRemoteBase)
     , mTrampSize(aTrampSize)
     , mNumTramps(aNumTramps)
     , mPrevProt(0)
+    , mCS(nullptr)
   {
     if (!aNumTramps) {
       return;
     }
 
     DebugOnly<BOOL> ok = mMMPolicy.Protect(aLocalBase, aNumTramps * aTrampSize,
                                            PAGE_EXECUTE_READWRITE, &mPrevProt);
     MOZ_ASSERT(ok);
@@ -316,16 +318,30 @@ public:
   ~TrampolineCollection()
   {
     if (!mPrevProt) {
       return;
     }
 
     mMMPolicy.Protect(mLocalBase, mNumTramps * mTrampSize,
                       mPrevProt, &mPrevProt);
+
+    if (mCS) {
+      ::LeaveCriticalSection(mCS);
+    }
+  }
+
+  void Lock(CRITICAL_SECTION& aCS)
+  {
+    if (!mPrevProt || mCS) {
+      return;
+    }
+
+    mCS = &aCS;
+    ::EnterCriticalSection(&aCS);
   }
 
   TrampolineIterator begin() const
   {
     if (!mPrevProt) {
       return end();
     }
 
@@ -343,27 +359,30 @@ public:
 
   TrampolineCollection(TrampolineCollection&& aOther)
     : mMMPolicy(aOther.mMMPolicy)
     , mLocalBase(aOther.mLocalBase)
     , mRemoteBase(aOther.mRemoteBase)
     , mTrampSize(aOther.mTrampSize)
     , mNumTramps(aOther.mNumTramps)
     , mPrevProt(aOther.mPrevProt)
+    , mCS(aOther.mCS)
   {
     aOther.mPrevProt = 0;
+    aOther.mCS = nullptr;
   }
 
 private:
-  const MMPolicy& mMMPolicy;
-  uint8_t* const  mLocalBase;
-  const uintptr_t mRemoteBase;
-  const uint32_t  mTrampSize;
-  const uint32_t  mNumTramps;
-  uint32_t        mPrevProt;
+  const MMPolicy&   mMMPolicy;
+  uint8_t* const    mLocalBase;
+  const uintptr_t   mRemoteBase;
+  const uint32_t    mTrampSize;
+  const uint32_t    mNumTramps;
+  uint32_t          mPrevProt;
+  CRITICAL_SECTION* mCS;
 
   friend class TrampolineIterator;
 };
 
 } // namespace interceptor
 } // namespace mozilla
 
 #endif // mozilla_interceptor_Trampoline_h
--- a/mozglue/misc/interceptor/VMSharingPolicies.h
+++ b/mozglue/misc/interceptor/VMSharingPolicies.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_interceptor_VMSharingPolicies_h
 #define mozilla_interceptor_VMSharingPolicies_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Types.h"
+#include "mozilla/StaticPtr.h"
 
 namespace mozilla {
 namespace interceptor {
 
 template <typename MMPolicy, uint32_t kChunkSize>
 class VMSharingPolicyUnique : public MMPolicy
 {
 public:
@@ -78,13 +79,220 @@ public:
     aOther.mNextChunkIndex = 0;
     return *this;
   }
 
 private:
   uint32_t  mNextChunkIndex;
 };
 
+template <typename MMPolicy, uint32_t kChunkSize>
+class VMSharingPolicyShared : public MMPolicyBase
+{
+  typedef VMSharingPolicyUnique<MMPolicy, kChunkSize> ValueT;
+
+  // We use pid instead of HANDLE for mapping, since more than one handle may
+  // map to the same pid. We don't worry about pid reuse becuase each mVMPolicy
+  // holds an open handle to pid, thus keeping the pid reserved at least for the
+  // lifetime of mVMPolicy.
+  struct ProcMapEntry
+  {
+    ProcMapEntry()
+      : mPid(::GetCurrentProcessId())
+    {
+    }
+
+    explicit ProcMapEntry(HANDLE aProc)
+      : mPid(::GetProcessId(aProc))
+      , mVMPolicy(aProc)
+    {
+    }
+
+    ProcMapEntry(ProcMapEntry&& aOther)
+      : mPid(aOther.mPid)
+      , mVMPolicy(Move(aOther.mVMPolicy))
+    {
+      aOther.mPid = 0;
+    }
+
+    ProcMapEntry(const ProcMapEntry&) = delete;
+    ProcMapEntry& operator=(const ProcMapEntry&) = delete;
+
+    ProcMapEntry& operator=(ProcMapEntry&& aOther)
+    {
+      mPid = aOther.mPid;
+      mVMPolicy = Move(aOther.mVMPolicy);
+      aOther.mPid = 0;
+      return *this;
+    }
+
+    bool operator==(DWORD aPid) const
+    {
+      return mPid == aPid;
+    }
+
+    DWORD   mPid;
+    ValueT  mVMPolicy;
+  };
+
+  // We normally expect to reference only one other process at a time, but this
+  // is not a requirement.
+  typedef Vector<ProcMapEntry, 1> MapT;
+
+public:
+  typedef MMPolicy MMPolicyT;
+
+  template <typename... Args>
+  explicit VMSharingPolicyShared(Args... aArgs)
+    : mPid(GetPid(aArgs...))
+  {
+    static const bool isAlloc = []() -> bool {
+      sPerProcVM = new MapT();
+      DWORD flags = 0;
+#if defined(RELEASE_OR_BETA)
+      flags |= CRITICAL_SECTION_NO_DEBUG_INFO;
+#endif // defined(RELEASE_OR_BETA)
+      ::InitializeCriticalSectionEx(&sCS, 4000, flags);
+      return true;
+    }();
+
+    MOZ_ASSERT(mPid);
+    if (!mPid) {
+      return;
+    }
+
+    AutoCriticalSection lock(&sCS);
+
+    if (find(mPid)) {
+      return;
+    }
+
+    MOZ_RELEASE_ASSERT(sPerProcVM->append(ProcMapEntry(aArgs...)));
+  }
+
+  explicit operator bool() const
+  {
+    AutoCriticalSection lock(&sCS);
+
+    ProcMapEntry* entry;
+    MOZ_RELEASE_ASSERT(find(mPid, &entry));
+
+    return !!entry->mVMPolicy;
+  }
+
+  operator const MMPolicy&() const
+  {
+    AutoCriticalSection lock(&sCS);
+
+    ProcMapEntry* entry;
+    MOZ_RELEASE_ASSERT(find(mPid, &entry));
+
+    return entry->mVMPolicy;
+  }
+
+  bool ShouldUnhookUponDestruction() const
+  {
+    AutoCriticalSection lock(&sCS);
+
+    ProcMapEntry* entry;
+    if (!find(mPid, &entry)) {
+      return 0;
+    }
+
+    return entry->mVMPolicy.ShouldUnhookUponDestruction();
+  }
+
+  bool Reserve(uint32_t aCount)
+  {
+    AutoCriticalSection lock(&sCS);
+
+    ProcMapEntry* entry;
+    if (!find(mPid, &entry)) {
+      return false;
+    }
+
+    return entry->mVMPolicy.Reserve(aCount);
+  }
+
+  Trampoline<MMPolicy> GetNextTrampoline()
+  {
+    AutoCriticalSection lock(&sCS);
+
+    ProcMapEntry* entry;
+    if (!find(mPid, &entry)) {
+      return nullptr;
+    }
+
+    return entry->mVMPolicy.GetNextTrampoline();
+  }
+
+  TrampolineCollection<MMPolicy> Items() const
+  {
+    AutoCriticalSection lock(&sCS);
+
+    ProcMapEntry* entry;
+    MOZ_RELEASE_ASSERT(find(mPid, &entry));
+
+    TrampolineCollection<MMPolicy> items(Move(entry->mVMPolicy.Items()));
+
+    // We need to continue holding the lock until items is destroyed.
+    items.Lock(sCS);
+
+    return Move(items);
+  }
+
+  void Clear()
+  {
+    // This must be a no-op for shared VM policy; we can't have one interceptor
+    // wiping out trampolines for all interceptors in the process.
+  }
+
+  ~VMSharingPolicyShared() = default;
+
+  VMSharingPolicyShared(const VMSharingPolicyShared&) = delete;
+  VMSharingPolicyShared(VMSharingPolicyShared&&) = delete;
+  VMSharingPolicyShared& operator=(const VMSharingPolicyShared&) = delete;
+  VMSharingPolicyShared& operator=(VMSharingPolicyShared&&) = delete;
+
+private:
+  static bool find(DWORD aPid, ProcMapEntry** aOutEntry = nullptr)
+  {
+    MOZ_ASSERT(sPerProcVM);
+    if (!sPerProcVM) {
+      return false;
+    }
+
+    if (aOutEntry) {
+      *aOutEntry = nullptr;
+    }
+
+    for (auto&& mapping : *sPerProcVM) {
+      if (mapping == aPid) {
+        if (aOutEntry) {
+          *aOutEntry = &mapping;
+        }
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  static DWORD GetPid() { return ::GetCurrentProcessId(); }
+  static DWORD GetPid(HANDLE aHandle) { return ::GetProcessId(aHandle); }
+
+  DWORD mPid;
+  static StaticAutoPtr<MapT> sPerProcVM;
+  static CRITICAL_SECTION sCS;
+};
+
+template <typename MMPolicy, uint32_t kChunkSize>
+StaticAutoPtr<typename VMSharingPolicyShared<MMPolicy, kChunkSize>::MapT>
+  VMSharingPolicyShared<MMPolicy, kChunkSize>::sPerProcVM;
+
+template <typename MMPolicy, uint32_t kChunkSize>
+CRITICAL_SECTION VMSharingPolicyShared<MMPolicy, kChunkSize>::sCS;
+
 } // namespace interceptor
 } // namespace mozilla
 
 #endif // mozilla_interceptor_VMSharingPolicies_h