Closed lundmikkel closed 7 years ago
Looks right - could you make a pull request fixing this? Thanks
This issue is interesting. I have given it a thought and this looks in need of some rewriting. Maybe in the method that calls expand if we check that the new capacity is lower than int maximum value but greater than 1,073,741,824 (2^30) then the capacity should be increased by 100 000 and no multiplied by 2. Then if this new capacity exceeds 2,147,483,647 (2^31 - 1) then we should use an expand method that uses long instead of int. Dividing the actual method expand in two parts, check size and expand, where check size guarantees which expand to call the one with int and the one with long. This is just a thought.
A subtle bug in
ArrayBase<T>
will cause an infinite loop when adding very large enumerables to array-based collections. The bug can be triggered by the following code:The problem arises when the large array is added to the list. In order for
ArrayList<T>
, which inherits fromArrayBase<T>
, to add the very large enumerable, it must first allocate an array big enough to contain all the enumerable's items. Theexpand()
method inArrayBase<T>
is used to enlarge the underlying array:ArrayBase<T>
stores its items inarray
, whose length is always a positive power of two. The method is called with the required capacity and the collection's current count – in the example above,newcapacity
is 2^30 + 1 andnewsize
is 0. The method will take the current size of the array and double it until it is not less than the required capacity.The problem arises when we double the integer
newlength
for long enough. Whennewlength
is 2^30 and still less thannewcapacity
, it is doubled, giving an expected value of 2^31. The integer range in C#, however, is -2,147,483,648 (-2^31) to 2,147,483,647 (2^31 - 1), which causesnewlength
to overflow. Doubling the integer again makes it zero. The loop condition is now always true, sincenewlength
remains zero even when doubled.The easiest solution is probably to wrap the multiplication in
checked
:When the multiplication causes an overflow, an
OverflowException
is thrown. This does not fix the problem, but it does ensure that the problem does not go unnoticed.