Skip to content

Commit 0d426df

Browse files
authored
Fix frame boundary detection in SslStream (#104606)
1 parent 8ba8249 commit 0d426df

File tree

2 files changed

+25
-4
lines changed

2 files changed

+25
-4
lines changed

src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ private ProtocolToken ProcessTlsFrame(int frameSize)
469469
{
470470
TlsFrameHeader nextHeader = default;
471471

472-
if (!TlsFrameHelper.TryGetFrameHeader(_buffer.EncryptedReadOnlySpan.Slice(chunkSize), ref nextHeader))
472+
if (!TlsFrameHelper.TryGetFrameHeader(availableData.Slice(chunkSize), ref nextHeader))
473473
{
474474
break;
475475
}
@@ -478,7 +478,7 @@ private ProtocolToken ProcessTlsFrame(int frameSize)
478478

479479
// Can process more handshake frames in single step or during TLS1.3 post-handshake auth, but we should
480480
// avoid processing too much so as to preserve API boundary between handshake and I/O.
481-
if ((nextHeader.Type != TlsContentType.Handshake && nextHeader.Type != TlsContentType.ChangeCipherSpec) && !_isRenego || frameSize > _buffer.EncryptedLength)
481+
if ((nextHeader.Type != TlsContentType.Handshake && nextHeader.Type != TlsContentType.ChangeCipherSpec) && !_isRenego || frameSize > availableData.Length - chunkSize)
482482
{
483483
// We don't have full frame left or we already have app data which needs to be processed by decrypt.
484484
break;

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ public enum FramingType
3939
// 1 byte reads
4040
ByteByByte,
4141

42-
// coalesce reads to biggest chunks possible
42+
// Receive data at chunks, not necessarily respecting frame boundaries
43+
Chunked,
44+
45+
// Coalesce reads to biggest chunks possible
4346
Coalescing
4447
}
4548

@@ -129,7 +132,6 @@ public async Task Handshake_Success(FramingType framingType, SslProtocols sslPro
129132
Assert.True(serverStream.ReadCalled, "Mocked read method was not used");
130133

131134
await TestHelper.PingPong(client, server);
132-
133135
}
134136

135137
internal class ConfigurableReadStream : Stream
@@ -168,6 +170,7 @@ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, Cancellation
168170
{
169171
case FramingType.ByteByByte:
170172
return await _stream.ReadAsync(buffer.Length > 0 ? buffer.Slice(0, 1) : buffer, cancellationToken);
173+
171174
case FramingType.Coalescing:
172175
{
173176
if (buffer.Length > 0)
@@ -178,6 +181,24 @@ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, Cancellation
178181
}
179182
return await _stream.ReadAsync(buffer, cancellationToken);
180183
}
184+
case FramingType.Chunked:
185+
{
186+
if (buffer.Length > 0)
187+
{
188+
// wait 10ms, this should be enough for the other side to write as much data
189+
// as it will ever write before receiving something back.
190+
await Task.Delay(10);
191+
192+
const int maxRead = 1519; // arbitrarily chosen chunk size
193+
194+
if (buffer.Length > maxRead)
195+
{
196+
buffer = buffer.Slice(0, maxRead);
197+
}
198+
}
199+
return await _stream.ReadAsync(buffer, cancellationToken);
200+
}
201+
181202
default:
182203
throw new NotImplementedException();
183204
}

0 commit comments

Comments
 (0)