Range check eliminates the bounds check for a[x >>> (y & 3)] even though x can be negative, which yields a large positive index (e.g. 0x7FFFFFFF) and an out-of-bounds read.
Minimal Repro
using System;
using System.Runtime.CompilerServices;
public class Program
{
public static void Main()
{
try
{
Console.WriteLine(Test(-1, 1));
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("IndexOutOfRangeException");
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int Test(int x, int y)
{
int[] a = new int[51];
if (x <= 50)
{
int v = x >>> (y & 3);
if (v >= 0)
return a[v];
}
return 0;
}
}
Expected
Actual
Regression?
Yes, regressed in .NET 11: .NET 8, 9 and 10 are correct; .NET 11 RC2 and main are affected (win-x64).
Notes
RangeOps::ShiftRight computes the upper limit as r1.hi >> r2.lo whenever r1.hi is a non-negative constant, which is unsound for logical shifts when r1 may be negative.
The type-based override (#128146) only kicks in when r2.lo >= 1; for y & 3, r2.lo == 0, so x >>> [0..3] gets [.., 50], narrowed to [0..50] by v >= 0.
Range check eliminates the bounds check for
a[x >>> (y & 3)]even thoughxcan be negative, which yields a large positive index (e.g.0x7FFFFFFF) and an out-of-bounds read.Minimal Repro
Expected
Actual
Regression?
Yes, regressed in .NET 11: .NET 8, 9 and 10 are correct; .NET 11 RC2 and main are affected (win-x64).
Notes
RangeOps::ShiftRightcomputes the upper limit asr1.hi >> r2.lowheneverr1.hiis a non-negative constant, which is unsound for logical shifts whenr1may be negative.The type-based override (#128146) only kicks in when
r2.lo >= 1; fory & 3,r2.lo == 0, sox >>> [0..3]gets[.., 50], narrowed to[0..50]byv >= 0.