Enforce max frame size #2651

This commit is contained in:
Chris Ross (ASP.NET) 2018-06-07 18:40:27 -07:00
parent 810a302e66
commit a217206f1f
5 changed files with 47 additions and 20 deletions

View File

@ -497,9 +497,6 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
<data name="MultipleCertificateSources" xml:space="preserve">
<value>The endpoint {endpointName} specified multiple certificate sources.</value>
</data>
<data name="Http2NotSupported" xml:space="preserve">
<value>HTTP/2 support is experimental, see https://go.microsoft.com/fwlink/?linkid=866785 to enable it.</value>
</data>
<data name="WritingToResponseBodyAfterResponseCompleted" xml:space="preserve">
<value>Cannot write to the response body, the response has completed.</value>
</data>
@ -518,4 +515,7 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
<data name="ConnectionTimedOutByServer" xml:space="preserve">
<value>The connection was timed out by the server.</value>
</data>
<data name="Http2ErrorFrameOverLimit" xml:space="preserve">
<value>The received frame size of {size} exceeds the limit {limit}.</value>
</data>
</root>

View File

@ -157,7 +157,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
if (!readableBuffer.IsEmpty)
{
if (Http2FrameReader.ReadFrame(readableBuffer, _incomingFrame, out consumed, out examined))
if (Http2FrameReader.ReadFrame(readableBuffer, _incomingFrame, _serverSettings.MaxFrameSize, out consumed, out examined))
{
Log.LogTrace($"Connection id {ConnectionId} received {_incomingFrame.Type} frame with flags 0x{_incomingFrame.Flags:x} and length {_incomingFrame.Length} for stream ID {_incomingFrame.StreamId}");
await ProcessFrameAsync<TContext>(application);

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
public static class Http2FrameReader
{
public static bool ReadFrame(ReadOnlySequence<byte> readableBuffer, Http2Frame frame, out SequencePosition consumed, out SequencePosition examined)
public static bool ReadFrame(ReadOnlySequence<byte> readableBuffer, Http2Frame frame, uint maxFrameSize, out SequencePosition consumed, out SequencePosition examined)
{
consumed = readableBuffer.Start;
examined = readableBuffer.End;
@ -22,6 +22,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
var headerSlice = readableBuffer.Slice(0, Http2Frame.HeaderLength);
headerSlice.CopyTo(frame.Raw);
if (frame.Length > maxFrameSize)
{
throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorFrameOverLimit(frame.Length, maxFrameSize), Http2ErrorCode.FRAME_SIZE_ERROR);
}
if (readableBuffer.Length < Http2Frame.HeaderLength + frame.Length)
{
return false;

View File

@ -1778,20 +1778,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
internal static string FormatMultipleCertificateSources(object endpointName)
=> string.Format(CultureInfo.CurrentCulture, GetString("MultipleCertificateSources", "endpointName"), endpointName);
/// <summary>
/// HTTP/2 support is experimental, see https://go.microsoft.com/fwlink/?linkid=866785 to enable it.
/// </summary>
internal static string Http2NotSupported
{
get => GetString("Http2NotSupported");
}
/// <summary>
/// HTTP/2 support is experimental, see https://go.microsoft.com/fwlink/?linkid=866785 to enable it.
/// </summary>
internal static string FormatHttp2NotSupported()
=> GetString("Http2NotSupported");
/// <summary>
/// Cannot write to the response body, the response has completed.
/// </summary>
@ -1876,6 +1862,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
internal static string FormatConnectionTimedOutByServer()
=> GetString("ConnectionTimedOutByServer");
/// <summary>
/// The received frame size of {size} exceeds the limit {limit}.
/// </summary>
internal static string Http2ErrorFrameOverLimit
{
get => GetString("Http2ErrorFrameOverLimit");
}
/// <summary>
/// The received frame size of {size} exceeds the limit {limit}.
/// </summary>
internal static string FormatHttp2ErrorFrameOverLimit(object size, object limit)
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorFrameOverLimit", "size", "limit"), size, limit);
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -299,6 +299,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetAsciiStringNonNullCharacters();
}
[Fact]
public async Task Frame_Received_OverMaxSize_FrameError()
{
await InitializeConnectionAsync(_echoApplication);
await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
// Manually craft a frame where the size is too large. Our own frame class won't allow this.
// See Http2Frame.Length
var length = Http2Frame.MinAllowedMaxFrameSize + 1; // Too big
var frame = new byte[9 + length];
frame[0] = (byte)((length & 0x00ff0000) >> 16);
frame[1] = (byte)((length & 0x0000ff00) >> 8);
frame[2] = (byte)(length & 0x000000ff);
await SendAsync(frame);
await WaitForConnectionErrorAsync<Http2ConnectionErrorException>(
ignoreNonGoAwayFrames: true,
expectedLastStreamId: 1,
expectedErrorCode: Http2ErrorCode.FRAME_SIZE_ERROR,
expectedErrorMessage: CoreStrings.FormatHttp2ErrorFrameOverLimit(length, Http2Frame.MinAllowedMaxFrameSize));
}
[Fact]
public async Task DATA_Received_ReadByStream()
{
@ -2794,7 +2816,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
Assert.True(buffer.Length > 0);
if (Http2FrameReader.ReadFrame(buffer, frame, out consumed, out examined))
if (Http2FrameReader.ReadFrame(buffer, frame, 16_384, out consumed, out examined))
{
return frame;
}