From f69fc98fe9923ae9bf697f7e7ef800968bea630e Mon Sep 17 00:00:00 2001 From: asad24-dev Date: Wed, 16 Sep 2026 23:48:47 +0100 Subject: [PATCH 1/2] Fix TimeSpan constructor overflow Accumulate day-based constructor inputs with widened arithmetic before\nrange checking so oversized durations cannot wrap.\n\nAdd regression coverage for the affected overloads.\n\nFix #134109 --- .../System.Private.CoreLib/src/System/TimeSpan.cs | 14 +++++++------- .../System.Runtime.Tests/System/TimeSpanTests.cs | 10 ++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs index 861a8b8eac0836..624651c79c8219 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs @@ -291,18 +291,18 @@ public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) /// public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds) { - long totalMicroseconds = (days * MicrosecondsPerDay) - + (hours * MicrosecondsPerHour) - + (minutes * MicrosecondsPerMinute) - + (seconds * MicrosecondsPerSecond) - + (milliseconds * MicrosecondsPerMillisecond) - + microseconds; + Int128 totalMicroseconds = Math.BigMul(days, MicrosecondsPerDay) + + Math.BigMul(hours, MicrosecondsPerHour) + + Math.BigMul(minutes, MicrosecondsPerMinute) + + Math.BigMul(seconds, MicrosecondsPerSecond) + + Math.BigMul(milliseconds, MicrosecondsPerMillisecond) + + microseconds; if ((totalMicroseconds > MaxMicroseconds) || (totalMicroseconds < MinMicroseconds)) { ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong(); } - _ticks = totalMicroseconds * TicksPerMicrosecond; + _ticks = (long)totalMicroseconds * TicksPerMicrosecond; } public long Ticks => _ticks; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs index 4b619a1f48e80f..2c8eb5893f2f96 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs @@ -111,6 +111,16 @@ public static void Ctor_Int_Int_Int_Int_Int_Int_Invalid() AssertExtensions.Throws(null, () => new TimeSpan(max.Days, max.Hours, max.Minutes, max.Seconds, max.Milliseconds, max.Microseconds + 1)); } + [Fact] + public static void Ctor_DayBased_IntermediateMicrosecondOverflow() + { + AssertExtensions.Throws(null, () => new TimeSpan(213_503_983, 0, 0, 0)); + AssertExtensions.Throws(null, () => new TimeSpan(-213_503_983, 0, 0, 0)); + AssertExtensions.Throws(null, () => new TimeSpan(213_503_982, 8, 1, 49, 551, 616)); + AssertExtensions.Throws(null, () => new TimeSpan(213_503_983, 0, 0, 0, 0)); + AssertExtensions.Throws(null, () => new TimeSpan(213_503_983, 0, 0, 0, 0, 0)); + } + [Theory] [InlineData(100)] [InlineData(300)] From c027f207ceb9c02b12abb405d5938f6d6e8ffe1e Mon Sep 17 00:00:00 2001 From: asad24-dev Date: Thu, 17 Sep 2026 00:52:25 +0100 Subject: [PATCH 2/2] Improve TimeSpan overflow coverage Report overflow regressions independently and cover valid day-based boundaries.\n\nFix #134109 --- .../System/TimeSpanTests.cs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs index 2c8eb5893f2f96..e66e910f38cabc 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs @@ -111,14 +111,30 @@ public static void Ctor_Int_Int_Int_Int_Int_Int_Invalid() AssertExtensions.Throws(null, () => new TimeSpan(max.Days, max.Hours, max.Minutes, max.Seconds, max.Milliseconds, max.Microseconds + 1)); } + [Theory] + [InlineData(4, 213_503_983, 0, 0, 0, 0, 0)] + [InlineData(4, -213_503_983, 0, 0, 0, 0, 0)] + [InlineData(6, 213_503_982, 8, 1, 49, 551, 616)] + [InlineData(5, 213_503_983, 0, 0, 0, 0, 0)] + [InlineData(6, 213_503_983, 0, 0, 0, 0, 0)] + public static void Ctor_DayBased_Overflow_Invalid(int argumentCount, int days, int hours, int minutes, int seconds, int milliseconds, int microseconds) + { + Action action = argumentCount switch + { + 4 => () => new TimeSpan(days, hours, minutes, seconds), + 5 => () => new TimeSpan(days, hours, minutes, seconds, milliseconds), + 6 => () => new TimeSpan(days, hours, minutes, seconds, milliseconds, microseconds), + _ => throw new ArgumentOutOfRangeException(nameof(argumentCount)), + }; + + AssertExtensions.Throws(null, action); + } + [Fact] - public static void Ctor_DayBased_IntermediateMicrosecondOverflow() + public static void Ctor_DayBased_Boundary_Valid() { - AssertExtensions.Throws(null, () => new TimeSpan(213_503_983, 0, 0, 0)); - AssertExtensions.Throws(null, () => new TimeSpan(-213_503_983, 0, 0, 0)); - AssertExtensions.Throws(null, () => new TimeSpan(213_503_982, 8, 1, 49, 551, 616)); - AssertExtensions.Throws(null, () => new TimeSpan(213_503_983, 0, 0, 0, 0)); - AssertExtensions.Throws(null, () => new TimeSpan(213_503_983, 0, 0, 0, 0, 0)); + Assert.Equal(TimeSpan.FromDays(TimeSpan.MaxValue.Days), new TimeSpan(TimeSpan.MaxValue.Days, 0, 0, 0)); + Assert.Equal(TimeSpan.FromDays(TimeSpan.MinValue.Days), new TimeSpan(TimeSpan.MinValue.Days, 0, 0, 0)); } [Theory]