Skip to content

Fix memorystream local test failure#123709

Open
alinpahontu2912 wants to merge 4 commits intodotnet:mainfrom
alinpahontu2912:memstream_throw_fix
Open

Fix memorystream local test failure#123709
alinpahontu2912 wants to merge 4 commits intodotnet:mainfrom
alinpahontu2912:memstream_throw_fix

Conversation

@alinpahontu2912
Copy link
Member

Follow up for #123440

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

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 a test failure in MemoryStream_CapacityBoundaryChecks by adding explicit validation to throw ArgumentOutOfRangeException when setting the Capacity property to a value exceeding Array.MaxLength. Previously, attempting to set an invalid capacity would result in an OutOfMemoryException during array allocation, but the test expected ArgumentOutOfRangeException.

Changes:

  • Added validation in the Capacity setter to throw ArgumentOutOfRangeException before attempting array allocation when the capacity exceeds the maximum allowed array length

…m.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
if (value < Length)
throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
if (value > MemStreamMaxLength)
throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_Capacity);
Copy link
Member

Choose a reason for hiding this comment

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

Has this always resulted in an OOMException, even on .NET Framework? Or did it used to throw an ArgumentException for this, that regressed, and now this is just fixing the regression?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it results in an OutOfMemoryException because it tries to allocate a capacity larger than what’s possible. There wasn’t a guard before, so it would fail during the allocation rather than throwing an argument exception here.

Copy link
Member

Choose a reason for hiding this comment

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

An OOM seems appropriate to me, or at least more consistent, given that even smaller values could produce an OOMException when trying to allocate the array, and there are many other places we allocate arrays without checking against Array.MaxValue. Without a strong reason to make a breaking change here, I don't think we should.

Copy link
Member

Choose a reason for hiding this comment

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

@stephentoub Position, Seek and SetLength already do a MemStreamMaxLength check to throw AOOREx, we aligned the ctor with those in https://github.com/dotnet/runtime/pull/119089/changes#r2319364701 and this is now aligning Capacity, which we missed in the previous PRs.

Copy link
Member

Choose a reason for hiding this comment

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

Do you think we shouldn't have done that? This got in in .NET 11 so we could backpedal if we think that's appropriate.

Copy link
Member

Choose a reason for hiding this comment

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

It sounds like both @jkotas and @stephentoub are suggesting to undo the breaking change. I am also skeptical of the original motivation. Consistency is not not a priority over compatibility. If we were doing something from the start we might choose consistency, but once we've already had it working a certain way it's better to keep it the same rather than change (only for consistency's sake). If you can somehow prove that you improve reliability/usability substantially with this change, then maybe that would outweigh the compatibility impact, but you'd need to convince with data.

Copy link
Member Author

Choose a reason for hiding this comment

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

I believe it should now be okay

Copy link
Member

@jkotas jkotas Feb 10, 2026

Choose a reason for hiding this comment

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

What is our plan for the original breaking change? Are we going to revert it too? I think we should either undo the breaking change completely or fix all methods to be consistent.

Copy link
Member Author

Choose a reason for hiding this comment

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

I undid the breaking change change by removing the extra check that was added in the ctor

Copy link
Member

Choose a reason for hiding this comment

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

@@ -263,6 +263,8 @@ public virtual int Capacity
// Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity
if (value < Length)
throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
if (value > MemStreamMaxLength)
throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_Capacity);
Copy link
Member

Choose a reason for hiding this comment

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

We don't use ArgumentOutOfRange_Capacity anywhere else in MemoryStream and what we use in other places e.g. ArgumentOutOfRange_StreamLength, includes the maximum array length:

Stream length must be non-negative and less than the maximum array length {0} - origin.

Should we add a new string mentioning that?

Copy link
Member Author

Choose a reason for hiding this comment

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

Can I maybe just use a similar check to here instead of adding a new error string?

ArgumentOutOfRangeException.ThrowIfGreaterThan(capacity, MemStreamMaxLength);

Copilot AI review requested due to automatic review settings February 5, 2026 10:32
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

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

stephentoub

This comment was marked as outdated.

@stephentoub
Copy link
Member

🤖 Copilot Code Review — PR #123709

Holistic Assessment

Motivation: The PR fixes a legitimate test failure where MemoryStream_CapacityBoundaryChecks was expecting ArgumentOutOfRangeException but MemoryStream.Capacity setter actually throws OutOfMemoryException when setting capacity beyond Array.MaxLength. Since the test is marked [SkipOnCI], this failure was only observed when running locally.

Approach: The PR updates test expectations to match actual runtime behavior, which is the correct minimal fix for this test failure.

Summary: ✅ LGTM. This is a minimal, correct fix that aligns test expectations with actual runtime behavior. The test correctly verifies that an exception is thrown for invalid capacity values, and now expects the correct exception type.


Detailed Findings

✅ Correctness — Test now matches implementation

The fix is correct. Looking at MemoryStream.Capacity setter (lines 273-277):

if (value > 0)
{
    byte[] newBuffer = new byte[value];
    // ...
}

When value > Array.MaxLength, the array allocation throws OutOfMemoryException. The test expectations now correctly match this behavior.

💡 API Consistency Note (Informational, not blocking)

I note there is an existing API inconsistency in MemoryStream where other boundary validations throw ArgumentOutOfRangeException:

  • Constructor: ArgumentOutOfRangeException.ThrowIfGreaterThan(capacity, MemStreamMaxLength) (line 47)
  • SetLength: throws ArgumentOutOfRangeException when value > MemStreamMaxLength (lines 550-551)
  • Position setter: throws ArgumentOutOfRangeException when value > MemStreamMaxLength (lines 314-315)

The Capacity setter lacks this explicit check, relying instead on the array allocation failure. This is an existing inconsistency in the product, not introduced by this PR. Whether to add a bounds check to Capacity is a separate product decision (which was discussed and decided against in the review comments).

✅ Test Quality — Appropriate test structure

The test correctly validates boundary behavior. The [SkipOnCI] attribute is appropriate given the memory-intensive nature of allocating arrays near Array.MaxLength.


Summary

Verdict: ✅ LGTM

This is a minimal test-only fix. No production code changes. The test now correctly expects OutOfMemoryException, which matches the actual behavior of MemoryStream.Capacity setter when attempting to allocate an array larger than Array.MaxLength.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants