Skip to content

Commit 22024bf

Browse files
authored
Merge pull request #179 from AArnott/fix178
Fix SequenceTextReader decoding of surrogate pairs at the buffer boundary
2 parents 9929f53 + e7e99f7 commit 22024bf

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

src/Nerdbank.Streams.Tests/SequenceTextReaderTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,42 @@ public void Read_VeryLarge()
221221
}
222222
while (charsRead > 0);
223223
}
224+
225+
[Theory]
226+
[CombinatorialData]
227+
public void SurrogatePairsAtBufferBoundary(bool surrogatePairsStartOnOddIndex)
228+
{
229+
// Fill a buffer large enough that it surely exceeds the internal char[] size inside the SequenceTextReader.
230+
var surrogateCharsBuffer = new char[5 * 1024];
231+
232+
// Now initialize it with surrogate character pairs.
233+
// Each pair will align at the even or odd indexes (based on test method argument) to ensure that
234+
// if there's a vulnerability in SequenceTextReader, we'll find it.
235+
int i = surrogatePairsStartOnOddIndex ? 1 : 0;
236+
for (; i < surrogateCharsBuffer.Length - 1; i += 2)
237+
{
238+
// Compose a treble clef (𝄞).
239+
surrogateCharsBuffer[i] = '\uD834';
240+
surrogateCharsBuffer[i + 1] = '\uDD1E';
241+
}
242+
243+
byte[] bytes = DefaultEncoding.GetBytes(surrogateCharsBuffer);
244+
245+
this.sequenceTextReader.Initialize(new ReadOnlySequence<byte>(bytes), DefaultEncoding);
246+
char[] actualBlock = new char[49];
247+
int totalCharsRead = 0;
248+
int charsRead;
249+
do
250+
{
251+
charsRead = this.sequenceTextReader.Read(actualBlock, 0, actualBlock.Length);
252+
for (int j = 0; j < charsRead; j++)
253+
{
254+
Assert.Equal(surrogateCharsBuffer[totalCharsRead + j], actualBlock[j]);
255+
}
256+
257+
totalCharsRead += charsRead;
258+
}
259+
while (charsRead > 0);
260+
Assert.Equal(surrogateCharsBuffer.Length, totalCharsRead);
261+
}
224262
}

src/Nerdbank.Streams/SequenceTextReader.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,9 @@ private void DecodeChars()
291291
this.charBufferLength = 0;
292292
}
293293

294-
while (this.charBufferLength < this.charBuffer.Length)
294+
// Continue to decode characters for as long as we have room for TWO chars, since Decoder.Convert throws
295+
// if we provide a char[1] for output when it encounters a surrogate pair.
296+
while (this.charBuffer.Length - this.charBufferLength >= 2)
295297
{
296298
Assumes.True(this.sequence.TryGet(ref this.sequencePosition, out ReadOnlyMemory<byte> memory, advance: false));
297299
if (memory.IsEmpty)

0 commit comments

Comments
 (0)