From 56e2bc856fa13f22e6bac1d3151cfa690c14e146 Mon Sep 17 00:00:00 2001 From: Andrew Au <3410332+cshung@users.noreply.github.com> Date: Fri, 10 Jul 2026 23:02:53 +0000 Subject: [PATCH] Fix SIP (sweep-in-plan) plan_allocated corruption in process_remaining_regions When the allocation region for a generation is itself swept-in-plan (SIP), heap_segment_non_sip() skips it and returns a different, empty region. The region chain is not address-ordered, so the returned region may be at a higher or lower address than the skipped SIP region. The code then set that region's plan_allocated to the consing pointer, which belongs to the skipped SIP region and therefore lies outside the returned region's [mem, reserved] range - either below mem or above reserved. Two asserts fired depending on direction: - consing pointer below mem: the region could later become gen0's start region, tripping fix_generation_bounds (plan_allocated == mem) and corrupting gen0 bounds. - consing pointer above reserved: find_first_valid_region copies plan_allocated into allocated, making allocated > reserved and tripping verify_regions (FATAL_GC_ERROR). Since a region reached by skipping a SIP allocation region is empty regardless of address direction, clamp plan_allocated to the region's mem whenever the consing pointer falls outside [mem, reserved]. --- src/coreclr/gc/plan_phase.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/coreclr/gc/plan_phase.cpp b/src/coreclr/gc/plan_phase.cpp index 5d3802b282977c..1b8be955de2f0d 100644 --- a/src/coreclr/gc/plan_phase.cpp +++ b/src/coreclr/gc/plan_phase.cpp @@ -2981,7 +2981,18 @@ void gc_heap::process_remaining_regions (int current_plan_gen_num, generation* c if (!heap_segment_swept_in_plan (current_region)) { - heap_segment_plan_allocated (current_region) = generation_allocation_pointer (consing_gen); + // If the allocation region was swept in plan (SIP), heap_segment_non_sip above skipped + // it to land on this region. In that case the consing pointer belongs to the skipped + // region, not this one (and the skipped region may be at a higher or lower address, so + // the pointer can be below mem or above reserved). Either way this region is empty, so + // its plan_allocated is its mem. + uint8_t* plan_alloc = generation_allocation_pointer (consing_gen); + if ((plan_alloc < heap_segment_mem (current_region)) || + (plan_alloc > heap_segment_reserved (current_region))) + { + plan_alloc = heap_segment_mem (current_region); + } + heap_segment_plan_allocated (current_region) = plan_alloc; dprintf (REGIONS_LOG, ("h%d setting alloc seg %p plan alloc to %p", heap_number, heap_segment_mem (current_region), heap_segment_plan_allocated (current_region)));