Fix disk-attach restore TOCTOU by impersonating the mounting user on VHD restore#40782
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens WSL’s disk attach path against a TOCTOU junction/symlink swap by resolving user-supplied disk paths under impersonation, rejecting final-component reparse points, canonicalizing via handle-based resolution, and (for VHDs) pinning the target file with an open handle during the attach window.
Changes:
- Added
WslCoreVm::ResolveDiskPath()to open + validate + canonicalize disk paths and (for VHDs) keep a non-FILE_SHARE_DELETEhandle open to pin the attach target during privileged operations. - Updated attach/detach/eject/destructor paths to perform privileged operations (grant/revoke/online/restore) against the stored canonical path (
DiskState::ResolvedPath). - Added TAEF tests covering symlink rejection and directory-junction canonicalization for VHD mounts.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/windows/service/exe/WslCoreVm.h |
Introduces DiskState::ResolvedPath and the ResolveDiskPath/ResolvedDisk API to support canonicalized, pinned disk operations. |
src/windows/service/exe/WslCoreVm.cpp |
Wires canonical path usage into attach/detach/eject/teardown and implements handle-based resolution/pinning logic. |
test/windows/MountTests.cpp |
Adds mount tests for rejecting symlink final components and allowing directory junctions via canonicalization. |
|
Thanks for the review. Addressed in 627b2b9:
|
|
Fixed the wsl2 CI failures (commit 7a60b63): Persisted path / comment 2 follow-up: Persisting the canonical resolved path broke the persisted-path contract that the mount-state tests assert (the registry Error-code expectations: Resolving the disk by handle now surfaces a missing file/path from the Verified locally (Version=2): |
|
Addressed the latest review (commit a91041f):
|
|
/azp run wsl-github-pr |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…VHD restore A standard user's live ' wsl --mount --vhd' is already safe from a junction/symlink swap: the VM access grant runs while impersonating the user, and the SYSTEM-side AddVhd only succeeds on a file the VM was granted access to, so a swap yields ACCESS_DENIED rather than disclosure. The actual gap was disk restore: when the VM is recreated, _LoadDiskMount re-attached persisted VHDs as SYSTEM (no token), re-resolving a user-controllable path and reopening the TOCTOU. Because the disk-mount state is stored under the user's SID in a volatile (per-boot) key, the disk being restored was mounted by this same user in this same boot, so we can simply pass the user token and let the existing impersonated grant close the window. Pass-through devices stay SYSTEM (elevation-gated; \\.\PhysicalDriveN has no reparse surface). This replaces the earlier handle-pinning/reparse-rejection approach, which also regressed legitimate symlinked VHDs. Add tests covering a symlinked VHD mounting and surviving a VM idle-timeout restore. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
a91041f to
9ad3a64
Compare
OneBlue
left a comment
There was a problem hiding this comment.
LGTM. Minor test comment
…re symlink creation in mount tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…VHD restore (#40782) (#40907) * Fix disk-attach restore TOCTOU by impersonating the mounting user on VHD restore A standard user's live ' wsl --mount --vhd' is already safe from a junction/symlink swap: the VM access grant runs while impersonating the user, and the SYSTEM-side AddVhd only succeeds on a file the VM was granted access to, so a swap yields ACCESS_DENIED rather than disclosure. The actual gap was disk restore: when the VM is recreated, _LoadDiskMount re-attached persisted VHDs as SYSTEM (no token), re-resolving a user-controllable path and reopening the TOCTOU. Because the disk-mount state is stored under the user's SID in a volatile (per-boot) key, the disk being restored was mounted by this same user in this same boot, so we can simply pass the user token and let the existing impersonated grant close the window. Pass-through devices stay SYSTEM (elevation-gated; \\.\PhysicalDriveN has no reparse surface). This replaces the earlier handle-pinning/reparse-rejection approach, which also regressed legitimate symlinked VHDs. Add tests covering a symlinked VHD mounting and surviving a VM idle-timeout restore. * Address PR feedback: re-query block device after VM timeout and require symlink creation in mount tests --------- (cherry picked from commit f0f4b10) Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Closes a time-of-check/time-of-use (TOCTOU) gap in disk attach where the restore path re-resolved a user-controllable VHD path as SYSTEM after the VM was recreated.
Background
For a live
wsl --mount --vhd, a junction/symlink swap is already harmless: the VM access grant runs while impersonating the user (GrantVmAccess), and the SYSTEM-sideAddVhdonly succeeds on a file the VM was granted access to. A swap therefore yieldsACCESS_DENIED, not disclosure.The real gap was disk restore: when the utility VM is torn down on idle and later recreated,
LxssUserSession::_LoadDiskMountre-attached persisted VHDs with no user token (as SYSTEM), re-resolving the user-supplied path and reopening the swap window.Fix
Disk-mount state is stored under the user's SID in a volatile (per-boot) registry key, so the disk being restored was mounted earlier in the same boot by the same user, and that user's token is already available at VM-create time. So restore simply passes the user token for VHDs and lets the existing impersonated grant close the window:
\\.\PhysicalDriveNpaths have no reparse-point surface, so there is nothing to swap.This replaces the earlier handle-pinning / reparse-point-rejection approach (per review feedback), which also regressed legitimate symlinked VHDs.
Tests
MountVhdThroughSymlinkSucceeds— a symlinked VHD mounts (regression guard).MountVhdThroughSymlinkSurvivesVmTimeout— a symlinked VHD re-attaches after a VM idle-timeout restore.