浏览代码

Fix VerifyZeroFill asserts in LargeHeapBlock allocations
After commit 93a8495f, the fuzzer was hitting asserts due to the fact that we attempt to verify that large heap block allocations
are zero'ed out, but we've already written the dummy v-table object. Update the verification function for specific locations to
become aware of the fact that the dummy vtable may already be installed and skip that memory for validation.
Add a unit test, and some GCStress coverage that would have caught this.

Daniel Libby 8 年之前
父节点
当前提交
b6cefd5db1

+ 1 - 0
bin/GCStress/GCStress.cpp

@@ -229,6 +229,7 @@ void BuildObjectCreationTable()
     objectCreationTable.AddWeightedEntry(&LeafObject<1001, 50000>::New, 1);
     objectCreationTable.AddWeightedEntry(&ScannedObject<1001, 50000>::New, 10);
     objectCreationTable.AddWeightedEntry(&BarrierObject<1001, 50000>::New, 2);
+    objectCreationTable.AddWeightedEntry(&FinalizedObject<1001, 50000>::New, 2);
 //    objectCreationTable.AddWeightedEntry(&TrackedObject<1001, 50000>::New, 2);    // Large tracked objects are not supported
 //    objectCreationTable.AddWeightedEntry(&RecyclerVisitedObject<1001, 50000>::New, 2); // Large recycler visited objects are not supported
 }

+ 1 - 0
bin/GCStress/RecyclerTestObject.cpp

@@ -10,6 +10,7 @@ size_t RecyclerTestObject::walkObjectCount = 0;
 size_t RecyclerTestObject::walkScannedByteCount = 0;
 size_t RecyclerTestObject::walkBarrierByteCount = 0;
 size_t RecyclerTestObject::walkTrackedByteCount = 0;
+size_t RecyclerTestObject::walkFinalizedByteCount = 0;
 size_t RecyclerTestObject::walkRecyclerVisitedByteCount = 0;
 size_t RecyclerTestObject::walkLeafByteCount = 0;
 size_t RecyclerTestObject::currentWalkDepth = 0;

+ 59 - 2
bin/GCStress/RecyclerTestObject.h

@@ -35,6 +35,7 @@ public:
         walkScannedByteCount = 0;
         walkBarrierByteCount = 0;
         walkTrackedByteCount = 0;
+        walkFinalizedByteCount = 0;
         walkRecyclerVisitedByteCount = 0;
         walkLeafByteCount = 0;
         maxWalkDepth = 0;
@@ -42,7 +43,7 @@ public:
         currentWalkDepth = 0;
 
         wprintf(_u("-------------------------------------------\n"));
-        wprintf(_u("Full heap walk starting\n"));
+        wprintf(_u("Full heap walk starting. Current generation: %12llu\n"), (unsigned long long) currentGeneration);
     }
 
     static void WalkReference(RecyclerTestObject * object)
@@ -79,9 +80,10 @@ public:
         wprintf(_u("Scanned Bytes:          %12llu\n"), (unsigned long long) walkScannedByteCount);
         wprintf(_u("Barrier Bytes:          %12llu\n"), (unsigned long long) walkBarrierByteCount);
         wprintf(_u("Tracked Bytes:          %12llu\n"), (unsigned long long) walkTrackedByteCount);
+        wprintf(_u("Finalized Bytes:        %12llu\n"), (unsigned long long) walkFinalizedByteCount);
         wprintf(_u("RecyclerVisited Bytes:  %12llu\n"), (unsigned long long) walkRecyclerVisitedByteCount);
         wprintf(_u("Leaf Bytes:             %12llu\n"), (unsigned long long) walkLeafByteCount);
-        wprintf(_u("Total Bytes:            %12llu\n"), (unsigned long long) (walkScannedByteCount + walkBarrierByteCount + walkTrackedByteCount + walkLeafByteCount + walkRecyclerVisitedByteCount));
+        wprintf(_u("Total Bytes:            %12llu\n"), (unsigned long long) (walkScannedByteCount + walkBarrierByteCount + walkTrackedByteCount + walkFinalizedByteCount + walkLeafByteCount + walkRecyclerVisitedByteCount));
         wprintf(_u("Max Depth:              %12llu\n"), (unsigned long long) maxWalkDepth);
     }
 
@@ -110,6 +112,7 @@ protected:
     static size_t walkLeafByteCount;
     static size_t walkBarrierByteCount;
     static size_t walkTrackedByteCount;
+    static size_t walkFinalizedByteCount;
     static size_t walkRecyclerVisitedByteCount;
     static size_t currentWalkDepth;
     static size_t maxWalkDepth;
@@ -311,6 +314,60 @@ private:
     FieldNoBarrier(RecyclerTestObject *) references[0];  // SWB-TODO: is this correct?
 };
 
