Repository navigation
linux: no placeholder where no mount point can be made - #688
Merged
dylan-conway merged 4 commits intoOct 13, 2026
Merged
Conversation
denyWrite takes precedence over allowWrite however the two are nested. A deny is a read-only bind emitted after every allow bind: for a deny path inside an allowed write path, equal to one, or holding one. The deny loop's gate and its pre-pass share the predicate. An absent deny path that only write paths beneath a deny directory hold gets no mount point: nothing can create it there.
For an absent denyWrite path inside an allowed write path bubblewrap makes a mount point on the host, as this user, before the command runs. Where it cannot, no sandboxed command starts: a Windows drive's protected folder under WSL 2, or any absent deny path under /usr. mountPointFor decides what every absent deny path gets. A placeholder is left out only where the sandboxed command cannot create the path either: the parent refuses and the refusal is not this user's to lift. On any doubt it stays. On a 9p file system only the other side knows, so the mount point is made at wrap time.
Base automatically changed from
fix/linux-write-deny-above-a-write-root
to
main
October 12, 2026 14:14
With the lint comment main needs: the rule against direct realpath calls and the one call that is made on purpose met there.
This was referenced Oct 12, 2026
dylan-conway
approved these changes
Oct 13, 2026
dylan-conway
deleted the
fix/linux-no-mount-point-where-none-can-be-made
branch
October 13, 2026 20:06
ronleizrowice-ant
added a commit
that referenced
this pull request
Oct 20, 2026
Joins that needed a decision: - The placeholder planning of #688 and #705 (one decision per destination, asked of the record and not of the host; the kind settled beforehand by whether a deny lies beneath; a file above the path held in place; no mount point where none can be made) replaces this branch's upgrade in place. What this branch adds to it: an empty file a manifest names is covered like an absent path and named, and what the plan makes goes into the wrap's manifest instead of the set in memory. A held file that a manifest names is named by the look at everything the profile binds from. - The count of wraps and the forced clean-ups of #671 are kept in this branch's terms: what the count protects is the manifest and the --args profile of a command that has not started. - #673's cases on a file found at a deny path assert that a leftover stays; here a manifest says which is one. The suite is this branch's own. - #675: one return value for the plan, with the mandatory denies' share. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SEZ8LUnnuZdEgiFocuvVDm Co-authored-by: ronleizrowice <ronleizrowice@anthropic.com>
This was referenced Oct 20, 2026
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.
The fault
For an absent
denyWritepath inside an allowed write path, bubblewrap makes a mount point on the host, as this user, before the command runs. Where it cannot, no sandboxed command starts:bwrap: Can't mkdir /mnt/c/Program Files/X: Permission deniedon WSL 2 once/mnt/cis writable, or any absent deny path under/usror/etc.The change
mountPointFordecides what every absent deny path gets: nothing, a file or a directory.Invariant: a placeholder is left out only where the sandboxed command cannot create the path either. On any doubt it stays. At worst bubblewrap cannot start, as today. No deny is lost.
O_PATHdescriptor answers every question, so a running command cannot swap the path between two. Ancestors are pinned as for any deny.chmodit.Not cured: running as root, where
access()passes on a capability the sandbox lacks.Cost
Six calls per placeholder: about 0.3 ms on a 3 ms wrap with 15 of them. None where #684 already leaves the placeholder out.
Testing
/usr,/etc,/,/proc, an own 0555 directory and a read-only bind among them: none that starts on the base fails to start, 668 start here only. No deny is lost, no read or write differs, nothing is written to the host at wrap time.Also here
Three comment lines in
windows-sandbox-utils.ts. #683 brought a lint rule that names a call #686 makes on purpose, and the two met onmain, wherenpx eslint .fails. #688 and #689 both carry the same lines, so either can merge first.