Correctly check for limits in the split buffer case (#12117)

- We were looking all total consumed bytes instead of bytes consumed since the last time we parsed a form value. This resulted in thinking that the key or value was too long if it couldn't be parsed after having parsed a bunch of data in the same read.
- Added tests
This commit is contained in:
David Fowler 2019-07-12 09:46:35 -07:00 committed by GitHub
parent dfe159023a
commit 72f91b8507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 11 deletions

View File

@ -218,6 +218,7 @@ namespace Microsoft.AspNetCore.WebUtilities
{ {
var sequenceReader = new SequenceReader<byte>(buffer); var sequenceReader = new SequenceReader<byte>(buffer);
var consumed = sequenceReader.Position; var consumed = sequenceReader.Position;
var consumedBytes = default(long);
var equalsDelimiter = GetEqualsForEncoding(); var equalsDelimiter = GetEqualsForEncoding();
var andDelimiter = GetAndForEncoding(); var andDelimiter = GetAndForEncoding();
@ -227,7 +228,7 @@ namespace Microsoft.AspNetCore.WebUtilities
if (!sequenceReader.TryReadTo(out ReadOnlySequence<byte> key, equalsDelimiter, advancePastDelimiter: false) || if (!sequenceReader.TryReadTo(out ReadOnlySequence<byte> key, equalsDelimiter, advancePastDelimiter: false) ||
!sequenceReader.IsNext(equalsDelimiter, true)) !sequenceReader.IsNext(equalsDelimiter, true))
{ {
if (sequenceReader.Consumed > KeyLengthLimit) if ((sequenceReader.Consumed - consumedBytes) > KeyLengthLimit)
{ {
ThrowKeyTooLargeException(); ThrowKeyTooLargeException();
} }
@ -245,7 +246,7 @@ namespace Microsoft.AspNetCore.WebUtilities
{ {
if (!isFinalBlock) if (!isFinalBlock)
{ {
if (sequenceReader.Consumed - key.Length > ValueLengthLimit) if ((sequenceReader.Consumed - consumedBytes - key.Length) > ValueLengthLimit)
{ {
ThrowValueTooLargeException(); ThrowValueTooLargeException();
} }
@ -268,6 +269,7 @@ namespace Microsoft.AspNetCore.WebUtilities
AppendAndVerify(ref accumulator, decodedKey, decodedValue); AppendAndVerify(ref accumulator, decodedKey, decodedValue);
consumedBytes = sequenceReader.Consumed;
consumed = sequenceReader.Position; consumed = sequenceReader.Position;
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -152,15 +153,6 @@ namespace Microsoft.AspNetCore.WebUtilities
Assert.Equal(expectedValue, form[key]); Assert.Equal(expectedValue, form[key]);
} }
public static TheoryData<Encoding> Encodings =>
new TheoryData<Encoding>
{
{ Encoding.UTF8 },
{ Encoding.UTF32 },
{ Encoding.ASCII },
{ Encoding.Unicode }
};
[Theory] [Theory]
[MemberData(nameof(Encodings))] [MemberData(nameof(Encodings))]
public void TryParseFormValues_SingleSegmentWorks(Encoding encoding) public void TryParseFormValues_SingleSegmentWorks(Encoding encoding)
@ -386,6 +378,67 @@ namespace Microsoft.AspNetCore.WebUtilities
} }
} }
[Theory]
[MemberData(nameof(IncompleteFormKeys))]
public void ParseFormWithIncompleteKeyWhenIsFinalBlockSucceeds(ReadOnlySequence<byte> readOnlySequence)
{
KeyValueAccumulator accumulator = default;
var formReader = new FormPipeReader(null)
{
KeyLengthLimit = 3
};
formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false);
IDictionary<string, StringValues> values = accumulator.GetResults();
Assert.Single(values);
Assert.Contains("fo", values);
Assert.Equal("bar", values["fo"]);
}
[Theory]
[MemberData(nameof(IncompleteFormValues))]
public void ParseFormWithIncompleteValueWhenIsFinalBlockSucceeds(ReadOnlySequence<byte> readOnlySequence)
{
KeyValueAccumulator accumulator = default;
var formReader = new FormPipeReader(null)
{
ValueLengthLimit = 3
};
formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false);
IDictionary<string, StringValues> values = accumulator.GetResults();
Assert.Single(values);
Assert.Contains("fo", values);
Assert.Equal("bar", values["fo"]);
}
public static TheoryData<ReadOnlySequence<byte>> IncompleteFormKeys =>
new TheoryData<ReadOnlySequence<byte>>
{
{ ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&b"), Encoding.UTF8.GetBytes("a")) },
{ new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes("fo=bar&ba")) }
};
public static TheoryData<ReadOnlySequence<byte>> IncompleteFormValues =>
new TheoryData<ReadOnlySequence<byte>>
{
{ ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&b"), Encoding.UTF8.GetBytes("=")) },
{ new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes("fo=bar&b=")) }
};
public static TheoryData<Encoding> Encodings =>
new TheoryData<Encoding>
{
{ Encoding.UTF8 },
{ Encoding.UTF32 },
{ Encoding.ASCII },
{ Encoding.Unicode }
};
internal virtual Task<Dictionary<string, StringValues>> ReadFormAsync(FormPipeReader reader) internal virtual Task<Dictionary<string, StringValues>> ReadFormAsync(FormPipeReader reader)
{ {
return reader.ReadFormAsync(); return reader.ReadFormAsync();