windows: fix OOB read in Readlink reparse point parsing#268
windows: fix OOB read in Readlink reparse point parsing#268mohammadmseet-hue wants to merge 1 commit intogolang:masterfrom
Conversation
|
This PR (HEAD: 1f239af) has been imported to Gerrit for code review. Please visit Gerrit at https://go-review.googlesource.com/c/sys/+/760001. Important tips:
|
|
Message from Gopher Robot: Patch Set 1: (1 comment) Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
4ec36f3 to
ca3e5dd
Compare
|
Message from Mohammad Seet: Patch Set 1: (1 comment) Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Mohammad Seet: Patch Set 1: (1 comment) Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
This PR (HEAD: ca3e5dd) has been imported to Gerrit for code review. Please visit Gerrit at https://go-review.googlesource.com/c/sys/+/760001. Important tips:
|
|
Message from Mohammad Seet: Patch Set 1: Done. Updated PR description to remove markdown formatting, wrap lines at ~76 characters, and fix any issues flagged by the bot. Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Alex Brainman: Patch Set 2: (1 comment) Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Mohammad Seet: Patch Set 2: Hi Alex, fair point — it's not a heavily used code path, but even if usage is low, an OOB read in a syscall wrapper is the kind of thing that can bite someone down the line — especially since the fix is small and doesn't change behavior for valid inputs. Figured it's better to bounds-check now than leave it as a footgun for whoever touches this next. Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Alex Brainman: Patch Set 2: (1 comment) Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
Readlink on Windows uses subtraction instead of addition when computing the PrintName slice from a mount point or symlink reparse data buffer. Per the Windows REPARSE_DATA_BUFFER documentation, PrintNameOffset is the byte offset and PrintNameLength is the byte length of the print name. The correct end index is (PrintNameOffset + PrintNameLength) / 2, but the code computes (PrintNameLength - PrintNameOffset) / 2. When PrintNameOffset is non-zero (common for symlinks where the substitute name precedes the print name in PathBuffer), the subtraction produces a smaller-than-intended or negative slice index, causing either a truncated result or an out-of-bounds heap read of up to ~64KB. The fix changes the two subtraction operations to addition on both the IO_REPARSE_TAG_SYMLINK and IO_REPARSE_TAG_MOUNT_POINT code paths. Tests are added covering file symlinks, directory symlinks, and directory junctions. Change-Id: Iece12b37ea0655b6b397135a6b3959147e1dc16f
ca3e5dd to
be72080
Compare
|
Message from Mohammad Seet: Patch Set 2: Hi Alex, I ran into this while auditing the x/sys reparse point handling and cross-referencing it with the stdlib implementation. Wrote a few test cases with crafted reparse buffers to confirm the math was wrong — when PrintNameOffset > 0 (which is the normal case for symlinks where SubstituteName comes first in PathBuffer), the subtraction produces a bogus end index. On the syscall.Readlink difference — the two implementations intentionally read different fields: syscall.Readlink uses SubstituteName and then strips the \??\ prefix, while x/sys uses PrintName which is already the clean display path. That part is fine and I'm not changing it. The only thing this CL fixes is the offset arithmetic: subtraction → addition, same correction the stdlib applied to its own SubstituteName slicing. I'll add a test in the next patchset. Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
This PR (HEAD: be72080) has been imported to Gerrit for code review. Please visit Gerrit at https://go-review.googlesource.com/c/sys/+/760001. Important tips:
|
|
Message from Alex Brainman: Patch Set 3: (1 comment) Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Alex Brainman: Patch Set 3: Commit-Queue+1 TryBot-Bypass+1 Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Go LUCI: Patch Set 3: Dry run: CV is trying the patch. Bot data: {"action":"start","triggered_at":"2026-04-11T07:42:07Z","revision":"296616a37fb9d4f08780648c7f56fcb49ea98d52"} Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Alex Brainman: Patch Set 3: -Commit-Queue (Performed by <GERRIT_ACCOUNT_60063> on behalf of <GERRIT_ACCOUNT_5070>) Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Go LUCI: Patch Set 3: This CL has passed the run Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
|
Message from Go LUCI: Patch Set 3: LUCI-TryBot-Result+1 Please don’t reply on this GitHub thread. Visit golang.org/cl/760001. |
Readlink on Windows uses subtraction instead of addition
when computing the PrintName slice from a mount point or
symlink reparse data buffer.
Per the Windows REPARSE_DATA_BUFFER documentation,
PrintNameOffset is the byte offset and PrintNameLength is
the byte length of the print name. The correct end index
is (PrintNameOffset + PrintNameLength) / 2, but the code
computes (PrintNameLength - PrintNameOffset) / 2.
When PrintNameOffset is non-zero (common for symlinks where
the substitute name precedes the print name in PathBuffer),
the subtraction produces a smaller-than-intended or negative
slice index, causing either a truncated result or an
out-of-bounds heap read of up to ~64KB.
The Go standard library fixed this same bug in
internal/syscall/windows in 2016-2017 (issues #15978,
#16861), but the x/sys copy was never updated.
The fix changes the two subtraction operations to addition
on both the IO_REPARSE_TAG_SYMLINK and
IO_REPARSE_TAG_MOUNT_POINT code paths, matching the
corrected standard library implementation.