Skip to content

Conversation

@prozolic
Copy link
Contributor

#121673

LengthBuckets.CreateLengthBucketsArrayIfAppropriate caused an integer overflow when calculating bucket array size for very large strings, resulting in a negative value being passed to ArrayPool.Shared.Rent. This triggered an ArgumentOutOfRangeException when creating a FrozenDictionary with large strings approaching Array.MaxLength.

Add an overflow check to prevent creating buckets when the calculation would overflow.

LengthBuckets.CreateLengthBucketsArrayIfAppropriate caused an
integer overflow when calculating bucket array size for very large
strings, resulting in a negative value being passed to
ArrayPool<T>.Shared.Rent. This triggered an ArgumentOutOfRangeException
when creating a FrozenDictionary with large strings approaching Array.MaxLength.

Add an overflow check to prevent creating buckets when the calculation
would overflow.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes an integer overflow bug in LengthBuckets.CreateLengthBucketsArrayIfAppropriate that occurred when creating FrozenDictionaries with very large strings. The overflow happened during calculation of the bucket array size, causing a negative value to be passed to ArrayPool<T>.Shared.Rent, which resulted in an ArgumentOutOfRangeException.

Key Changes:

  • Changed intermediate array size calculation from int to long to prevent overflow
  • Added explicit cast to long for the multiplication operation before bounds checking
  • Safely cast back to int only after validating the result is within acceptable bounds

Copy link
Member

@eiriktsarpalis eiriktsarpalis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a regression test please?

@prozolic
Copy link
Contributor Author

@eiriktsarpalis I Added test case with extremely large strings that exceed length bucket boundaries to FrozenFromKnownValuesTests.StringStringData. This error has been confirmed to occur in both the ToFrozenDictionary method and the ToFrozenSet method, so adding this test case verifies both.
Could you please confirm if this is acceptable?

@eiriktsarpalis
Copy link
Member

@prozolic given the size of the input I would suggest crafting a separate unit test and additionally annotate it with [OuterLoop] attribute. This will make it less of an issue if it ends up impacting one of more resource constrained machines in CI.

@prozolic
Copy link
Contributor Author

prozolic commented Nov 18, 2025

Thank you for your advice. Is the approach of adding unit tests with the [OuterLoop] attribute to the existing classes as shown below correct?

public abstract class FrozenDictionary_Generic_Tests_string_string
{
    [Fact]
    [OuterLoop]
    public void ToFrozenDictionary_WithExtremelyLargeStrings()
    {
        // Test case with extremely large strings that exceed length bucket boundaries.
        var keys = new[] { "", new string('a', 0X7FFFFFC7 / 4) };
        var frozen = keys.ToFrozenDictionary(s => s, GetKeyIEqualityComparer());
        Assert.Equal(keys.Length, frozen.Count);
        Assert.Equal(keys.Length, frozen.Keys.Length);
        Assert.Equal(keys.Length, frozen.Values.Length);
        Assert.All(keys, key => frozen.ContainsKey(key));
    }
}

public abstract class FrozenSet_Generic_Tests_string
{
    [Fact]
    [OuterLoop]
    public void ToFrozenSet_WithExtremelyLargeStrings()
    {
        // Test case with extremely large strings that exceed length bucket boundaries.
        var keys = new[] { "", new string('a', 0X7FFFFFC7 / 4) };
        var frozen = keys.ToFrozenSet(GetIEqualityComparer());
        Assert.Equal(keys.Length, frozen.Count);
        Assert.All(keys, key => frozen.Contains(key));
    }
}

@eiriktsarpalis
Copy link
Member

@prozolic yep that looks right 👍

@prozolic
Copy link
Contributor Author

@eiriktsarpalis Thank you! I've added the tests.

Copy link
Member

@eiriktsarpalis eiriktsarpalis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@eiriktsarpalis eiriktsarpalis enabled auto-merge (squash) November 20, 2025 15:44
@eiriktsarpalis eiriktsarpalis merged commit f74d527 into dotnet:main Dec 23, 2025
83 of 85 checks passed
@prozolic prozolic deleted the LengthBuckets branch December 25, 2025 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Collections community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants