Skip to content

Commit f5455d3

Browse files
dakersnartannergoodingdanmoseley
authored
Fix incorrect results of right shifting huge BigIntegers error reported in Issue #65633 (#67867)
* Fix #65633, multiplication overflow causing incorrect right shift results * Add unit test reproducing incorrect right shift results for BigInteger (#65633) * Fix inconsistent formatting * Expand LargeNegativeBigIntegerShiftTest to validate values, expose private BigInteger members to tests * Apply suggestions from code review Co-authored-by: Tanner Gooding <tagoo@outlook.com> * Restrict unit test to 64-bit processors * Document reasoning for restricting the test to 64-bit Co-authored-by: Dan Moseley <danmose@microsoft.com> Co-authored-by: Tanner Gooding <tagoo@outlook.com> Co-authored-by: Dan Moseley <danmose@microsoft.com>
1 parent 400d171 commit f5455d3

File tree

5 files changed

+60
-1
lines changed

5 files changed

+60
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
[assembly: InternalsVisibleTo("System.Runtime.Numerics.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004b86c4cb78549b34bab61a3b1800e23bfeb5b3ec390074041536a7e3cbd97f5f04cf0f857155a8928eaa29ebfd11cfbbad3ba70efea7bda3226c6a8d370a4cd303f714486b6ebc225985a638471e6ef571cc92a4613c00b8fa65d61ccee0cbe5f36330c9a01f4183559f1bef24cc2917c6d913e3a541333a1d05d9bed22b38cb")]

src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
Link="CoreLib\System\Text\ValueStringBuilder.cs" />
2626
<Compile Include="$(CommonPath)System\HexConverter.cs"
2727
Link="Common\System\HexConverter.cs" />
28+
<Compile Include="Properties\InternalsVisibleTo.cs" />
2829
</ItemGroup>
2930
<ItemGroup>
3031
<Reference Include="System.Memory" />

src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2211,7 +2211,7 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
22112211

22122212
if (negx)
22132213
{
2214-
if (shift >= (kcbitUint * xd.Length))
2214+
if (shift >= ((long)kcbitUint * xd.Length))
22152215
{
22162216
result = MinusOne;
22172217
goto exit;

src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs

+48
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,54 @@ public static void BigShiftsTest()
2525
}
2626
}
2727

28+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] // May fail on 32-bit due to a large memory requirement
29+
public static void LargeNegativeBigIntegerShiftTest()
30+
{
31+
// Create a very large negative BigInteger
32+
BigInteger bigInt = new BigInteger(-1) << int.MaxValue;
33+
Assert.Equal(2147483647, bigInt.GetBitLength());
34+
Assert.Equal(-1, bigInt.Sign);
35+
36+
// Validate internal representation.
37+
// At this point, bigInt should be a 1 followed by int.MaxValue zeros.
38+
// Given this, bigInt._bits is expected to be structured as follows:
39+
// - _bits.Length == (int.MaxValue + 1) / (8 * sizeof(uint))
40+
// - First (_bits.Length - 1) elements: 0x00000000
41+
// - Last element: 0x80000000
42+
// ^------ (There's the leading '1')
43+
44+
Assert.Equal(((uint)int.MaxValue + 1) / (8 * sizeof(uint)), (uint)bigInt._bits.Length);
45+
46+
uint i = 0;
47+
for (; i < (bigInt._bits.Length - 1); i++) {
48+
Assert.Equal(0x00000000u, bigInt._bits[i]);
49+
}
50+
51+
Assert.Equal(0x80000000u, bigInt._bits[i]);
52+
53+
// Right shift the BigInteger
54+
BigInteger shiftedBigInt = bigInt >> 1;
55+
Assert.Equal(2147483646, shiftedBigInt.GetBitLength());
56+
Assert.Equal(-1, shiftedBigInt.Sign);
57+
58+
// Validate internal representation.
59+
// At this point, shiftedBigInt should be a 1 followed by int.MaxValue - 1 zeros.
60+
// Given this, shiftedBigInt._bits is expected to be structured as follows:
61+
// - _bits.Length == (int.MaxValue + 1) / (8 * sizeof(uint))
62+
// - First (_bits.Length - 1) elements: 0x00000000
63+
// - Last element: 0x40000000
64+
// ^------ (the '1' is now one position to the right)
65+
66+
Assert.Equal(((uint)int.MaxValue + 1) / (8 * sizeof(uint)), (uint)shiftedBigInt._bits.Length);
67+
68+
i = 0;
69+
for (; i < (shiftedBigInt._bits.Length - 1); i++) {
70+
Assert.Equal(0x00000000u, shiftedBigInt._bits[i]);
71+
}
72+
73+
Assert.Equal(0x40000000u, shiftedBigInt._bits[i]);
74+
}
75+
2876
[Fact]
2977
public static void RunRightShiftTests()
3078
{

src/libraries/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,8 @@
5454
<Compile Include="BigInteger\TryWriteBytes.cs" />
5555
<Compile Include="ComplexTests.cs" />
5656
</ItemGroup>
57+
<ItemGroup>
58+
<!-- Some internal types are needed, so we reference the implementation assembly, rather than the reference assembly. -->
59+
<ProjectReference Include="..\src\System.Runtime.Numerics.csproj" SkipUseReferenceAssembly="true" />
60+
</ItemGroup>
5761
</Project>

0 commit comments

Comments
 (0)