Skip to content

learn: decide the write tier by the granted path - #231

Merged
congwang-mk merged 1 commit into
mainfrom
learn-protected-tier-consistency
Sep 20, 2026
Merged

congwang-mk merged 1 commit into
mainfrom
learn-protected-tier-consistency

Conversation

@congwang-mk

Copy link
Copy Markdown
Contributor

Fixes #217.

Problem

collapse_write_paths had two branches that disagreed about tiers:

  • A path that exists was recorded with a NOTE, whatever its tier. Syscalls such as mkdirat, unlinkat, renameat2, symlinkat and linkat record the parent directory, so mkdir /root/foo landed here and got a recursive write grant on /root.
  • A path that does not exist was walked up to its nearest existing ancestor, and only that ancestor was checked against the tiers. open("/root/foo/bar", O_CREAT) reached /root and was skipped with an error.

A Landlock grant on a directory is recursive either way, so the same effective grant had two outcomes. The credential suffixes (.ssh, .aws, .kube, .gnupg) had the same split, and Guarded paths had a milder one (NOTE on one route, warning plus sibling diff on the other).

Fix

Resolve the granted path first (the path itself, or its nearest existing ancestor), then apply the tier check once. Both routes now skip Protected and emit the warning plus the unobserved-siblings diff on Guarded.

/root moves from Protected to Guarded

Looking at why /root was Protected at all, the special case does not hold up:

  • For a non-root caller /root is 0700 and not writable, so the rule protects nothing.
  • For a root caller (containers, CI) /root is simply $HOME, where dotfile writes are as legitimate as in /home/user, which is already Guarded for that reason. The rule only bit in that case, leaving learned profiles silently incomplete.
  • The home != "/root" check in classify_path was unreachable, because the Protected match returned first.

/root is listed in GUARDED_PATHS unconditionally rather than relying on the $HOME match. Learn records an open before the kernel denies it, so a non-root workload that pokes at /root would otherwise get a Normal grant with no warning.

Protected is now / plus the credential suffixes.

Behavior changes

  • mkdir, unlink, rename, symlink or link directly inside a Protected directory is now skipped with a warning instead of granted with a NOTE.
  • The same operations directly inside a Guarded directory now print the warning and sibling diff instead of a NOTE.
  • The NOTE: observed a direct write message is gone. Warning wording is unified across both routes.

Tests

  • test_write_grant_skips_protected replaces the /root based test: it uses a temporary .ssh directory and covers both creating a file under it and mkdir inside it.
  • test_write_grant_warns_guarded_home runs the same two routes against a temporary $HOME and expects the grant, the guarded warning and the diff each time.
  • test_direct_write_root_skipped updated for the unified "filesystem root" wording.
  • Locally: learn_test passes (41), sandlock-cli unit tests pass (6). The rest of the workspace suites were not run locally.

docs/learn.md tier table updated to match.

🤖 Generated with Claude Code

collapse_write_paths had two branches: an existing path was recorded
with a NOTE whatever its tier, while a missing path was walked up to its
nearest existing ancestor and only there checked against the tiers. A
Landlock grant on a directory is recursive either way, so mkdir in a
protected directory got the grant that creating a file under the same
directory was refused. Resolve the granted path first and apply the tier
check once, so both routes skip Protected and warn with a diff on
Guarded.

Also move /root from Protected to Guarded. As a non-root caller /root is
not writable anyway, and as root it is simply $HOME, where dotfile
writes are as legitimate as in any other home directory. The old
special case made learned profiles silently incomplete for root
workloads and left the HOME != "/root" check unreachable.

Fixes #217

Signed-off-by: Cong Wang <cwang@multikernel.io>
@congwang-mk

Copy link
Copy Markdown
Contributor Author

@ghazariann Please take a look. Thanks!

@congwang-mk
congwang-mk merged commit 0f1d389 into main Sep 20, 2026
17 checks passed
@congwang-mk
congwang-mk deleted the learn-protected-tier-consistency branch September 20, 2026 21:08
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.

learn: inconsistent behavior for write grants to Protected paths

1 participant