Range check removes the bounds check on a[j] in a loop where j can become negative after x wraps around, leading to an out-of-bounds read.
Minimal Repro
using System;
using System.Runtime.CompilerServices;
public class Program
{
public static void Main()
{
try
{
Console.WriteLine(Test(new int[101], true));
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name);
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int Test(int[] a, bool flag)
{
int j = 0, x = 0, sum = 0;
if (a.Length != 101)
return 0;
while (j < 101)
{
if (flag)
sum += a[j];
x++;
if (x >= -100 && x <= 10)
j += x;
}
return sum;
}
}
(Takes ~5 seconds since x has to wrap around once.)
Expected
IndexOutOfRangeException (j reaches 55, then after x wraps to -100, j becomes -45). Same as with DOTNET_JitMinOpts=1 or DOTNET_JitDoRangeAnalysis=0.
Actual
UB
Regression?
Yes, regressed in .NET 10: .NET 8 and 9 are correct; .NET 10, 11 RC2 and main are affected (win-x64).
Notes
In DoesVarDefOverflow(x), Merge(<Dependent, 100>, [-100, 10]) equals the top-level range of j, so it returns "no overflow" without inspecting the def x = x + 1.
Widen then treats j += x as monotonic and gives j the range [0, 100]. The shortcut compares against the top-level range instead of x's own contribution, and is also unsound when the lower limit is Dependent.
Range check removes the bounds check on
a[j]in a loop wherejcan become negative afterxwraps around, leading to an out-of-bounds read.Minimal Repro
(Takes ~5 seconds since
xhas to wrap around once.)Expected
IndexOutOfRangeException(jreaches 55, then afterxwraps to-100,jbecomes-45). Same as withDOTNET_JitMinOpts=1orDOTNET_JitDoRangeAnalysis=0.Actual
UB
Regression?
Yes, regressed in .NET 10: .NET 8 and 9 are correct; .NET 10, 11 RC2 and main are affected (win-x64).
Notes
In
DoesVarDefOverflow(x),Merge(<Dependent, 100>, [-100, 10])equals the top-level range ofj, so it returns "no overflow" without inspecting the defx = x + 1.Widenthen treatsj += xas monotonic and givesjthe range[0, 100]. The shortcut compares against the top-level range instead ofx's own contribution, and is also unsound when the lower limit isDependent.