Skip to content

Commit

Permalink
Merged PR 38104: Fix some issues around accessing invalid blocks for …
Browse files Browse the repository at this point in the history
…Number.BigInteger

Resolve MSRC 68613 for .NET 8
  • Loading branch information
tannergooding authored and krwq committed Apr 11, 2024
2 parents a567ee7 + aa04622 commit 173b4b8
Showing 1 changed file with 106 additions and 14 deletions.
120 changes: 106 additions & 14 deletions src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ internal unsafe ref struct BigInteger
private const int MaxBits = BitsForLongestBinaryMantissa + BitsForLongestDigitSequence + BitsPerBlock;

private const int BitsPerBlock = sizeof(int) * 8;
private const int MaxBlockCount = (MaxBits + (BitsPerBlock - 1)) / BitsPerBlock;

// We need one extra block to make our shift left algorithm significantly simpler
private const int MaxBlockCount = ((MaxBits + (BitsPerBlock - 1)) / BitsPerBlock) + 1;

private static ReadOnlySpan<uint> Pow10UInt32Table => new uint[]
{
Expand Down Expand Up @@ -302,7 +304,8 @@ internal unsafe ref struct BigInteger
0xD9D61A05,
0x00000325,

// 9 Trailing blocks to ensure MaxBlockCount
// 10 Trailing blocks to ensure MaxBlockCount
0x00000000,
0x00000000,
0x00000000,
0x00000000,
Expand Down Expand Up @@ -358,11 +361,24 @@ public static void Add(scoped ref BigInteger lhs, scoped ref BigInteger rhs, out
resultIndex++;
}

int resultLength = largeLength;

// If there's still a carry, append a new block
if (carry != 0)
{
Debug.Assert(carry == 1);
Debug.Assert((resultIndex == largeLength) && (largeLength < MaxBlockCount));
Debug.Assert(resultIndex == resultLength);
Debug.Assert(unchecked((uint)(resultLength)) < MaxBlockCount);

if (unchecked((uint)(resultLength)) >= MaxBlockCount)
{
// We shouldn't reach here, and the above assert will help flag this
// during testing, but we'll ensure that we return a safe value of
// zero in the case we end up overflowing in any way.

SetZero(out result);
return;
}

result._blocks[resultIndex] = 1;
result._length++;
Expand Down Expand Up @@ -733,16 +749,27 @@ public static void Multiply(scoped ref BigInteger lhs, uint value, out BigIntege
index++;
}

int resultLength = lhsLength;

if (carry != 0)
{
Debug.Assert(unchecked((uint)(lhsLength)) + 1 <= MaxBlockCount);
Debug.Assert(unchecked((uint)(resultLength)) < MaxBlockCount);

if (unchecked((uint)(resultLength)) >= MaxBlockCount)
{
// We shouldn't reach here, and the above assert will help flag this
// during testing, but we'll ensure that we return a safe value of
// zero in the case we end up overflowing in any way.

SetZero(out result);
return;
}

result._blocks[index] = carry;
result._length = (lhsLength + 1);
}
else
{
result._length = lhsLength;
resultLength += 1;
}

result._length = resultLength;
}

public static void Multiply(scoped ref BigInteger lhs, scoped ref BigInteger rhs, out BigInteger result)
Expand Down Expand Up @@ -777,6 +804,16 @@ public static void Multiply(scoped ref BigInteger lhs, scoped ref BigInteger rhs
int maxResultLength = smallLength + largeLength;
Debug.Assert(unchecked((uint)(maxResultLength)) <= MaxBlockCount);

if (unchecked((uint)(maxResultLength)) > MaxBlockCount)
{
// We shouldn't reach here, and the above assert will help flag this
// during testing, but we'll ensure that we return a safe value of
// zero in the case we end up overflowing in any way.

SetZero(out result);
return;
}

// Zero out result internal blocks.
result._length = maxResultLength;
result.Clear((uint)maxResultLength);
Expand Down Expand Up @@ -822,7 +859,19 @@ public static void Pow2(uint exponent, out BigInteger result)
{
uint blocksToShift = DivRem32(exponent, out uint remainingBitsToShift);
result._length = (int)blocksToShift + 1;

Debug.Assert(unchecked((uint)result._length) <= MaxBlockCount);

if (unchecked((uint)result._length) > MaxBlockCount)
{
// We shouldn't reach here, and the above assert will help flag this
// during testing, but we'll ensure that we return a safe value of
// zero in the case we end up overflowing in any way.

SetZero(out result);
return;
}

if (blocksToShift > 0)
{
result.Clear(blocksToShift);
Expand Down Expand Up @@ -1012,7 +1061,18 @@ public void Add(uint value)
}
}

Debug.Assert(unchecked((uint)(length)) + 1 <= MaxBlockCount);
Debug.Assert(unchecked((uint)(length)) < MaxBlockCount);

if (unchecked((uint)(length)) >= MaxBlockCount)
{
// We shouldn't reach here, and the above assert will help flag this
// during testing, but we'll ensure that we return a safe value of
// zero in the case we end up overflowing in any way.

SetZero(out this);
return;
}

_blocks[length] = 1;
_length = length + 1;
}
Expand Down Expand Up @@ -1074,9 +1134,20 @@ public void Multiply10()

if (carry != 0)
{
Debug.Assert(unchecked((uint)(_length)) + 1 <= MaxBlockCount);
Debug.Assert(unchecked((uint)(length)) < MaxBlockCount);

if (unchecked((uint)(length)) >= MaxBlockCount)
{
// We shouldn't reach here, and the above assert will help flag this
// during testing, but we'll ensure that we return a safe value of
// zero in the case we end up overflowing in any way.

SetZero(out this);
return;
}

_blocks[index] = (uint)carry;
_length++;
_length = length + 1;
}
}

Expand Down Expand Up @@ -1152,7 +1223,17 @@ public void ShiftLeft(uint shift)
// Check if the shift is block aligned
if (remainingBitsToShift == 0)
{
Debug.Assert(writeIndex < MaxBlockCount);
Debug.Assert(unchecked((uint)(length)) < MaxBlockCount);

if (unchecked((uint)(length)) >= MaxBlockCount)
{
// We shouldn't reach here, and the above assert will help flag this
// during testing, but we'll ensure that we return a safe value of
// zero in the case we end up overflowing in any way.

SetZero(out this);
return;
}

while (readIndex >= 0)
{
Expand All @@ -1169,8 +1250,19 @@ public void ShiftLeft(uint shift)
else
{
// We need an extra block for the partial shift

writeIndex++;
Debug.Assert(writeIndex < MaxBlockCount);
Debug.Assert(unchecked((uint)(length)) < MaxBlockCount);

if (unchecked((uint)(length)) >= MaxBlockCount)
{
// We shouldn't reach here, and the above assert will help flag this
// during testing, but we'll ensure that we return a safe value of
// zero in the case we end up overflowing in any way.

SetZero(out this);
return;
}

// Set the length to hold the shifted blocks
_length = writeIndex + 1;
Expand Down

0 comments on commit 173b4b8

Please sign in to comment.