Fix SIP (sweep-in-plan) plan_allocated corruption in process_remaining_regions#131015
Open
cshung wants to merge 1 commit into
Open
Fix SIP (sweep-in-plan) plan_allocated corruption in process_remaining_regions#131015cshung wants to merge 1 commit into
cshung wants to merge 1 commit into
Conversation
…g_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].
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adjusts regions-GC planning in gc_heap::process_remaining_regions to prevent heap_segment_plan_allocated from being set to a pointer that falls outside the selected non-SIP region after SIP skipping, avoiding downstream state corruption and debug asserts.
Changes:
- Replace direct assignment of
heap_segment_plan_allocated(current_region)from the consing allocation pointer with a guarded/clamped assignment. - When the consing allocation pointer is outside
[heap_segment_mem(current_region), heap_segment_reserved(current_region)], treat the region as empty and setplan_allocatedtomeminstead.
cshung
force-pushed
the
fix-sip-plan-allocated
branch
from
July 18, 2026 19:35
8d35fc1 to
56e2bc8
Compare
This was referenced Jul 18, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix SIP (sweep-in-plan) plan_allocated corruption in process_remaining_regions
Problem
With the regions GC, setting
DOTNET_GCEnableSpecialRegions=1trips a_DEBUGGC assert shortly after startup (SIGABRT in a Checked build). Two different asserts can fire depending on heap layout:fix_generation_bounds—plan_allocated == memverify_regions—heap_segment_allocated <= heap_segment_reserved(FATAL_GC_ERROR)Both trace back to a single root cause in
process_remaining_regions.Root cause
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 assigned that region's
plan_allocateddirectly from the consing pointer:heap_segment_plan_allocated (current_region) = generation_allocation_pointer (consing_gen);But the consing pointer belongs to the skipped SIP region, not
current_region, so it lies outsidecurrent_region's[mem, reserved]range:mem→ the region can later become gen0's start region, trippingfix_generation_boundsand corrupting gen0 bounds.reserved→find_first_valid_regioncopiesplan_allocatedintoallocated, makingallocated > reservedand trippingverify_regions.Fix
A region reached by skipping a SIP allocation region is empty regardless of address direction, so its
plan_allocatedis simply itsmem. Clampplan_allocatedtomemwhenever the consing pointer falls outside[mem, reserved]:The clamp only triggers on the SIP-skip path (consing pointer out of range), so the default path is unchanged.
Validation
Checked CLR,
linux-x64.GC/API,GC/Scenarios,GC/Coverage) also pass withDOTNET_GCEnableSpecialRegions=1after the fix. No regressions with the switch off.DOTNET_GCStress=0xCand0xF(the latter with a reduced gen0 size to force frequent compacting GCs) across allocation-heavy scenarios (linked-list, jagged-array, server-model, large-object). Results identical with the switch on and off.