Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Fix overflow in LengthBuckets for large strings - #121674

#121674
Merged
eiriktsarpalis merged 8 commits into
dotnet:maindotnet/runtime:mainfrom
prozolic:LengthBucketsprozolic/runtime:LengthBucketsCopy head branch name to clipboard
Dec 23, 2025
Merged

Fix overflow in LengthBuckets for large strings#121674
eiriktsarpalis merged 8 commits into
dotnet:maindotnet/runtime:mainfrom
prozolic:LengthBucketsprozolic/runtime:LengthBucketsCopy head branch name to clipboard

Conversation

@prozolic

Copy link
Copy Markdown
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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

@eiriktsarpalis eiriktsarpalis left a comment

Copy link
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
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

prozolic commented Nov 18, 2025

Copy link
Copy Markdown
Contributor Author

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
Copy Markdown
Member

@prozolic yep that looks right 👍

@prozolic

Copy link
Copy Markdown
Contributor Author

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

@eiriktsarpalis eiriktsarpalis left a comment

Copy link
Copy Markdown
Member

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 subscribe to this conversation on GitHub. Already have an account? Sign in.

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.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.