Fix issue 41413 (#41445)
authorPeter Sollich <petersol@microsoft.com>
Tue, 1 Sep 2020 10:23:21 +0000 (12:23 +0200)
committerGitHub <noreply@github.com>
Tue, 1 Sep 2020 10:23:21 +0000 (12:23 +0200)
Fix the AV issue in GCHeap::ApproxTotalBytesInUse:

- The AV is fixed by stopping the iteration when seg1 becomes null
- don't count segments that have been decommited
- stop iterating when seg1 becomes freeable_soh_segment - this takes care of the case where we have put a segment on the list, but the OS call to decommit hasn't come back.

These fixes will still leave a window where the result will be inaccurate. To reduce the chances of an inaccurate result, we try this 3 times.

src/coreclr/src/gc/gc.cpp

index adb9fea..8d21e36 100644 (file)
@@ -37867,15 +37867,26 @@ size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
     size_t totsize = 0;
     enter_spin_lock (&pGenGCHeap->gc_lock);
 
-    heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
-    // Get small block heap size info
-    totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
-    heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
-    while (seg1 != eph_seg)
-    {
-        totsize += heap_segment_allocated (seg1) -
-            heap_segment_mem (seg1);
-        seg1 = heap_segment_next (seg1);
+    // the complication with the following code is that background GC may
+    // remove the ephemeral segment while we are iterating
+    // if so, we retry a couple times and ultimately may report a slightly wrong result
+    for (int tries = 1; tries <= 3; tries++)
+    {
+        heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
+        // Get small block heap size info
+        totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
+        heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
+        while ((seg1 != eph_seg) && (seg1 != nullptr) && (seg1 != pGenGCHeap->freeable_soh_segment))
+        {
+            if (!heap_segment_decommitted_p (seg1))
+            {
+                totsize += heap_segment_allocated (seg1) -
+                    heap_segment_mem (seg1);
+            }
+            seg1 = heap_segment_next (seg1);
+        }
+        if (seg1 == eph_seg)
+            break;
     }
 
     //discount the fragmentation