Repository navigation
Conversation
TryGetReparseDataBuffer ignored the byte count returned by DeviceIoControl and read a SymbolicLinkReparseBuffer from the whole rented ArrayPool buffer. Pooled arrays are not zeroed, and the span was larger than the actual response, so MemoryMarshal.TryRead always succeeded: when the driver returned fewer bytes than the struct size, the remaining fields contained stale data from whatever previously used the array. The method also interpreted SymbolicLinkReparseBuffer for every reparse tag, although its fields (including Flags) are only valid for IO_REPARSE_TAG_SYMLINK; for mount points, the same bytes belong to a different layout. Capture lpBytesReturned and parse only the returned prefix of the buffer, so a response shorter than the common header is rejected instead of reading stale memory. Clear the header area of the pooled array before the call as well. Replace the symlink-specific struct with a ReparseDataBufferHeader containing only the fields common to all reparse tags; tag-specific data must be parsed per tag after checking ReparseTag. Return the rented buffer to the pool from a finally block on every path. IsJunction still reads only ReparseTag, so its behavior is unchanged. Added tests for empty and too-short buffers, header parsing, ignoring tag-specific bytes, and a non-reparse-point path. Signed-off-by: bfs2021 <101915083+bfs2021@users.noreply.github.com>
ForNeVeR
self-requested a review
October 21, 2026 09:11
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes #242.
DiskUtils.TryGetReparseDataBufferrents a 16 KiB buffer fromArrayPool<byte>.Shared, callsDeviceIoControl(FSCTL_GET_REPARSE_POINT), then reads aSymbolicLinkReparseBufferout of the whole array. The driver-reported byte count is discarded, and pooled arrays are not zeroed, soMemoryMarshal.TryReadalways succeeds. When the driver returns fewer bytes than the struct size, the remaining fields are leftovers from whatever previously used the pooled array. The method also interprets the symlink-specific layout for every reparse tag, although fields such asFlagsonly exist forIO_REPARSE_TAG_SYMLINK.Root cause
lpBytesReturnedwas passed asout _and ignored.SymbolicLinkReparseBuffer.Fix
lpBytesReturned, validate it against the rented array size, and parse onlybuffer[0..bytesReturned]. A response shorter than the common header now returnsnull.SymbolicLinkReparseBufferwithReparseDataBufferHeader(ReparseTag,ReparseDataLength,Reserved), the part common to all reparse tags. Tag-specific data is never interpreted here.finallyblock on every path.IsJunctionis unchanged and still only comparesReparseTag.Testing
TruePath.Tests/DiskUtilsTests.cs: empty and too-short buffers returnnull; header fields are parsed correctly; tag-specific bytes after the header are ignored; a real non-reparse directory returnsnull. No elevated privileges needed.dotnet build TruePath.slnanddotnet build TruePath.sln -p:CheckXmlDoc=true --no-incremental: clean.dotnet test TruePath.Tests: 477 passed, 1 pre-existing failure (AbsolutePathTests.ReadKind_IsSymlink, which needs symlink-creation privilege; verified identical without this change).dotnet test ... -- --filter-class TruePath.Tests.DiskUtilsTests: 10/10 passed.