-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System.IO.MemoryStream: ReadByte() and WriteByte() can throw IndexOutOfRangeException #88541
Comments
Tagging subscribers to this area: @dotnet/area-system-io Issue DetailsDescriptionWhen creating a non-resizable MemoryStream from an array region whose start offset is larger than zero, the MemoryStream.Position value can be set in a way that leads to IndexOutOfRangeException when trying to read the stream. This is due to the assignment of the private __position field in the MemoryStream.Position set accessor:
which is vulnerable to overflows and thus can lead to _position becoming negative. Reproduction Stepsstatic void OutputPrivatePositionField(System.IO.MemoryStream stream)
{
var fi = typeof(System.IO.MemoryStream).GetField("_position", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
System.Console.WriteLine($"MemoryStream._position = {fi.GetValue(stream)}");
}
byte[] buffer = new byte[200];
for (int i=0; i < buffer.Length; ++i)
buffer[i] = (byte) i;
using var ms = new System.IOMemoryStream(buffer, 10, buffer.Length - 10, true);
//
// Let the private MemoryStream._position field overflow
//
ms.Position = int.MaxValue - 9;
OutputPrivatePositionField(ms);
var b = ms.ReadByte();
System.Console.WriteLine($"ms.ReadByte() returned {b}"); (Fiddle: https://dotnetfiddle.net/pnTEDr) Expected behaviorExpected result/output:
Actual behaviorActual result/output:
Regression?No response Known WorkaroundsNo response Configuration.NET 8 Preview 3 (https://dotnetfiddle.net/pnTEDr) Considering that the bug is already present and reproducible in the old .NET Framework 4.7.2, it's reasonable to assume that at least any .NET version following Framework 4.7.2 is also affected by this issue. Other informationNo response
|
Thanks for reporting this issue.
used in runtime/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs Lines 313 to 314 in 3110693
But this code does not take into account the _origin field!This must be fixed. It seems that the Seek method is also impacted with the same type of issue.
|
Your argument is in conflict with the documentation for MemoryStream.Position, which states (https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream.position?view=net-8.0#exceptions):
I am in no position to tell whether your argument is mistaken or the documentation is incorrect. But if the team decides that the MemoryStream.Position property should throw for positive values less than or equal to int.MaxValue, then the MemoryStream.Position documentation needs to be updated to correctly reflect the changed behavior. (To me personally, it would make more sense to keep the MemoryStream.Position behavior as is and adjust the _position checks in the ReadByte() and WriteByte() methods, as this will keep MemoryStream behavior in line with its existing documentation and doesn't require adding more special cases/conditions to the documentation as to when this or that exception is being thrown.) |
I agree that the situation is quite confusing here.
The limit of @stephentoub Can you please help here to define the expected behavior in this overflow case? |
(Edit: I updated the issue report, as not only ReadByte() is affected but WriteByte() as well).
Description
When creating a non-resizable MemoryStream from an array region whose start/origin index is larger than zero, the MemoryStream.Position value can be set in a way that leads to IndexOutOfRangeException when calling MemoryStream.ReadByte() or MemoryStream.WriteByte() .
This is due to the assignment of the private _position field in the MemoryStream.Position set accessor:
runtime/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs
Line 315 in 57608c3
which can lead to _position becoming negative.
Both the ReadByte() and WriteByte() method, however, do not test for _position being negative before using it as an index into the _buffer byte array, thus being vulnerable to causing IndexOutOfRangeException.
ReadByte():
runtime/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs
Lines 430 to 433 in 57608c3
WriteByte():
runtime/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs
Lines 736 to 754 in 57608c3
Reproduction Steps
Expected behavior
Expected result/output:
Actual behavior
Actual result/output:
Regression?
No response
Known Workarounds
No response
Configuration
.NET 8 Preview 3 (https://dotnetfiddle.net/V1POV0)
.NET Framework 4.7.2 (https://dotnetfiddle.net/gM2LkH)
Considering that the bug is already present and reproducible in the old .NET Framework 4.7.2, it's reasonable to assume that at least any .NET version following Framework 4.7.2 is also affected by this issue.
Other information
No response
The text was updated successfully, but these errors were encountered: