Browse Source

Fix overflow computing the element count of a multi-dim array initializer

HandleSimpleArrayInitializer multiplies the array dimensions to size the list
it collects elements into. The dimensions come from the input assembly and
need not multiply within int range, and ICSharpCode.Decompiler is built with
CheckForOverflowUnderflow, so an implausible pair of dimensions aborted
decompilation of the whole member. The product is only a capacity hint, so it
can saturate.

Found by fuzzing nuget.org; reproduces on obfuscated assemblies.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
pull/3959/head
Siegfried Pammer 1 month ago committed by Siegfried Pammer
parent
commit
6fe6e06bad
  1. 10
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/MultidimensionalArray.cs
  2. 4
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

10
ICSharpCode.Decompiler.Tests/TestCases/Pretty/MultidimensionalArray.cs

@ -52,5 +52,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
return new int[10][,]; return new int[10][,];
} }
// The dimensions multiply to more than int.MaxValue; the initializer transform
// must cope with that instead of overflowing while sizing its element list.
public int[,] DimensionsExceedingIntRange()
{
int[,] array = new int[65536, 65536];
array[0, 0] = 1;
array[0, 1] = 2;
return array;
}
} }
} }

4
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -594,7 +594,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
instructionsToRemove = 0; instructionsToRemove = 0;
int elementCount = 0; int elementCount = 0;
int length = arrayLength.Aggregate(1, (t, l) => t * l); // The dimensions come from the IL and need not multiply within int range.
// This is only a capacity hint, so saturate rather than overflow.
int length = arrayLength.Aggregate(1, (t, l) => (int)Math.Min((long)t * l, int.MaxValue));
// Cannot pre-allocate the result array, because we do not know yet, // Cannot pre-allocate the result array, because we do not know yet,
// whether there is in fact an array initializer. // whether there is in fact an array initializer.
// To prevent excessive allocations, use min(|block|, arraySize) als initial capacity. // To prevent excessive allocations, use min(|block|, arraySize) als initial capacity.

Loading…
Cancel
Save