From 7f989df43b312a59b8d85ebcc071d067e4b48570 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 26 Sep 2026 05:25:43 +0000 Subject: [PATCH] fix: stop GetRange's range check overflowing for large counts [patch] `startIndex + count` overflowed to a negative number when count was large, so the guard passed. ContiguousCollection then tried to allocate an int.MaxValue array, and the List-backed collections threw ArgumentException instead of the documented ArgumentOutOfRangeException. Compare count against `Count - startIndex`, which cannot overflow. Fixes ktsu-dev/Containers#66 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012Hhb8knddbu4LRSs2uu9jR --- Containers.Test/ContiguousCollectionTests.cs | 23 +++++++++++++++++++ .../InsertionOrderCollectionTests.cs | 23 +++++++++++++++++++ Containers.Test/OrderedCollectionTests.cs | 10 ++++++++ Containers/ContiguousCollection.cs | 2 +- Containers/InsertionOrderCollection.cs | 2 +- Containers/OrderedCollection.cs | 2 +- 6 files changed, 59 insertions(+), 3 deletions(-) diff --git a/Containers.Test/ContiguousCollectionTests.cs b/Containers.Test/ContiguousCollectionTests.cs index c0d912c..ce3930b 100644 --- a/Containers.Test/ContiguousCollectionTests.cs +++ b/Containers.Test/ContiguousCollectionTests.cs @@ -417,6 +417,29 @@ public void GetRange_InvalidParameters_ThrowsArgumentOutOfRangeException() Assert.ThrowsExactly(() => collection.GetRange(1, 3)); } + [TestMethod] + public void GetRange_CountOverflowingStartIndex_ThrowsArgumentOutOfRangeException() + { + // Arrange + ContiguousCollection collection = [.. new int[] { 1, 2, 3 }]; + + // Act & Assert: startIndex + count overflows int + Assert.ThrowsExactly(() => collection.GetRange(1, int.MaxValue)); + } + + [TestMethod] + public void GetRange_ZeroCountAtEnd_ReturnsEmpty() + { + // Arrange + ContiguousCollection collection = [.. new int[] { 1, 2, 3 }]; + + // Act + ContiguousCollection range = collection.GetRange(collection.Count, 0); + + // Assert + Assert.IsEmpty(range); + } + [TestMethod] public void Clone_CreatesShallowCopy() { diff --git a/Containers.Test/InsertionOrderCollectionTests.cs b/Containers.Test/InsertionOrderCollectionTests.cs index ed44f64..dd26fa4 100644 --- a/Containers.Test/InsertionOrderCollectionTests.cs +++ b/Containers.Test/InsertionOrderCollectionTests.cs @@ -405,6 +405,29 @@ public void GetRange_InvalidCount_ThrowsArgumentOutOfRangeException() Assert.ThrowsExactly(() => collection.GetRange(1, 3)); } + [TestMethod] + public void GetRange_CountOverflowingStartIndex_ThrowsArgumentOutOfRangeException() + { + // Arrange + InsertionOrderCollection collection = [.. new int[] { 1, 2, 3 }]; + + // Act & Assert: startIndex + count overflows int + Assert.ThrowsExactly(() => collection.GetRange(1, int.MaxValue)); + } + + [TestMethod] + public void GetRange_ZeroCountAtEnd_ReturnsEmpty() + { + // Arrange + InsertionOrderCollection collection = [.. new int[] { 1, 2, 3 }]; + + // Act + InsertionOrderCollection range = collection.GetRange(collection.Count, 0); + + // Assert + Assert.IsEmpty(range); + } + [TestMethod] public void Clone_CreatesShallowCopy() { diff --git a/Containers.Test/OrderedCollectionTests.cs b/Containers.Test/OrderedCollectionTests.cs index d4c5065..203baf0 100644 --- a/Containers.Test/OrderedCollectionTests.cs +++ b/Containers.Test/OrderedCollectionTests.cs @@ -484,6 +484,16 @@ public void GetRange_ZeroCountAtEnd_ReturnsEmpty() Assert.AreEqual(0, range.Count); } + [TestMethod] + public void GetRange_CountOverflowingStartIndex_ThrowsArgumentOutOfRangeException() + { + // Arrange + OrderedCollection collection = new([1, 2, 3]); + + // Act & Assert: startIndex + count overflows int + Assert.ThrowsExactly(() => collection.GetRange(1, int.MaxValue)); + } + [TestMethod] public void GetRange_ZeroCountOnEmptyCollection_ReturnsEmpty() { diff --git a/Containers/ContiguousCollection.cs b/Containers/ContiguousCollection.cs index 9d0da4a..3211827 100644 --- a/Containers/ContiguousCollection.cs +++ b/Containers/ContiguousCollection.cs @@ -376,7 +376,7 @@ public ContiguousCollection GetRange(int startIndex, int count) { ArgumentOutOfRangeException.ThrowIfNegative(startIndex); ArgumentOutOfRangeException.ThrowIfNegative(count); - ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex + count, Count); + ArgumentOutOfRangeException.ThrowIfGreaterThan(count, Count - startIndex); ContiguousCollection result = new(count); Array.Copy(items, startIndex, result.items, 0, count); result.Count = count; diff --git a/Containers/InsertionOrderCollection.cs b/Containers/InsertionOrderCollection.cs index 4e28f43..80705ca 100644 --- a/Containers/InsertionOrderCollection.cs +++ b/Containers/InsertionOrderCollection.cs @@ -194,7 +194,7 @@ public InsertionOrderCollection GetRange(int startIndex, int count) { ArgumentOutOfRangeException.ThrowIfNegative(startIndex); ArgumentOutOfRangeException.ThrowIfNegative(count); - ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex + count, Count); + ArgumentOutOfRangeException.ThrowIfGreaterThan(count, Count - startIndex); return [.. items.GetRange(startIndex, count)]; } diff --git a/Containers/OrderedCollection.cs b/Containers/OrderedCollection.cs index 55d735d..dd0b19f 100644 --- a/Containers/OrderedCollection.cs +++ b/Containers/OrderedCollection.cs @@ -354,7 +354,7 @@ public OrderedCollection GetRange(int startIndex, int count) { ArgumentOutOfRangeException.ThrowIfNegative(startIndex); ArgumentOutOfRangeException.ThrowIfNegative(count); - ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex + count, Count); + ArgumentOutOfRangeException.ThrowIfGreaterThan(count, Count - startIndex); List rangeItems = items.GetRange(startIndex, count); return new OrderedCollection(rangeItems, comparer);