Fix memorystream local test failure#123709
Fix memorystream local test failure#123709alinpahontu2912 wants to merge 4 commits intodotnet:mainfrom
Conversation
|
Tagging subscribers to this area: @dotnet/area-system-io |
There was a problem hiding this comment.
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
Capacitysetter to throwArgumentOutOfRangeExceptionbefore attempting array allocation when the capacity exceeds the maximum allowed array length
src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs
Outdated
Show resolved
Hide resolved
…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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I believe it should now be okay
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I undid the breaking change change by removing the extra check that was added in the ctor
There was a problem hiding this comment.
Is it just ctor? https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/11/memorystream-max-capacity#affected-apis lists a few methods too.
Also, some tests are failing.
| @@ -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); | |||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Can I maybe just use a similar check to here instead of adding a new error string?
🤖 Copilot Code Review — PR #123709Holistic AssessmentMotivation: The PR fixes a legitimate test failure where 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 implementationThe fix is correct. Looking at if (value > 0)
{
byte[] newBuffer = new byte[value];
// ...
}When 💡 API Consistency Note (Informational, not blocking)I note there is an existing API inconsistency in
The ✅ Test Quality — Appropriate test structureThe test correctly validates boundary behavior. The SummaryVerdict: ✅ LGTM This is a minimal test-only fix. No production code changes. The test now correctly expects |
Follow up for #123440