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

Most upvoted comments

Hmm… Do you mean the destination span has to be of the exact size? It can’t be larger?

Has to be same size or smaller.

since we don’t have an out int bytesWritten (or equivalent) to let the caller know how much was actually written.

Yes, that’s the reason. If you want everything up to a given size you need to check Remaining first 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.