Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Fix bounds checking in PagedCharBuffer and related code #5354

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/Microsoft.AspNetCore.Mvc.TagHelpers/CacheTagHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.Encodings.Web;
Expand Down Expand Up @@ -228,10 +229,13 @@ public void WriteTo(TextWriter writer, HtmlEncoder encoder)

for (var i = 0; i < _buffer.Pages.Count; i++)
{
var pageLength = Math.Min(length, PagedCharBuffer.PageSize);
writer.Write(_buffer.Pages[i], 0, pageLength);
var page = _buffer.Pages[i];
var pageLength = Math.Min(length, page.Length);
writer.Write(page, index: 0, count: pageLength);
length -= pageLength;
}

Debug.Assert(length == 0);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
Expand Down Expand Up @@ -39,15 +40,16 @@ public override async Task FlushAsync()
for (var i = 0; i < pages.Count; i++)
{
var page = pages[i];

var pageLength = Math.Min(length, PagedCharBuffer.PageSize);
var pageLength = Math.Min(length, page.Length);
if (pageLength != 0)
{
await _inner.WriteAsync(page, 0, pageLength);
await _inner.WriteAsync(page, index: 0, count: pageLength);
}

length -= pageLength;
}

Debug.Assert(length == 0);
_charBuffer.Clear();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ public int Length
get
{
var length = _charIndex;
if (Pages.Count > 1)
for (var i = 0; i < Pages.Count - 1; i++)
{
length += PageSize * (Pages.Count - 1);
length += Pages[i].Length;
}

return length;
Expand Down Expand Up @@ -120,8 +120,7 @@ public void Clear()

private char[] GetCurrentPage()
{
if (CurrentPage == null ||
_charIndex == PageSize)
if (CurrentPage == null || _charIndex == CurrentPage.Length)
{
CurrentPage = NewPage();
_charIndex = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,66 @@ public async Task FlushAsync_ReturnsPages()
Assert.Equal(3, pool.Returned.Count);
}

[Fact]
public async Task FlushAsync_FlushesContent()
{
// Arrange
var pool = new TestArrayPool();
var inner = new StringWriter();

var writer = new PagedBufferedTextWriter(pool, inner);
for (var i = 0; i < Content.Length; i++)
{
writer.Write(Content[i]);
}

// Act
await writer.FlushAsync();

// Assert
Assert.Equal<char>(Content, inner.ToString().ToCharArray());
}

[Fact]
public async Task FlushAsync_WritesContentToInner()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks identical to the previous test. What am I missing here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could add the new assertion to the previous test. But they're really looking for unrelated things in the same scenario. Could change the previous test's name and merge the two. LMK if you feel strongly...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fine. I just wasn't sure if this was a copy-paste error

{
// Arrange
var pool = new TestArrayPool();
var inner = new StringWriter();

var writer = new PagedBufferedTextWriter(pool, inner);
for (var i = 0; i < Content.Length; i++)
{
writer.Write(Content[i]);
}

// Act
await writer.FlushAsync();

// Assert
Assert.Equal<char>(Content, inner.ToString().ToCharArray());
}

[Fact]
public async Task FlushAsync_WritesContentToInner_WithLargeArrays()
{
// Arrange
var pool = new RentMoreArrayPool();
var inner = new StringWriter();

var writer = new PagedBufferedTextWriter(pool, inner);
for (var i = 0; i < Content.Length; i++)
{
writer.Write(Content[i]);
}

// Act
await writer.FlushAsync();

// Assert
Assert.Equal<char>(Content, inner.ToString().ToCharArray());
}

private class TestArrayPool : ArrayPool<char>
{
public IList<char[]> Returned { get; } = new List<char[]>();
Expand All @@ -279,5 +339,20 @@ public override void Return(char[] buffer, bool clearArray = false)
Returned.Add(buffer);
}
}

private class RentMoreArrayPool : ArrayPool<char>
{
public IList<char[]> Returned { get; } = new List<char[]>();

public override char[] Rent(int minimumLength)
{
return new char[2 * minimumLength];
}

public override void Return(char[] buffer, bool clearArray = false)
{
Returned.Add(buffer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this just no-op?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not using Returned in new test that uses this. But seemed better to be consistent w/ the other test ArrayPool<T> here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙈

}
}
}
}
}
Loading