Skip to content

Clear setgid when hardening Windows VM directories - #12323

Merged
dhh merged 2 commits into
omacom:quattrofrom
atoslins:fix-windows-vm-setgid
Oct 4, 2026
Merged

dhh merged 2 commits into
omacom:quattrofrom
atoslins:fix-windows-vm-setgid

Conversation

@atoslins

@atoslins atoslins commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #9334

Problem

omarchy windows vm launch exits 1 with no output whenever ~/Windows (or ~/.windows) is setgid. From the app menu nothing happens after the polkit prompt.

prepare_caller_mounts runs chmod 0700 on both sources and then requires the mode to be exactly 700. A numeric chmod keeps setuid/setgid on directories, so 2700 stays 2700 and the check fails:

$ mkdir d && chmod 2700 d && chmod 0700 d && stat -c %a d
2700

This is not an exotic setup: dockurr/windows sets the bit itself. /run/samba.sh in the container runs chmod 2777 on /shared when it is empty, which is the state of ~/Windows on a fresh install. The next launch that has to recreate the bind anchors (any reboot) then fails.

Changes

  • Harden with chmod u=rwx,go=,a-s at both sites (prepare_caller_mounts and prepare_user_mount_sources), which also drops a sticky bit. The exact-700 checks stay as they are.
  • Print a message when the mode check fails instead of returning 1 silently (separate commit).
  • Regression coverage: the root boundary test re-hardens setgid/setuid sources both behind live anchors and after the binds are recreated; the legacy migration test starts from a 2777 share, as dockur leaves it. Both fail before the fix and pass after.

Testing

  • test/shell.d/windows-vm-mount-boundary-test.sh, windows-vm-compose-test.sh, windows-vm-test.sh, bin-style-test.sh pass.
  • ./test/all: the remaining failures (config, locate, omarchy-kernel-migration, snapper, unowned-system-paths) fail identically on an untouched quattro checkout here (no omarchy-pkgs checkout, etc.).
  • Real machine (Omarchy 4.0.4): ~/Windows was 2700, launch failed silently; clearing the bit made the VM boot and RDP connect.

One note for reviewers: windows-vm-mount-boundary-test.sh fails on the host while a Windows VM is running, with or without this change. The host's live bind anchors stay listed in the test namespace's /proc/self/mountinfo, so mountpoint -q reports the fresh anchor as already mounted. I ran it in a clean pivot_root tree. Not addressed here to keep the PR focused.

Related

Canonical issue is #9334 (my #12322, plus #9630 and #11940, were closed as duplicates of it). Other open implementations of the same chmod change: #9322 (oldest, Aug 31; the maintainers' duplication check kept it and closed #10595 in its favor), #12019 and #12264 (bundled with an unrelated launcher change). What this PR adds on top of #9322: the root-boundary regressions (re-hardening behind live anchors and after the binds are recreated), the diagnostic for the silent failure, and its fault-injected test. If #9322 lands first I will rebase this into a tests-plus-diagnostic follow-up.

A numeric chmod keeps setuid/setgid on a directory, so a setgid ~/Windows
stayed 2700 and failed the exact 700 privacy check, aborting the launch.
dockur marks an initially empty /shared setgid (chmod 2777) on first boot,
so this hits fresh installs the next time the bind anchors are recreated.
@llstrk

llstrk commented Sep 17, 2026

Copy link
Copy Markdown

Automated AI review: targeted fix verified

A/B-tested d1b18d5 against 9c5482c, the baseline before both PR commits. No actionable regression found in the reviewed scope.

  • The updated compose/migration and root mount-boundary suites both fail on baseline at the setgid regressions and pass on the PR, without skips. These tests use real bind mounts in private user/mount namespaces.
  • A separate prepare_user_mount_sources probe covered initial modes 0755, 2777, 6755, 1777, and 7777. Baseline retains 2700/6700 in the affected cases; the PR produces exactly 0700 for both directories in every case. Existing file contents and the shared-directory symlink are preserved.
  • Re-hardening behind live anchors and after unmount/rebind both pass. The symbolic mode change preserves the pinned-FD approach and exact-mode checks rather than weakening them.

This verifies the targeted permission and mount behavior, not a Windows boot/RDP cycle. The new diagnostic was inspected but not fault-injection tested, and the full repository suite was not run.

@pigeon9989

Copy link
Copy Markdown

Reproduced on Omarchy 4.0.4, coreutils 9.11, kernel 7.2.5-3-omarchy.

Same conditions as described here: ~/Windows was empty, came back as 2777 after a run, and every launch after the first exited 1 with nothing but ❌ Failed to start Windows VM!. chmod g-s ~/Windows before each launch works as a stopgap (chmod 0700 and chmod 700 both leave it at 2700; only 00700 or g-s clear it).

One thing worth fixing alongside the chmod: the mode check in prepare_caller_mounts returns 1 without printing anything, so the failure gives you no string to search for and no indication of which path or check was involved. From the outside it looks like a Docker problem — the daemon is the obvious suspect when the only symptom is "the VM will not start" — and that is a long detour. An echo naming the offending directory and its mode would make this self-diagnosing even before the fix lands.

The mode check returned 1 without any output, so a launch from the app
menu died after the polkit prompt with nothing to diagnose. Name each
source directory and the mode it was left with.
@atoslins
atoslins force-pushed the fix-windows-vm-setgid branch from d1b18d5 to 45fd98f Compare September 18, 2026 10:49
@atoslins

