From 8f19f4ebd511994be27a2e1f4c88424a26f91e7c Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 May 2023 13:07:17 -0400 Subject: [PATCH] Fix possible stack overflow in TotalOrderIeee754Comparer (#86593) * Fix possible stack overflow in TotalOrderIeee754Comparer * Add a test --- .../Numerics/TotalOrderIeee754Comparer.cs | 4 +- .../TotalOrderIeee754ComparerTests.cs | 242 ++++++++++++++++++ 2 files changed, 244 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/TotalOrderIeee754Comparer.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/TotalOrderIeee754Comparer.cs index d461168433634..77c33c4e04dce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/TotalOrderIeee754Comparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/TotalOrderIeee754Comparer.cs @@ -155,8 +155,8 @@ static int CompareSignificand(T x, T y) int xSignificandLength = x.GetSignificandByteCount(); int ySignificandLength = y.GetSignificandByteCount(); - Span significandX = xSignificandLength <= StackAllocThreshold ? stackalloc byte[xSignificandLength] : new byte[xSignificandLength]; - Span significandY = ySignificandLength <= StackAllocThreshold ? stackalloc byte[ySignificandLength] : new byte[ySignificandLength]; + Span significandX = (uint)xSignificandLength <= StackAllocThreshold ? stackalloc byte[xSignificandLength] : new byte[xSignificandLength]; + Span significandY = (uint)ySignificandLength <= StackAllocThreshold ? stackalloc byte[ySignificandLength] : new byte[ySignificandLength]; x.WriteSignificandBigEndian(significandX); y.WriteSignificandBigEndian(significandY); diff --git a/src/libraries/System.Runtime/tests/System/Numerics/TotalOrderIeee754ComparerTests.cs b/src/libraries/System.Runtime/tests/System/Numerics/TotalOrderIeee754ComparerTests.cs index 454a60dff8e64..019aabc2f227e 100644 --- a/src/libraries/System.Runtime/tests/System/Numerics/TotalOrderIeee754ComparerTests.cs +++ b/src/libraries/System.Runtime/tests/System/Numerics/TotalOrderIeee754ComparerTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; using Xunit; @@ -100,5 +101,246 @@ public void TotalOrderTestNFloat(float x, float y, int result) var comparer = new TotalOrderIeee754Comparer(); Assert.Equal(result, Math.Sign(comparer.Compare(x, y))); } + + [Theory] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void TotalOrderTestInvalidSignificand(int significandByteCount) + { + var comparer = new TotalOrderIeee754Comparer(); + StubFloatingPointIeee754 xy = new StubFloatingPointIeee754(1, significandByteCount); + Assert.Throws(() => comparer.Compare(xy, xy)); + } + + private readonly struct StubFloatingPointIeee754 : IFloatingPointIeee754 + { + private readonly int _significandBitLength; + private readonly int _significandByteCount; + + public StubFloatingPointIeee754(int significandBitLength, int significandByteCount) + { + _significandBitLength = significandBitLength; + _significandByteCount = significandByteCount; + } + + public static StubFloatingPointIeee754 Epsilon => default; + public static StubFloatingPointIeee754 NaN => default; + public static StubFloatingPointIeee754 NegativeInfinity => default; + public static StubFloatingPointIeee754 NegativeZero => default; + public static StubFloatingPointIeee754 PositiveInfinity => default; + public static StubFloatingPointIeee754 NegativeOne => default; + public static StubFloatingPointIeee754 E => default; + public static StubFloatingPointIeee754 Pi => default; + public static StubFloatingPointIeee754 Tau => default; + public static StubFloatingPointIeee754 One => default; + public static int Radix => default; + public static StubFloatingPointIeee754 Zero => default; + public static StubFloatingPointIeee754 AdditiveIdentity => default; + public static StubFloatingPointIeee754 MultiplicativeIdentity => default; + public static StubFloatingPointIeee754 Abs(StubFloatingPointIeee754 value) => default; + public static StubFloatingPointIeee754 Acos(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Acosh(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 AcosPi(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Asin(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Asinh(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 AsinPi(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Atan(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Atan2(StubFloatingPointIeee754 y, StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Atan2Pi(StubFloatingPointIeee754 y, StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Atanh(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 AtanPi(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 BitDecrement(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 BitIncrement(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Cbrt(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Cos(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Cosh(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 CosPi(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Exp(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Exp10(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Exp2(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 FusedMultiplyAdd(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right, StubFloatingPointIeee754 addend) => default; + public static StubFloatingPointIeee754 Hypot(StubFloatingPointIeee754 x, StubFloatingPointIeee754 y) => default; + public static StubFloatingPointIeee754 Ieee754Remainder(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => default; + public static int ILogB(StubFloatingPointIeee754 x) => default; + public static bool IsCanonical(StubFloatingPointIeee754 value) => true; + public static bool IsComplexNumber(StubFloatingPointIeee754 value) => false; + public static bool IsEvenInteger(StubFloatingPointIeee754 value) => false; + public static bool IsFinite(StubFloatingPointIeee754 value) => false; + public static bool IsImaginaryNumber(StubFloatingPointIeee754 value) => false; + public static bool IsInfinity(StubFloatingPointIeee754 value) => false; + public static bool IsInteger(StubFloatingPointIeee754 value) => false; + public static bool IsNaN(StubFloatingPointIeee754 value) => true; + public static bool IsNegative(StubFloatingPointIeee754 value) => false; + public static bool IsNegativeInfinity(StubFloatingPointIeee754 value) => false; + public static bool IsNormal(StubFloatingPointIeee754 value) => false; + public static bool IsOddInteger(StubFloatingPointIeee754 value) => false; + public static bool IsPositive(StubFloatingPointIeee754 value) => false; + public static bool IsPositiveInfinity(StubFloatingPointIeee754 value) => false; + public static bool IsRealNumber(StubFloatingPointIeee754 value) => false; + public static bool IsSubnormal(StubFloatingPointIeee754 value) => false; + public static bool IsZero(StubFloatingPointIeee754 value) => false; + public static StubFloatingPointIeee754 Log(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Log(StubFloatingPointIeee754 x, StubFloatingPointIeee754 newBase) => default; + public static StubFloatingPointIeee754 Log10(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Log2(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 MaxMagnitude(StubFloatingPointIeee754 x, StubFloatingPointIeee754 y) => default; + public static StubFloatingPointIeee754 MaxMagnitudeNumber(StubFloatingPointIeee754 x, StubFloatingPointIeee754 y) => default; + public static StubFloatingPointIeee754 MinMagnitude(StubFloatingPointIeee754 x, StubFloatingPointIeee754 y) => default; + public static StubFloatingPointIeee754 MinMagnitudeNumber(StubFloatingPointIeee754 x, StubFloatingPointIeee754 y) => default; + public static StubFloatingPointIeee754 Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) => default; + public static StubFloatingPointIeee754 Parse(string s, NumberStyles style, IFormatProvider? provider) => default; + public static StubFloatingPointIeee754 Parse(ReadOnlySpan s, IFormatProvider? provider) => default; + public static StubFloatingPointIeee754 Parse(string s, IFormatProvider? provider) => default; + public static StubFloatingPointIeee754 Pow(StubFloatingPointIeee754 x, StubFloatingPointIeee754 y) => default; + public static StubFloatingPointIeee754 RootN(StubFloatingPointIeee754 x, int n) => default; + public static StubFloatingPointIeee754 Round(StubFloatingPointIeee754 x, int digits, MidpointRounding mode) => default; + public static StubFloatingPointIeee754 ScaleB(StubFloatingPointIeee754 x, int n) => default; + public static StubFloatingPointIeee754 Sin(StubFloatingPointIeee754 x) => default; + public static (StubFloatingPointIeee754 Sin, StubFloatingPointIeee754 Cos) SinCos(StubFloatingPointIeee754 x) => default; + public static (StubFloatingPointIeee754 SinPi, StubFloatingPointIeee754 CosPi) SinCosPi(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Sinh(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 SinPi(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Sqrt(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Tan(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 Tanh(StubFloatingPointIeee754 x) => default; + public static StubFloatingPointIeee754 TanPi(StubFloatingPointIeee754 x) => default; + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out StubFloatingPointIeee754 result) + { + result = default; + return false; + } + + public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out StubFloatingPointIeee754 result) + { + result = default; + return false; + } + + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out StubFloatingPointIeee754 result) + { + result = default; + return false; + } + + public static bool TryParse(string? s, IFormatProvider? provider, out StubFloatingPointIeee754 result) + { + result = default; + return false; + } + + static bool INumberBase.TryConvertFromChecked(TOther value, out StubFloatingPointIeee754 result) + { + result = default; + return false; + } + + static bool INumberBase.TryConvertFromSaturating(TOther value, out StubFloatingPointIeee754 result) + { + result = default; + return false; + } + + static bool INumberBase.TryConvertFromTruncating(TOther value, out StubFloatingPointIeee754 result) + { + result = default; + return false; + } + + static bool INumberBase.TryConvertToChecked(StubFloatingPointIeee754 value, out TOther result) + { + result = default; + return false; + } + + static bool INumberBase.TryConvertToSaturating(StubFloatingPointIeee754 value, out TOther result) + { + result = default; + return false; + } + + static bool INumberBase.TryConvertToTruncating(StubFloatingPointIeee754 value, out TOther result) + { + result = default; + return false; + } + + public int CompareTo(object? obj) => 1; + + public int CompareTo(StubFloatingPointIeee754 other) => 1; + + public bool Equals(StubFloatingPointIeee754 other) => false; + + public int GetExponentByteCount() => 0; + + public int GetExponentShortestBitLength() => 0; + + public int GetSignificandBitLength() => _significandBitLength; + + public int GetSignificandByteCount() => _significandByteCount; + + public string ToString(string? format, IFormatProvider? formatProvider) => string.Empty; + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + charsWritten = 0; + return false; + } + + public bool TryWriteExponentBigEndian(Span destination, out int bytesWritten) + { + bytesWritten = 0; + return false; + } + + public bool TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + { + bytesWritten = 0; + return false; + } + + public bool TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + { + if (destination.Length >= _significandByteCount) + { + bytesWritten = _significandByteCount; + return true; + } + + bytesWritten = 0; + return false; + } + + public bool TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + { + if (destination.Length >= _significandByteCount) + { + bytesWritten = _significandByteCount; + return true; + } + + bytesWritten = 0; + return false; + } + + public override bool Equals(object o) => false; + public override int GetHashCode() => 0; + + public static StubFloatingPointIeee754 operator +(StubFloatingPointIeee754 value) => default; + public static StubFloatingPointIeee754 operator +(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => default; + public static StubFloatingPointIeee754 operator -(StubFloatingPointIeee754 value) => default; + public static StubFloatingPointIeee754 operator -(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => default; + public static StubFloatingPointIeee754 operator ++(StubFloatingPointIeee754 value) => default; + public static StubFloatingPointIeee754 operator --(StubFloatingPointIeee754 value) => default; + public static StubFloatingPointIeee754 operator *(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => default; + public static StubFloatingPointIeee754 operator /(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => default; + public static StubFloatingPointIeee754 operator %(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => default; + public static bool operator ==(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => false; + public static bool operator !=(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => false; + public static bool operator <(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => false; + public static bool operator >(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => false; + public static bool operator <=(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => false; + public static bool operator >=(StubFloatingPointIeee754 left, StubFloatingPointIeee754 right) => false; + } } }