Skip to content

System.IO.MemoryStream: ReadByte() and WriteByte() can throw IndexOutOfRangeException #88541

@elgonzo

Description

@elgonzo

(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:


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():

if (_position >= _length)
return -1;
return _buffer[_position++];

WriteByte():

if (_position >= _length)
{
int newLength = _position + 1;
bool mustZero = _position > _length;
if (newLength >= _capacity)
{
bool allocatedNewArray = EnsureCapacity(newLength);
if (allocatedNewArray)
{
mustZero = false;
}
}
if (mustZero)
{
Array.Clear(_buffer, _length, _position - _length);
}
_length = newLength;
}
_buffer[_position++] = value;

Reproduction Steps

byte[] buffer = new byte[100];
using var ms = new System.IO.MemoryStream(buffer, 10, buffer.Length - 10, true);

//
// Let the private MemoryStream._position field overflow
//
var newPosition = int.MaxValue - 9;
ms.Position = newPosition;
System.Console.WriteLine("ms.Position == newPosition: " + (ms.Position == newPosition));

System.Console.WriteLine();
System.Console.WriteLine("ReadByte");
try
{
	var b = ms.ReadByte();
	System.Console.WriteLine("ms.ReadByte() returned " + b);
}
catch (System.Exception ex)
{
	System.Console.WriteLine(ex);
}

System.Console.WriteLine();
System.Console.WriteLine("WriteByte");
try
{
	ms.WriteByte(0);
}
catch (System.Exception ex)
{
	System.Console.WriteLine(ex);
}

Expected behavior

Expected result/output:

ms.Position == newPosition: True

ReadByte
ms.ReadByte() returned -1

WriteByte
System.NotSupportedException: Memory stream is not expandable.
   ...

Actual behavior

Actual result/output:

ms.Position == newPosition: True

ReadByte
System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.IO.MemoryStream.ReadByte()
   ...

WriteByte
System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.IO.MemoryStream.WriteByte(Byte value)
   ...

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions