[release/10.0] JIT: Add visit budget checks to IsMonotonicallyIncreasing and ComputeDoesOverflow - #133161
Open
EgorBo wants to merge 1 commit into
Open
[release/10.0] JIT: Add visit budget checks to IsMonotonicallyIncreasing and ComputeDoesOverflow#133161EgorBo wants to merge 1 commit into
EgorBo wants to merge 1 commit into
Conversation
…ing and ComputeDoesOverflow Backport of dotnet#125156 to release/10.0. `IsMonotonicallyIncreasing` and `ComputeDoesOverflow` lacked the `m_nVisitBudget` guard that `ComputeRange` already uses, allowing unbounded recursion cost in pathological cases. Fixes dotnet#133147, where a single 2415-byte-IL async state machine method (control-flow-flattened by an obfuscator) took ~10s and ~7.9GB of JIT arena memory to compile, causing the process to be OOM-killed on its first call to that method. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a175f87a-8911-41d2-9b9b-56b14d84f5d0
|
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.
🟢 Approval recommended
The change is a minimal, conservative early-exit guard consistent with existing visit-budget usage and does not introduce observable correctness risk (only potentially reduces an optimization when over budget).
Pull request overview
Backport to release/10.0 of a JIT range-check analysis hardening change that prevents pathological compilation-time and JIT arena memory blowups by enforcing the existing visit-budget mechanism in additional recursive helpers.
Changes:
- Add
m_nVisitBudgetguard/decrement toRangeCheck::IsMonotonicallyIncreasingto bound recursive UD-chain walking. - Add
m_nVisitBudgetguard/decrement toRangeCheck::ComputeDoesOverflowto bound recursive overflow checks. - Ensure both early-exit paths return conservative results and occur before mutating the search-path state.
File summaries
| File | Description |
|---|---|
| src/coreclr/jit/rangecheck.cpp | Adds visit-budget checks to two recursive range-analysis helpers to prevent extreme JIT throughput/memory pathologies on obfuscated control flow. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Member
Author
|
PTAL @AndyAyersMS, backport of #125156 |
AndyAyersMS
approved these changes
Sep 3, 2026
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.
Backport of #125156 to
release/10.0.Customer Impact
A single method can take ~10 seconds and ~7.9 GB of JIT arena memory to compile, on the thread that first calls it. Reported in #133147: a containerized ASP.NET Core service was OOM-killed by the cgroup limit on the first request that reached one method, returning
BadGateway. The memory is JIT arena, not the managed heap, so it is invisible to GC counters andeeheap, which makes it very hard to diagnose.The trigger is a method with a flattened control flow graph (dispatcher
switchloops, opaque predicates, backward branches insidefinallyhandlers), as produced by commercial obfuscators with control-flow obfuscation enabled. The reporter hit it in production; the obfuscator output is non-deterministic, so it appeared and disappeared between releases with no source change.The generated code is correct. Only JIT throughput and JIT memory are affected.
Regression
No, this is a long-standing issue, not a regression from a previous release. It reproduces on 10.0.10 and 10.0.11.
Testing
Reproduced with the reduced IL repro from #133147 (a 2415-byte-IL async state machine
MoveNext), win-x64,DOTNET_TieredCompilation=0:release/10.0built locally, without this changerelease/10.0with this changeDOTNET_JitTimeLogCsvon 10.0.11 attributes 40,743,816,978 of 40,767,907,863 total cycles (99.94%) to theOptimize index checksphase, withTotal Bytes Allocatedof 8.2 GB for a graph of only 194 basic blocks. That isolates the cost to range check analysis rather than to graph size.Causality was confirmed independently on
main, which already carries #125156 and compiles the same repro in 39 ms / 47 MB. Reverting only these two budget checks onmainbrings the pathology back and amplifies it: the compile did not finish within 13 minutes and reached 45 GB working set / 191 GB committed.jit-formatis clean.Risk
Low. The change adds the same
m_nVisitBudgetguard thatComputeRangealready uses, so it only takes effect once the existing visit budget is exhausted, which does not happen in ordinary code.Both early exits return the conservative answer:
IsMonotonicallyIncreasingreturnsfalse, meaning "not proven monotonic".ComputeDoesOverflowreturnstrue, meaning "may overflow".Both block bounds-check elimination rather than enabling it, so no unsound optimization can result. The checks are placed before
GetSearchPath()->Set(...)so no stale search-path entries are left behind on early exit.This has been in
mainsince March 2026 (#125156) with no follow-up issues.