-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ILGenerator computation of maxstack is too conservative #63805
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Tagging subscribers to this area: @dotnet/area-system-reflection-emit Issue DetailsDescriptionWhen generating a dynamic method both with many unconditional jumps, the ILGenerator's computation of maxstack is way to large - it adds the max stack of each basic block ending with an unconditional transfer (br, ret, throw, etc). If the generated code has many such bbs (eg, from the "then" clauses of if-then-else constructs) this sum can overflow 2^16. When it does, the maxstack recorded/used is the computed value mod 2^16. When that is less than the correct value, the JIT throws an For example, if there are 2^12 (4096) such blocks, each with a max stack of 2^4 (16), the computed maxstack is 2^16. That value mod 2^16 is zero. Since that is less than the actual (16 or more), the JIT will throw. See the attached example code. This is written as a "test" that passes when the bug is present. The line enclosed by Reproduction StepsBuild and run the unit test code above with .Net Core (verified with 3.1, and many later versions). Expected behaviorThe line marked with Actual behaviorAn InvalidProgramException is thrown. Also, the computed max stack values (as written to console) are way too large. The actual max stack needed for this dynamic method is 4, and it doesn't depend on the number of basic blocks (the Regression?No response Known WorkaroundsNo good ones. This is blocking. ConfigurationNo response Other informationI'm working on a fix by fixing the computation in
|
See also #62913 for discussion on how maxstack is (or can be) calculated. |
@shonk-msft any update? |
Hi @steveharter, sorry for the delay. I implemented a fix long ago but haven't gotten around to submitting the PR. I'll be handing this off to a co-worker Monday who should be more responsive :-). |
When generating a dynamic method both with many unconditional jumps, the ILGenerator's computation of maxstack is way to large - it adds the max stack of each basic block ending with an unconditional transfer (br, ret, throw, etc). If the generated code has many such bbs (eg, from the "then" clauses of if-then-else constructs) this sum can overflow 2^16. When it does, the maxstack recorded/used is the computed value mod 2^16. When that is less than the correct value, the JIT throws an InvalidProgramException. Keep track of the stack depth at various positions and use it to calculate an adjustment to the depth. Fix dotnet#63805
* Fix ILGenerator maxstack computation When generating a dynamic method both with many unconditional jumps, the ILGenerator's computation of maxstack is way to large - it adds the max stack of each basic block ending with an unconditional transfer (br, ret, throw, etc). If the generated code has many such bbs (eg, from the "then" clauses of if-then-else constructs) this sum can overflow 2^16. When it does, the maxstack recorded/used is the computed value mod 2^16. When that is less than the correct value, the JIT throws an InvalidProgramException. Keep track of the stack depth at various positions and use it to calculate an adjustment to the depth. Fix #63805 * Fix System.Linq.Expressions tests * Remove stack handling in MethodBuilder
Description
When generating a dynamic method both with many unconditional jumps, the ILGenerator's computation of maxstack is way to large - it adds the max stack of each basic block ending with an unconditional transfer (br, ret, throw, etc). If the generated code has many such bbs (eg, from the "then" clauses of if-then-else constructs) this sum can overflow 2^16. When it does, the maxstack recorded/used is the computed value mod 2^16. When that is less than the correct value, the JIT throws an
InvalidProgramException
.For example, if there are 2^12 (4096) such blocks, each with a max stack of 2^4 (16), the computed maxstack is 2^16. That value mod 2^16 is zero. Since that is less than the actual (16 or more), the JIT will throw.
See the attached example code. This is written as a "test" that passes when the bug is present. The line enclosed by
Assert.ThrowsException
really should not throw.ClrBugTests.cs.txt
Reproduction Steps
Build and run the unit test code above with .Net Core (verified with 3.1, and many later versions).
Expected behavior
The line marked with
Assert.ThrowsException
should not throw.Actual behavior
An InvalidProgramException is thrown. Also, the computed max stack values (as written to console) are way too large. The actual max stack needed for this dynamic method is 4, and it doesn't depend on the number of basic blocks (the
num
parameter). The computed max stack values increase withnum
.Regression?
No response
Known Workarounds
No good ones. This is blocking.
Configuration
No response
Other information
I'm working on a fix by fixing the computation in
ILGenerator.cs
.The text was updated successfully, but these errors were encountered: