Bug 1510724 Part 2 - Add new HashTable diagnostics, r=lsmyth.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 28 Nov 2018 09:11:33 -1000
changeset 505570 a69ff828253927c4731ec5dc39d29861fce9bcdf
parent 505569 c1a7bed23922c06094d251ac6de96af92cc6d649
child 505571 5aa50800cd8b74b0b86ceca0d22bd76f6f633857
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsmyth
bugs1510724
milestone65.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 1510724 Part 2 - Add new HashTable diagnostics, r=lsmyth.
toolkit/recordreplay/HashTable.cpp
toolkit/recordreplay/HashTable.h
--- a/toolkit/recordreplay/HashTable.cpp
+++ b/toolkit/recordreplay/HashTable.cpp
@@ -9,18 +9,16 @@
 #include "mozilla/StaticMutex.h"
 
 #include "HashTable.h"
 #include "InfallibleVector.h"
 #include "ProcessRecordReplay.h"
 #include "ProcessRedirect.h"
 #include "ValueIndex.h"
 
-#include "PLDHashTable.h"
-
 #include <unordered_set>
 
 namespace mozilla {
 namespace recordreplay {
 
 // Hash tables frequently incorporate pointer values into the hash numbers they
 // compute, which are not guaranteed to be the same between recording and
 // replaying and consequently lead to inconsistent hash numbers and iteration
@@ -70,22 +68,28 @@ class StableHashTableInfo {
   HashNumber mLastNewHash;
 
   // Counter for generating new hash numbers for entries added to the table.
   // This increases monotonically, though it is fine if it overflows.
   uint32_t mHashGenerator;
 
   // Buffer with executable memory for use in binding functions.
   uint8_t* mCallbackStorage;
+  uint32_t mCallbackStorageSize;
   static const size_t CallbackStorageCapacity = 4096;
 
   // Whether this table has been marked as destroyed and is unusable. This is
   // temporary state to detect UAF bugs related to this class.
   bool mDestroyed;
 
+  // Associated table and hash of its callback storage, for more integrity
+  // checking.
+  void* mTable;
+  uint32_t mCallbackHash;
+
   // Get an existing key in the table.
   KeyInfo* FindKeyInfo(HashNumber aOriginalHash, const void* aKey,
                        HashInfo** aHashInfo = nullptr) {
     HashToKeyMap::iterator iter = mHashToKey.find(aOriginalHash);
     MOZ_RELEASE_ASSERT(iter != mHashToKey.end());
 
     HashInfo* hashInfo = iter->second.get();
     for (KeyInfo& keyInfo : hashInfo->mKeys) {
@@ -100,17 +104,20 @@ class StableHashTableInfo {
   }
 
  public:
   StableHashTableInfo()
       : mLastKey(nullptr),
         mLastNewHash(0),
         mHashGenerator(0),
         mCallbackStorage(nullptr),
-        mDestroyed(false) {
+        mDestroyed(false),
+        mTable(nullptr),
+        mCallbackHash(0)
+  {
     // Use AllocateMemory, as the result will have RWX permissions.
     mCallbackStorage =
         (uint8_t*)AllocateMemory(CallbackStorageCapacity, MemoryKind::Tracked);
 
     MarkValid();
   }
 
   ~StableHashTableInfo() {
@@ -123,16 +130,27 @@ class StableHashTableInfo {
 
   bool IsDestroyed() { return mDestroyed; }
 
   void MarkDestroyed() {
     MOZ_RELEASE_ASSERT(!IsDestroyed());
     mDestroyed = true;
   }
 
+  void CheckIntegrity(void* aTable) {
+    MOZ_RELEASE_ASSERT(aTable);
+    if (!mTable) {
+      mTable = aTable;
+      mCallbackHash = HashBytes(mCallbackStorage, mCallbackStorageSize);
+    } else {
+      MOZ_RELEASE_ASSERT(mTable == aTable);
+      MOZ_RELEASE_ASSERT(mCallbackHash == HashBytes(mCallbackStorage, mCallbackStorageSize));
+    }
+  }
+
   void AddKey(HashNumber aOriginalHash, const void* aKey, HashNumber aNewHash) {
     HashToKeyMap::iterator iter = mHashToKey.find(aOriginalHash);
     if (iter == mHashToKey.end()) {
       iter = mHashToKey
                  .insert(HashToKeyMap::value_type(aOriginalHash,
                                                   MakeUnique<HashInfo>()))
                  .first;
     }
@@ -184,19 +202,27 @@ class StableHashTableInfo {
   HashNumber GetOriginalHashNumber(const void* aKey) {
     KeyToHashMap::iterator iter = mKeyToHash.find(aKey);
     MOZ_RELEASE_ASSERT(iter != mKeyToHash.end());
     return iter->second;
   }
 
   class Assembler : public recordreplay::Assembler {
    public:
+    StableHashTableInfo& mInfo;
+
     explicit Assembler(StableHashTableInfo& aInfo)
         : recordreplay::Assembler(aInfo.mCallbackStorage,
-                                  CallbackStorageCapacity) {}
+                                  CallbackStorageCapacity),
+          mInfo(aInfo)
+    {}
+
+    ~Assembler() {
+      mInfo.mCallbackStorageSize = Current() - mInfo.mCallbackStorage;
+    }
   };
 
   // Use the callback storage buffer to create a new function T which has one
   // fewer argument than S and calls S with aArgument bound to the last
   // argument position. See BindFunctionArgument in ProcessRedirect.h
   template <typename S, typename T>
   void NewBoundFunction(Assembler& aAssembler, S aFunction, void* aArgument,
                         size_t aArgumentPosition, T* aTarget) {
@@ -289,19 +315,28 @@ struct PLHashTableInfo : public StableHa
                   PLHashComparator aValueCompare,
                   const PLHashAllocOps* aAllocOps, void* aAllocPrivate)
       : mKeyHash(aKeyHash),
         mKeyCompare(aKeyCompare),
         mValueCompare(aValueCompare),
         mAllocOps(aAllocOps),
         mAllocPrivate(aAllocPrivate) {}
 
-  static PLHashTableInfo* FromPrivate(void* aAllocPrivate) {
+  static PLHashTableInfo* MaybeFromPrivate(void* aAllocPrivate) {
     PLHashTableInfo* info = reinterpret_cast<PLHashTableInfo*>(aAllocPrivate);
-    MOZ_RELEASE_ASSERT(!info->IsDestroyed());
+    if (info->IsValid()) {
+      MOZ_RELEASE_ASSERT(!info->IsDestroyed());
+      return info;
+    }
+    return nullptr;
+  }
+
+  static PLHashTableInfo* FromPrivate(void* aAllocPrivate) {
+    PLHashTableInfo* info = MaybeFromPrivate(aAllocPrivate);
+    MOZ_RELEASE_ASSERT(info);
     return info;
   }
 };
 
 static void* WrapPLHashAllocTable(void* aAllocPrivate, PRSize aSize) {
   PLHashTableInfo* info = PLHashTableInfo::FromPrivate(aAllocPrivate);
   return info->mAllocOps
              ? info->mAllocOps->allocTable(info->mAllocPrivate, aSize)
@@ -381,19 +416,28 @@ void GeneratePLHashTableCallbacks(PLHash
       *aKeyHash, *aKeyCompare, *aValueCompare, *aAllocOps, *aAllocPrivate);
   PLHashTableInfo::Assembler assembler(*info);
   info->NewBoundFunction(assembler, PLHashComputeHash, info, 1, aKeyHash);
   *aAllocOps = &gWrapPLHashAllocOps;
   *aAllocPrivate = info;
 }
 
 void DestroyPLHashTableCallbacks(void* aAllocPrivate) {
-  PLHashTableInfo* info = PLHashTableInfo::FromPrivate(aAllocPrivate);
-  info->MarkDestroyed();
-  // delete info;
+  PLHashTableInfo* info = PLHashTableInfo::MaybeFromPrivate(aAllocPrivate);
+  if (info) {
+    info->MarkDestroyed();
+    //delete info;
+  }
+}
+
+void CheckPLHashTable(PLHashTable* aTable) {
+  PLHashTableInfo* info = PLHashTableInfo::MaybeFromPrivate(aTable->allocPriv);
+  if (info) {
+    info->CheckIntegrity(aTable);
+  }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // PLDHashTable Stabilization
 ///////////////////////////////////////////////////////////////////////////////
 
 // For each PLDHashTable in the process, a PLDHashTableInfo is generated. This
 // structure is supplied to its callbacks using bound functions.
@@ -418,16 +462,23 @@ struct PLDHashTableInfo : public StableH
     return nullptr;
   }
 
   static PLDHashTableInfo* FromOps(const PLDHashTableOps* aOps) {
     PLDHashTableInfo* res = MaybeFromOps(aOps);
     MOZ_RELEASE_ASSERT(res);
     return res;
   }
+
+  static void CheckIntegrity(PLDHashTable* aTable) {
+    PLDHashTableInfo* info = MaybeFromOps(aTable->RecordReplayWrappedOps());
+    if (info) {
+      info->StableHashTableInfo::CheckIntegrity(aTable);
+    }
+  }
 };
 
 static PLDHashNumber PLDHashTableComputeHash(const void* aKey,
                                              PLDHashTableInfo* aInfo) {
   MOZ_RELEASE_ASSERT(!aInfo->IsDestroyed());
   uint32_t originalHash = aInfo->mOps->hashKey(aKey);
   HashNumber newHash;
   if (aInfo->HasMatchingKey(originalHash,
@@ -526,10 +577,14 @@ MOZ_EXPORT void RecordReplayInterface_In
   PLDHashTableInfo* firstInfo = PLDHashTableInfo::FromOps(aFirstOps);
   PLDHashTableInfo* secondInfo = PLDHashTableInfo::FromOps(aSecondOps);
 
   secondInfo->MoveContentsFrom(*firstInfo);
 }
 
 }  // extern "C"
 
+void CheckPLDHashTable(PLDHashTable* aTable) {
+  PLDHashTableInfo::CheckIntegrity(aTable);
+}
+
 }  // namespace recordreplay
 }  // namespace mozilla
--- a/toolkit/recordreplay/HashTable.h
+++ b/toolkit/recordreplay/HashTable.h
@@ -3,25 +3,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_recordreplay_HashTable_h
 #define mozilla_recordreplay_HashTable_h
 
 #include "plhash.h"
+#include "PLDHashTable.h"
 
 namespace mozilla {
 namespace recordreplay {
 
 // Routines for creating specialized callbacks for PLHashTables that preserve
 // iteration order, similar to those for PLDHashTables in RecordReplay.h.
 void GeneratePLHashTableCallbacks(PLHashFunction* aKeyHash,
                                   PLHashComparator* aKeyCompare,
                                   PLHashComparator* aValueCompare,
                                   const PLHashAllocOps** aAllocOps,
                                   void** aAllocPrivate);
 void DestroyPLHashTableCallbacks(void* aAllocPrivate);
 
+void CheckPLHashTable(PLHashTable* aTable);
+void CheckPLDHashTable(PLDHashTable* aTable);
+
 }  // namespace recordreplay
 }  // namespace mozilla
 
 #endif  // mozilla_recordreplay_HashTable_h