+// A type of object that is finalizable, but not traced/tracked so that it can be used to test finalization
+// for LargeHeapBlock (which currently supports the FinalizeBit, but not TrackBit)
+template <unsigned int minCount, unsigned int maxCount>
+class FinalizedObject : public RecyclerTestObject, public FinalizableObject
+{
+private:
+    FinalizedObject(unsigned int count) :
+        count(count)
+    {
+        for (unsigned int i = 0; i < count; i++)
+        {
+            references[i] = nullptr;
+        }
+    }
+
+public:
+    static RecyclerTestObject * New()
+    {
+        unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1);
+
+        return RecyclerNewFinalizedPlus(recyclerInstance, sizeof(RecyclerTestObject *) * count, FinalizedObject, count);
+    }
+
+    virtual bool TryGetRandomLocation(Location * location) override
+    {
+        // Get a random slot and construct a Location for it
+        *location = Location::Scanned(&references[GetRandomInteger(count)]);
+
+        return true;
+    }
+
+    virtual void Mark(Recycler * recycler) override { VerifyCondition(false); };
+
+    // Finalize implementation.
+    virtual void Finalize(bool isShutdown) override { }
+    virtual void Dispose(bool isShutdown) override { }
+
+
+protected:
+    virtual void DoWalkObject() override
+    {
+        walkFinalizedByteCount += sizeof(FinalizedObject) + count * sizeof(RecyclerTestObject *);
+
+        for (unsigned int i = 0; i < count; i++)
+        {
+            RecyclerTestObject::WalkReference(references[i]);
+        }
+    }
+
+private:
+    Field(unsigned int) count;
+    FieldNoBarrier(RecyclerTestObject *) references[0];  // SWB-TODO: is this correct?
+};
+
 #ifdef RECYCLER_VISITED_HOST
 
 template <unsigned int minCount, unsigned int maxCount>

+ 22 - 3
lib/Common/Memory/Recycler.cpp

@@ -1383,7 +1383,7 @@ Recycler::TryLargeAlloc(HeapInfo * heap, size_t size, ObjectInfoBits attributes,
         if (memBlock != nullptr)
         {
 #ifdef RECYCLER_ZERO_MEM_CHECK
-            VerifyZeroFill(memBlock, sizeCat);
+            VerifyLargeAllocZeroFill(memBlock, sizeCat, attributes);
 #endif
             return memBlock;
         }
@@ -1406,7 +1406,7 @@ Recycler::TryLargeAlloc(HeapInfo * heap, size_t size, ObjectInfoBits attributes,
             if (memBlock != nullptr)
             {
 #ifdef RECYCLER_ZERO_MEM_CHECK
-                VerifyZeroFill(memBlock, size);
+                VerifyLargeAllocZeroFill(memBlock, size, attributes);
 #endif
                 return memBlock;
             }
@@ -1422,7 +1422,7 @@ Recycler::TryLargeAlloc(HeapInfo * heap, size_t size, ObjectInfoBits attributes,
     memBlock = heapBlock->Alloc(sizeCat, attributes);
     Assert(memBlock != nullptr);
 #ifdef RECYCLER_ZERO_MEM_CHECK
-    VerifyZeroFill(memBlock, sizeCat);
+    VerifyLargeAllocZeroFill(memBlock, sizeCat, attributes);
 #endif
     return memBlock;
 }
@@ -7284,6 +7284,25 @@ Recycler::VerifyZeroFill(void * address, size_t size)
 
     Assert(IsAll((byte *)address, size, expectedFill));
 }
+
+void
+Recycler::VerifyLargeAllocZeroFill(void * address, size_t size, ObjectInfoBits attributes)
+{
+    // Large allocs will have already written the dummy vtable at the beginning of the allocation
+    // if either FinalizeBit or TrackBit attributes were set. Skip the verify for that memory
+    // if that is the case.
+    if ((attributes & (FinalizeBit | TrackBit)) != 0)
+    {
+        // Verify that it really is the dummy v-table before skipping it.
+        DummyVTableObject dummy;
+        Assert((*(void**)(&dummy)) == *((void**)address));
+
+        address = ((char*)address) + sizeof(DummyVTableObject);
+        size -= sizeof(DummyVTableObject);
+    }
+    VerifyZeroFill(address, size);
+}
+
 #endif
 
 #ifdef RECYCLER_MEMORY_VERIFY

+ 1 - 0
lib/Common/Memory/Recycler.h

@@ -1417,6 +1417,7 @@ public:
 #endif
 #ifdef RECYCLER_ZERO_MEM_CHECK
     void VerifyZeroFill(void * address, size_t size);
+    void VerifyLargeAllocZeroFill(void * address, size_t size, ObjectInfoBits attributes);
 #endif
 #ifdef RECYCLER_DUMP_OBJECT_GRAPH
     bool DumpObjectGraph(RecyclerObjectGraphDumper::Param * param = nullptr);

文件差异内容过多而无法显示
+ 12 - 0
test/Object/propertyRecordLargeHeapBlock.js


+ 6 - 0
test/Object/rlexe.xml

@@ -61,6 +61,12 @@
       <compile-flags>-args summary -endargs</compile-flags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>propertyRecordLargeHeapBlock.js</files>
+      <compile-flags>-args summary -endargs</compile-flags>
+    </default>
+  </test>
   <test>
     <default>
         <files>toLocaleString2.js</files>

部分文件因为文件数量过多而无法显示