Copy link
Copy Markdown
Contributor Author

Thanks for the repro. The second commit already printed the modes; I've updated it to name the directories too, so the failure now reads:

omarchy-windows-vm: could not make the VM data directories private: /home/alice/.windows is mode 700, /home/alice/Windows is mode 2700 (expected 700)

The boundary test now also fault-injects that path (a chmod that re-adds setgid) and asserts the message, so the diagnostic is covered rather than just inspected.

@sprajs

sprajs commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Two more open implementations of this same Samba/setgid fix are #10595 and #12019, in addition to #12264 already linked here. They change the same two hardening paths: #10595 uses 00700, while #12019 explicitly clears the special bits and adds a mode diagnostic.

Linking these for consolidation around one fix. The root-boundary and legacy-migration regressions in this PR are worth retaining whichever implementation lands.

@atoslins

Copy link
Copy Markdown
Contributor Author

Thanks for linking those, I had missed them. Agreed on consolidating: I closed my duplicate report (#12322) in favor of #9630 and pointed this PR at #9630 and #11940. No attachment to which implementation lands; if it is another one, the two regressions and the fault-injected diagnostic test here can be lifted as-is.

@Chessing234

Copy link
Copy Markdown
Contributor

hey, this is already covered in #12264 (clear setgid on harden). mind closing this as a duplicate?

@atoslins

Copy link
Copy Markdown
Contributor Author

Thanks. Before closing anything: the maintainers' duplication check already consolidated around #9322 (it closed #10595 in its favor on 09-20), and #9322 is the oldest of the four, so consolidation should go there rather than to #12264, which also bundles an unrelated launcher change. This one stays open only for what #9322 doesn't have: the root-boundary regressions and the diagnostic with its fault-injection test. If #9322 lands first I'll rebase this into a tests-only follow-up. No objection to closing whichever the maintainers pick.

@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed at 45fd98f3 and verified on a disposable Omarchy worker VM. Nothing found that needs changing.

The bug is real and this fixes its cause. A separate reproduction, written independently of the tests here, sets ~/Windows to the modes dockur leaves and runs prepare_user_mount_sources and prepare_caller_mounts in a private mount namespace. On the merge base and on current quattro, a 2777 share hardens to 2700 and a 6755 one to 6700, and prepare_caller_mounts returns 1 with nothing on stderr. On this head, 2777, 1777, 6755 and 0755 all come out 700 through both paths. Reverting only the chmod brings the failure back, now reported by the new diagnostic with both paths and modes.

Tests on the worker: windows-vm-mount-boundary-test.sh (11 passed), windows-vm-compose-test.sh (22 passed), windows-vm-test.sh and bin-style-test.sh all pass with no skips. ./test/cli is green. Against the base script, both new regressions fail: the boundary test at "root rejected setgid sources behind live anchors", and the compose test's legacy migration on the 2777 share.

Second opinion: Codex at medium effort reviewed the diff at this head and found no defects. It confirmed that u=rwx,go=,a-s produces exactly 0700 through the pinned /proc/$BASHPID/fd paths, that $LEGACY_STORAGE and $LEGACY_SHARED are always set by resolve_caller before the diagnostic is reached, and that no other site pairs a numeric chmod with an exact-mode check. That review read the same tree, so it is agreement, not independent confirmation. Its one note is about coverage, not behaviour: the compose test doesn't isolate prepare_user_mount_sources, since the privileged step clears the bits afterwards anyway. The reproduction above covers that path directly.

Competing fixes: #12019 and #12264 fix the same bug. Codex was asked separately, without our view, and also picked this one. #12019 is correct but adds no tests. #12264's chmod is equivalent, but it bundles an unrelated launcher change. #9322 and #10595 are closed.

Fixes #9334. This is now waiting on the maintainer.

This was referenced Oct 4, 2026
omarchybot added a commit to Chessing234/omarchy that referenced this pull request Oct 7, 2026
The setgid hardening it exercised landed on quattro with omacom#12323, whose compose and mount-boundary tests cover it through the root path. This one called prepare_caller_mounts itself before removal, so it could not show that removal hardens the directory, as its comment claimed. What remains is the launcher fix for omacom#7943.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Codex Medium <noreply@openai.com>
merkhanov added a commit to merkhanov/omarchy that referenced this pull request Oct 14, 2026
The only conflict was bin/omarchy-windows-vm. omacom#12323 landed the same
setgid fix on quattro using a symbolic mode (chmod u=rwx,go=,a-s) where
this branch used chmod 00700, so the branch takes quattro's version and
no longer changes that file.

Two follow-ups the text merge could not see:

- Dropped this branch's setgid test block from
  test/shell.d/windows-vm-mount-boundary-test.sh. It merged cleanly but
  tested a code change this branch no longer makes, and omacom#12323 added
  equivalent coverage to the same file.
- Updated the omarchy:summary line in bin/omarchy-toggle-bar. Quattro
  had reworded it to document the inverted behaviour ("on hides the bar,
  off shows it"), which contradicts this branch's fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ryanrhughes pushed a commit that referenced this pull request Oct 16, 2026
Clear setgid when hardening Windows VM directories

(cherry picked from commit 35a0d59)
@emirb emirb added this to the 4.0.5 milestone Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ready Good to merge verified Omarchy Triage has verified that this issue is ready for final review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows VM 4.0.2: launch fails silently after polkit auth (setgid chmod check can never pass)

8 participants