runtime: SequenceReader.TryCopyMultisegment has incorrect bounds check
The problematic code: https://github.com/dotnet/corefx/blob/8292e4556eae08f497e3d8666c001b5ea75f0d3b/src/System.Memory/src/System/Buffers/SequenceReader.cs#L346-L347
It looks like it’s meant to be a guard to ensure there is enough buffer to copy the remaining from the unread span, but it’s inverted. When working with a multi-span sequence, if the provided buffer is larger than the remaining code, SequenceReader.TryCopyTo will always fail
class Program
{
static void Main(string[] args)
{
var segment = new BufferSegment<byte>(new[] { (byte)1, (byte)'A' });
segment.Append(new[] { (byte)'B'});
var sequence = new ReadOnlySequence<byte>(segment, 0, segment.Next, 1);
var sequenceReader = new SequenceReader<byte>(sequence);
sequenceReader.TryRead(out var value);
Debug.Assert(value == (byte)1);
var readBuffer = new byte[16];
var result = sequenceReader.TryCopyTo(readBuffer);
System.Console.WriteLine($"{result} {readBuffer[0]} {readBuffer[1]}");
readBuffer = new byte[2];
result = sequenceReader.TryCopyTo(readBuffer);
System.Console.WriteLine($"{result} {readBuffer[0]} {readBuffer[1]}");
}
internal class BufferSegment<T> : ReadOnlySequenceSegment<T>
{
public BufferSegment(ReadOnlyMemory<T> memory)
{
Memory = memory;
}
public BufferSegment<T> Append(ReadOnlyMemory<T> memory)
{
var segment = new BufferSegment<T>(memory)
{
RunningIndex = RunningIndex + Memory.Length
};
Next = segment;
return segment;
}
}
}
Output:
False 0 0
True 65 66
Environment: Microsoft.NETCore.App 3.0.0-preview5-27619-16
About this issue
- Original URL
- State: open
- Created 5 years ago
- Comments: 19 (19 by maintainers)
Commits related to this issue
- Undo SequenceReader.TryCopyTo changes Based on discussions in https://github.com/dotnet/corefx/issues/37207 — committed to aspnet/MessagePack-CSharp by pranavkm 5 years ago
Has to be same size or smaller.
Yes, that’s the reason. If you want everything up to a given size you need to check
Remainingfirst and slice if that is what you want.The behavior is actually to spec. The contract is that it fills the given buffer- if it can’t fill it completely it fails. I’ll work on backfilling tests where we’re missing them and try to be more explicit in the comments.
Inverting sounds great: https://github.com/neuecc/MessagePack-CSharp/pull/430/files#diff-3dc920121fdf76ba3c2b8b5e9e153e82R441