Fix: update drvfs gid uid after default uid is set in oobe - #41521
Feng Wang (chemwolf6922) wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The added unit test uses a likely non-writable C:\ root path (risking flakiness) and the new refresh logic needs the proposed correctness tweaks applied before it’s safe to merge.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR addresses an OOBE-time ownership mismatch where DrvFs mounts initially come up as 0:0 and are not updated after OOBE sets the distro’s default UID, which can cause access/permission issues until restart.
Changes:
- Refreshes (remounts) auto-mounted DrvFs volumes in both elevated and non-elevated mount namespaces after successful OOBE when
oobe.defaultUidis present. - Tracks which DrvFs volumes were auto-mounted so only those are refreshed (avoids touching user-mounted shares).
- Adds/extends the
ModernOOBEunit test to validate DrvFs ownership refresh and chmod behavior post-OOBE.
File summaries
| File | Description |
|---|---|
| test/windows/UnitTests.cpp | Adds a ModernOOBE validation to ensure DrvFs mount ownership updates after default UID is set. |
| src/linux/init/init.cpp | Invokes DrvFs owner refresh after successful OOBE in utility VM mode. |
| src/linux/init/config.h | Declares ConfigRefreshDrvFsOwner(...). |
| src/linux/init/config.cpp | Tracks automounted DrvFs volumes and implements refresh logic by remounting only those volumes. |
Review details
Suppressed comments (1)
src/linux/init/config.cpp:2606
- This loop also iterates over all 32 bits and formats targets using 'a' + Index, which can produce invalid mountpoints for indices beyond Z if those bits are ever set. Limiting the loop to 26 drive letters avoids surprising behavior and matches Windows semantics.
for (size_t Index = 0; Index < SourceAutomountedVolumes.size(); Index += 1)
{
const auto Target = std::format("{}{:c}", Config.DrvFsPrefix, 'a' + Index);
if (SourceAutomountedVolumes[Index] && MountedVolumes.contains(std::make_pair(static_cast<unsigned int>(Index), Target)))
{
DestinationAutomountedVolumes.set(Index);
- Files reviewed: 4/4 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🔵 Needs a closer look
It changes low-level mount namespace and DrvFs remount behavior during OOBE, which warrants final human validation beyond automated review.
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
It changes low-level mount namespace and remount behavior in init, which is correctness-critical and should receive final human review despite the added test coverage.
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
Sylvester Kaczmarek (sylvesterkaczmarek)
left a comment
There was a problem hiding this comment.
One failure-path concern: the existing automount is detached before the replacement mount is attempted, and a failed ConfigMountDrvFsVolumes() leaves that drive untracked and unmounted while OOBE only logs the refresh failure and continues. Could a remount failure restore the previous mount/options, or keep the volume tracked for retry, so a transient error does not make the drive disappear until restart?
|
Hi Sylvester Kaczmarek (@sylvesterkaczmarek) . |
There was a problem hiding this comment.
🔵 Needs a closer look
The new post-OOBE refresh path validates defaultUid >= 0, but negative oobe.defaultUid values are still accepted by config parsing and can later be used, potentially wrapping to an unintended UID unless explicitly treated as invalid.
Review details
Suppressed comments (1)
src/linux/init/init.cpp:665
- defaultUid is parsed as a signed int (and config parsing accepts negative values), but if it’s negative we still proceed with the OOBE result flow and later ConfigureUid() uses it. This can wrap when converted to uint32_t inside ConfigureUid, producing an unintended UID. Consider explicitly treating negative oobe.defaultUid as invalid (marking it absent) before any further use.
if ((OobeResult == 0) && (defaultUidPresent == ConfigKeyPresence::Present) && (defaultUid >= 0) && UtilIsUtilityVm())
{
for (const auto Admin : {false, true})
{
if (ConfigRefreshDrvFsOwner(defaultUid, Admin, Config) < 0)
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
🟢 Approval recommended
The remount logic is scoped to auto-mounted DrvFs volumes, is gated to successful OOBE default-UID updates in WSL2, and is covered by an updated unit test.
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
Thanks, surfacing the OOBE warning is useful, and I agree that a literal rollback can hit the same failing mount operation. The remaining reliability point is narrower: |
There was a problem hiding this comment.
🔵 Needs a closer look
oobe.defaultUid can be negative (signed parse) and may wrap when used as a uint32_t uid, so it should be validated/normalized before being applied.
Review details
Suppressed comments (1)
src/linux/init/init.cpp:665
oobe.defaultUidis parsed as a signedint(the config parser accepts negative values), but it’s later passed toConfigureUid(uint32_t)and also sent back to the service asresult.DefaultUid. If the config contains a negative value (e.g.defaultUid = -1), it will wrap when converted touint32_t, potentially selecting an unintended uid and producing inconsistent behavior.
Consider treating negative defaultUid as invalid (mark the key as absent) before using it for the DrvFs refresh, returning it to the service, or calling ConfigureUid().
if ((OobeResult == 0) && (defaultUidPresent == ConfigKeyPresence::Present) && (defaultUid >= 0) && UtilIsUtilityVm())
{
for (const auto Admin : {false, true})
{
if (ConfigRefreshDrvFsOwner(defaultUid, Admin, Config) < 0)
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟡 Changes recommended
Refresh failures can leave mounts unavailable and OOBE falsely successful.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
src/linux/init/init.cpp:668
- A refresh failure is only logged here, while
OobeResultremains 0. The service treats that result as successful OOBE, clearsRunOOBE, and persists the UID, so a failed refresh is not retried and the original stale-owner problem can remain. Propagate the refresh failure into the OOBE result (or otherwise retain a retryable state) before sending the result.
if (ConfigRefreshDrvFsOwner(defaultUid, Admin, Config) < 0)
{
LOG_ERROR("Failed to refresh the {} DrvFs mount namespace after OOBE", Admin ? "elevated" : "non-elevated");
}
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
An unresolved moderate issue can leave DrvFs ownership tracking stale across the forked process path.
Review details
Suppressed comments (1)
src/linux/init/init.cpp:665
CreateProcessCommoninvokes this path from the forked relay child, so mutations tog_*AutomountedDrvFsVolumesmade byConfigRefreshDrvFsOwnerare not visible in the long-lived init process. That process still records the pre-OOBE owner and laterConfigRemountDrvFsImplcopies the stale UID/GID into the other namespace's tracking map, so a subsequent refresh can compare against incorrect ownership. Please synchronize the refreshed state back to the parent or rebuild the tracking state from the actual mount options.
if (ConfigRefreshDrvFsOwner(defaultUid, Admin, Config) < 0)
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
Summary of the Pull Request
For a newly installed distro, the drvfs are initially mount as 0:0. And is not updated after oobe sets the default uid. This could cause access issues before the distro is restarted.
This PR updates the auto mounted drvfs gid uid before completing the oobe. The non-auto mounted shares are not remounted to avoid breaking user set gid uid values.
PR Checklist
Detailed Description of the Pull Request / Additional comments
Validation Steps Performed
Update tests:
UnitTests::UnitTests::ModernOOBE