From 2df8dbe684dff6ebcb728d8e4e979b358ae6e439 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:43:32 +0000 Subject: [PATCH 1/3] Initial plan From a997d43ea99157cdfa04a3118f57c4940f62918e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:48:09 +0000 Subject: [PATCH 2/3] Add checked arithmetic to AppendPrepend GetCount methods and add overflow tests Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com> --- .../src/System/Linq/AppendPrepend.SpeedOpt.cs | 8 +-- .../System.Linq/tests/AppendPrependTests.cs | 54 +++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs index d549f093260543..b59f063cbebed5 100644 --- a/src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs @@ -124,10 +124,10 @@ public override int GetCount(bool onlyIfCheap) if (_source is Iterator iterator) { int count = iterator.GetCount(onlyIfCheap); - return count == -1 ? -1 : count + 1; + return count == -1 ? -1 : checked(count + 1); } - return !onlyIfCheap || _source is ICollection ? _source.Count() + 1 : -1; + return !onlyIfCheap || _source is ICollection ? checked(_source.Count() + 1) : -1; } public override TSource? TryGetFirst(out bool found) @@ -277,10 +277,10 @@ public override int GetCount(bool onlyIfCheap) if (_source is Iterator iterator) { int count = iterator.GetCount(onlyIfCheap); - return count == -1 ? -1 : count + _appendCount + _prependCount; + return count == -1 ? -1 : checked(count + _appendCount + _prependCount); } - return !onlyIfCheap || _source is ICollection ? _source.Count() + _appendCount + _prependCount : -1; + return !onlyIfCheap || _source is ICollection ? checked(_source.Count() + _appendCount + _prependCount) : -1; } public override bool Contains(TSource value) diff --git a/src/libraries/System.Linq/tests/AppendPrependTests.cs b/src/libraries/System.Linq/tests/AppendPrependTests.cs index 3a7a2e5ed699a3..902c2c72e08a86 100644 --- a/src/libraries/System.Linq/tests/AppendPrependTests.cs +++ b/src/libraries/System.Linq/tests/AppendPrependTests.cs @@ -263,5 +263,59 @@ public void AppendPrepend_First_Last_ElementAt() Assert.Equal(84, NumberRangeGuaranteedNotCollectionType(42, 1).Append(84).ElementAt(1)); Assert.Equal(84, NumberRangeGuaranteedNotCollectionType(84, 1).Prepend(42).ElementAt(1)); } + + [Fact] + public void AppendOverflowCount() + { + // AppendPrepend1Iterator overflow when source has GetCount optimization + var source = Enumerable.Repeat(0, int.MaxValue); + var appended = source.Append(1); + Assert.Throws(() => appended.Count()); + } + + [Fact] + public void PrependOverflowCount() + { + // AppendPrepend1Iterator overflow when source has GetCount optimization + var source = Enumerable.Repeat(0, int.MaxValue); + var prepended = source.Prepend(1); + Assert.Throws(() => prepended.Count()); + } + + [Fact] + public void AppendPrependNOverflowCount() + { + // AppendPrependN overflow when source has GetCount optimization + var source = Enumerable.Repeat(0, int.MaxValue); + var result = source.Append(1).Prepend(2); + Assert.Throws(() => result.Count()); + } + + [Fact] + public void AppendOverflowCountWithICollection() + { + // AppendPrepend1Iterator overflow when source is ICollection + var source = new int[int.MaxValue]; + var appended = source.Append(1); + Assert.Throws(() => appended.Count()); + } + + [Fact] + public void PrependOverflowCountWithICollection() + { + // AppendPrepend1Iterator overflow when source is ICollection + var source = new int[int.MaxValue]; + var prepended = source.Prepend(1); + Assert.Throws(() => prepended.Count()); + } + + [Fact] + public void AppendPrependNOverflowCountWithICollection() + { + // AppendPrependN overflow when source is ICollection + var source = new int[int.MaxValue]; + var result = source.Append(1).Prepend(2); + Assert.Throws(() => result.Count()); + } } } From a59c8d6d263adc7f0bf26a8029d0880f5dd594cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:16:11 +0000 Subject: [PATCH 3/3] Fix overflow tests to use mock collection instead of allocating huge arrays Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com> --- .../System.Linq/tests/AppendPrependTests.cs | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Linq/tests/AppendPrependTests.cs b/src/libraries/System.Linq/tests/AppendPrependTests.cs index 902c2c72e08a86..d54846b2055bfa 100644 --- a/src/libraries/System.Linq/tests/AppendPrependTests.cs +++ b/src/libraries/System.Linq/tests/AppendPrependTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections; using System.Collections.Generic; using Xunit; @@ -9,6 +10,25 @@ namespace System.Linq.Tests { public class AppendPrependTests : EnumerableTests { + // Mock collection for testing overflow without allocating memory + private sealed class MockCollection : ICollection + { + private readonly int _count; + + public MockCollection(int count) => _count = count; + + public int Count => _count; + public bool IsReadOnly => true; + + public void Add(T item) => throw new NotSupportedException(); + public void Clear() => throw new NotSupportedException(); + public bool Contains(T item) => false; + public void CopyTo(T[] array, int arrayIndex) { } + public bool Remove(T item) => throw new NotSupportedException(); + public IEnumerator GetEnumerator() => Enumerable.Empty().GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + [Fact] public void SameResultsRepeatCallsIntQueryAppend() { @@ -295,7 +315,7 @@ public void AppendPrependNOverflowCount() public void AppendOverflowCountWithICollection() { // AppendPrepend1Iterator overflow when source is ICollection - var source = new int[int.MaxValue]; + var source = new MockCollection(int.MaxValue); var appended = source.Append(1); Assert.Throws(() => appended.Count()); } @@ -304,7 +324,7 @@ public void AppendOverflowCountWithICollection() public void PrependOverflowCountWithICollection() { // AppendPrepend1Iterator overflow when source is ICollection - var source = new int[int.MaxValue]; + var source = new MockCollection(int.MaxValue); var prepended = source.Prepend(1); Assert.Throws(() => prepended.Count()); } @@ -313,7 +333,7 @@ public void PrependOverflowCountWithICollection() public void AppendPrependNOverflowCountWithICollection() { // AppendPrependN overflow when source is ICollection - var source = new int[int.MaxValue]; + var source = new MockCollection(int.MaxValue); var result = source.Append(1).Prepend(2); Assert.Throws(() => result.Count()); }