Skip to content

1.1.10 — apply_devfs_ruleset verb (terminal isolation)#205

Merged
click0 merged 2 commits into
mainfrom
claude/release-1.1.10
May 23, 2026
Merged

1.1.10 — apply_devfs_ruleset verb (terminal isolation)#205
click0 merged 2 commits into
mainfrom
claude/release-1.1.10

Conversation

@click0
Copy link
Copy Markdown
Owner

@click0 click0 commented May 17, 2026

Summary

Fixes silent loss of terminal isolation under rootless. lib/run.cpp:745-746's paired devfs -m <jail/dev> ruleset <N> + devfs -m <jail/dev> rule applyset calls EACCES'd for non-root operators, leaving the jail with the host's default devfs view (which exposes raw TTYs, /dev/mem, etc.) even when the spec explicitly set terminal.devfs_ruleset: 4.

This is particularly bad because devfs_ruleset is a security control. The operator configures it expecting hardening; the call fails silently; the jail runs with more device exposure than intended. Same shape as 1.1.5 (securelevel) and 1.1.6 (RCTL).

New verb: apply_devfs_ruleset

Wraps the paired set+applyset shape — both ops are meaningless without the other, so a single verb handles them atomically. Wire taxonomy grows from 26 to 27 verbs.

struct ApplyDevfsRulesetReq {
  std::string mountPath;     // absolute path to jail's /dev mount
  unsigned    ruleset = 0;   // ruleset number 1..65535
};

validateAbsolutePath on mountPath (rejects shell metas and traversals); ruleset bounded 1..65535 (devfs ruleset 0 is the implicit "no rules" default).

Call-site change

lib/run.cpp's terminal-isolation block detects the privops socket and routes through the new verb; legacy devfs(8) exec preserved for the non-privops path.

Coming next

lib/run.cpp:775-783 still has three bare devfs calls for GPU auto-unhide (devfs rule add path 'dri' unhide + siblings + applyset). Different argv shape — needs a separate AddDevfsUnhideRule verb. That's 1.1.11.

Audit chain status

Item Status
validateAnchorName allows / ✅ 1.1.2
validateJailName 64 → 200 chars ✅ 1.1.3
validateAnchorName 64 → 256 chars ✅ 1.1.4
securelevel + children.max via privops params ✅ 1.1.5
RCTL apply/cleanup via privops ✅ 1.1.6
ipfw teardown via privops ✅ 1.1.7
ipfw setup + ConfigureIpfwNat ✅ 1.1.8
cpuset via SetJailCpuset ✅ 1.1.9
devfs ruleset via ApplyDevfsRuleset this PR
GPU devfs unhide (AddDevfsUnhideRule) 1.1.11
chroot scripts, snapshot/zfs send TBD

Tests

tests/unit/privops_pure_test.cpp gains apply_devfs_ruleset_minimal covering: typical input, ruleset boundaries (1, 65535), ruleset=0 rejection, empty/relative/shell-meta path rejection. dispatch_covers_every_verb extended.

Suite grows from 1314 to 1315.

Test plan

  • FreeBSD CI lite green
  • Linux CI green
  • Manual rootless: crate run with terminal.devfs_ruleset: 4 — verify devfs -m <jail/dev> show (as root) lists ruleset 4 (was 0/default in 1.1.9)
  • Manual legacy setuid: behaviour unchanged

Generated by Claude Code

claude added 2 commits May 17, 2026 16:55
lib/run.cpp:745-746 had paired `devfs -m P ruleset N` and
`devfs -m P rule applyset` calls. Under 1.0.0+ rootless they
EACCES'd silently, leaving the jail with the host's default
devfs view — silent loss of a security control even when the
spec set terminal.devfs_ruleset.

New ApplyDevfsRuleset verb (full 7-file pattern) wraps the
paired set+applyset shape atomically. validateAbsolutePath on
mountPath, ruleset bounded 1..65535. Wire taxonomy grows
26 -> 27. lib/run.cpp's terminal-isolation block routes
through verb when socket detected; legacy devfs(8) exec
preserved.

Tests: apply_devfs_ruleset_minimal covers boundaries +
shell-meta + traversal rejection. Suite grows 1314 -> 1315.

GPU unhide (lines 775-783) still bare — needs separate verb
(AddDevfsUnhideRule), lands in 1.1.11.
User asked to combine 1.1.10 + 1.1.11 — both are devfs fixes
in lib/run.cpp and the verb pattern is the same shape. Bundle
GPU auto-unhide (lib/run.cpp:775-783) into this PR.

New verb add_devfs_unhide_rule wraps `devfs rule add path X
unhide` + applyset. Restricted to unhide action (devfs has
hide/unhide/mode/group/user; only unhide has a sensible
privops use case). Path-pattern validator allows
alnum+.+_+-+/+*; rejects leading slash + shell metas.

Wire taxonomy now 26 -> 28 (two verbs in this release instead
of one). lib/run.cpp's GUI auto-unhide block goes from 3-call
sequence (add + add + applyset) to 2 verb calls (each verb
internally includes applyset).

New tests add_devfs_unhide_rule_minimal + extended
dispatch_covers_every_verb. Suite grows 1314 -> 1316.
@click0 click0 merged commit 697850e into main May 23, 2026
2 checks passed
@click0 click0 deleted the claude/release-1.1.10 branch May 23, 2026 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants