Skip to content

Fix aligned buffer write #11861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 15, 2025
Merged

Fix aligned buffer write #11861

merged 7 commits into from
May 15, 2025

Conversation

333fred
Copy link
Member

@333fred 333fred commented May 14, 2025

This is a particularly subtle bug that requires a chunk (or remaining bit of a chunk) and the destination buffer being exactly the same size. When this happens, the following sequence occurs:

  1. source.Length and destination.Length are exactly aligned. This means that we do not enter the if on line 450, and endOfChunkWritten will be true.
  2. We slice destination by source.Length. This leaves us with a destination that has 0 elements, and IsEmpty is true.
  3. Because endOfChunkWritten is true, we enter the if on line 466. This increments us to the next chunk.
  4. On line 472 (in the before diff), destination.IsEmpty is true, so we enter the if, and break out of the loop, never reseting charIndex to 0.
  5. 4 happens again, this time on line 480 (in the before diff). Again, we do not reset charIndex to 0.
  6. We start reading the next chunk with the wrong offset: 38, instead of 0.

To fix this, we want to unconditionally reset charIndex when we increment a chunk, not wait until after the destination has been checked for empty.

This is a particularly subtle bug that requires a chunk (or remaining bit of a chunk) and the destination buffer being _exactly_ the same size. When this happens, the following sequence occurs:

1. `source.Length` and `destination.Length` are exactly aligned. This means that we do not enter the `if` on line 450, and `endOfChunkWritten` will be true.
2. We slice `destination` by `source.Length`. This leaves us with a `destination` that has 0 elements, and `IsEmpty` is true.
3. Because `endOfChunkWritten` is true, we enter the `if` on line 466. This increments us to the next chunk.
4. On line 472 (in the before diff), `destination.IsEmpty` is true, so we enter the `if`, and break out of the loop, never reseting `charIndex` to 0.
5. 4 happens again, this time on line 480 (in the before diff). Again, we do not reset `charIndex` to 0.

To fix this, we want to unconditionally reset `charIndex` when we increment a chunk, not wait until after the destination has been checked for empty.
@333fred 333fred requested a review from a team as a code owner May 14, 2025 22:35
Assert.Equal(output, FirstLine.AsSpan());
Array.Clear(output);

testWriter.Read(output, 0, output.Length);
Copy link
Member Author

Choose a reason for hiding this comment

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

Verified that without the charIndex change, this line fails.

}

[Fact]
public void ReaderOnlyReadsAsMuchAsRequested()
Copy link
Member Author

Choose a reason for hiding this comment

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

This issue is not directly related to the main bug, but is something that @jaredpar noticed when just looking over the code.

@333fred 333fred enabled auto-merge (squash) May 15, 2025 00:25
@333fred 333fred merged commit 368c0c0 into dotnet:main May 15, 2025
11 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone May 15, 2025
@333fred
Copy link
Member Author

333fred commented May 15, 2025

/backport to release/dev17.14

Copy link

Started backporting to release/dev17.14: https://github.com/dotnet/razor/actions/runs/15034407725

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants