Skip to content

Commit 219419f

Browse files
committed
fix .Concat() operation don't check length overflow #93
1 parent 92fd6e6 commit 219419f

File tree

3 files changed

+60
-1
lines changed

3 files changed

+60
-1
lines changed

src/ZLinq/Linq/Concat.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public bool TryGetNonEnumeratedCount(out int count)
5656
{
5757
if (first.TryGetNonEnumeratedCount(out var count1) && second.TryGetNonEnumeratedCount(out var count2))
5858
{
59-
count = count1 + count2;
59+
count = checked(count1 + count2);
6060
return true;
6161
}
6262
count = 0;

tests/ZLinq.Tests/Linq/ConcatTest.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,27 @@ public void Concat_MultipleOperations_ShouldWorkCorrectly()
254254
}
255255

256256
#endregion
257+
258+
//[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))]
259+
[Fact]
260+
public void CountOfConcatIteratorShouldThrowExceptionOnIntegerOverflow()
261+
{
262+
var supposedlyLargeCollection = new DelegateBasedCollection<int> { CountWorker = () => int.MaxValue };
263+
var tinyCollection = new DelegateBasedCollection<int> { CountWorker = () => 1 };
264+
265+
// We need to use checked arithmetic summing up the collections' counts.
266+
Assert.Throws<OverflowException>(() => supposedlyLargeCollection.AsValueEnumerable().Concat(tinyCollection).Count());
267+
Assert.Throws<OverflowException>(() => tinyCollection.Concat(tinyCollection).Concat(supposedlyLargeCollection).Count());
268+
Assert.Throws<OverflowException>(() => tinyCollection.Concat(tinyCollection).Concat(tinyCollection).Concat(supposedlyLargeCollection).Count());
269+
270+
//// This applies to ToArray() and ToList() as well, which try to preallocate the exact size
271+
//// needed if all inputs are ICollections.
272+
Assert.Throws<OverflowException>(() => supposedlyLargeCollection.Concat(tinyCollection).ToArray());
273+
Assert.Throws<OverflowException>(() => tinyCollection.Concat(tinyCollection).Concat(supposedlyLargeCollection).ToArray());
274+
Assert.Throws<OverflowException>(() => tinyCollection.Concat(tinyCollection).Concat(tinyCollection).Concat(supposedlyLargeCollection).ToArray());
275+
276+
Assert.Throws<OverflowException>(() => supposedlyLargeCollection.Concat(tinyCollection).ToList());
277+
Assert.Throws<OverflowException>(() => tinyCollection.Concat(tinyCollection).Concat(supposedlyLargeCollection).ToList());
278+
Assert.Throws<OverflowException>(() => tinyCollection.Concat(tinyCollection).Concat(tinyCollection).Concat(supposedlyLargeCollection).ToList());
279+
}
257280
}

tests/ZLinq.Tests/TestUtil.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,39 @@ public void Dispose() { }
168168

169169
public T Current => default(T)!;
170170
}
171+
172+
public class DelegateBasedCollection<T> : ICollection<T>
173+
{
174+
public Func<int> CountWorker { get; set; }
175+
public Func<bool> IsReadOnlyWorker { get; set; }
176+
public Action<T> AddWorker { get; set; }
177+
public Action ClearWorker { get; set; }
178+
public Func<T, bool> ContainsWorker { get; set; }
179+
public Func<T, bool> RemoveWorker { get; set; }
180+
public Action<T[], int> CopyToWorker { get; set; }
181+
public Func<IEnumerator<T>> GetEnumeratorWorker { get; set; }
182+
public Func<IEnumerator> NonGenericGetEnumeratorWorker { get; set; }
183+
184+
public DelegateBasedCollection()
185+
{
186+
CountWorker = () => 0;
187+
IsReadOnlyWorker = () => false;
188+
AddWorker = item => { };
189+
ClearWorker = () => { };
190+
ContainsWorker = item => false;
191+
RemoveWorker = item => false;
192+
CopyToWorker = (array, arrayIndex) => { };
193+
GetEnumeratorWorker = () => Enumerable.Empty<T>().GetEnumerator();
194+
NonGenericGetEnumeratorWorker = () => GetEnumeratorWorker();
195+
}
196+
197+
public int Count => CountWorker();
198+
public bool IsReadOnly => IsReadOnlyWorker();
199+
public void Add(T item) => AddWorker(item);
200+
public void Clear() => ClearWorker();
201+
public bool Contains(T item) => ContainsWorker(item);
202+
public bool Remove(T item) => RemoveWorker(item);
203+
public void CopyTo(T[] array, int arrayIndex) => CopyToWorker(array, arrayIndex);
204+
public IEnumerator<T> GetEnumerator() => GetEnumeratorWorker();
205+
IEnumerator IEnumerable.GetEnumerator() => NonGenericGetEnumeratorWorker();
206+
}

0 commit comments

Comments
 (0)