Skip to content

fix(uname): tolerate missing /proc/sys/kernel/arch on pre-6.1 kernels (Fixes #702) - #703

Merged
matt-dz merged 1 commit into
mainfrom
matthew.deguzman/issue-702-optional-proc-arch
Oct 28, 2026
Merged

matt-dz merged 1 commit into
mainfrom
matthew.deguzman/issue-702-optional-proc-arch

Conversation

@matt-dz

@matt-dz matt-dz commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

What

Fixes #702: on Linux kernels older than 6.1 (e.g. RHEL 9, kernel 5.14), privileged uname failed because /proc/sys/kernel/arch doesn't exist there.

Root cause

/proc/sys/kernel/arch was added in Linux 6.1. Two places assumed it always exists:

  1. Privileged worker Landlock policy. trustedPathsForCommands required the file, so building the policy for any command set that includes rshell:uname failed with ENOENT, before any command ran.
  2. The uname builtin. -m and -a read only that file, so they failed on older kernels even outside the helper. (The code comment saying "available since 2.6" was wrong.)

Fix

  • The worker now marks the arch trusted path Optional. Optional skips only ENOENT; any other open error still fails closed, as the issue asked. The other four uname files are still required.
  • procsyskernel.ReadMachine reads sys/kernel/arch and, only when that file is missing, falls back to the machine field of uname(2), which is what GNU uname uses. Other read errors are still reported. uname(2) is not in the seccomp denylist and touches no files, so it works inside the Landlock'd worker.
  • Allowlisted the new procsyskernel symbols (unix.Uname, unix.Utsname, unix.ByteSliceToString, errors.Is, os.ErrNotExist). This needs the human-only verified/allowed_symbols review.
  • Updated docs/PRIVILEGED_HELPER.md and SHELL_FEATURES.md.

Tests

  • TestTrustedPathsForCommands: arch must be Optional (fails before, passes after).
  • TestUnameMachineFallsBackWithoutArchFile (Linux): a fake proc tree without arch; uname -m and uname -a must succeed and print the uname(2) machine (fails before, passes after).
  • TestOpenRulesSkipsOnlyOptionalMissingExactFile: a missing optional exact file is skipped, and the same file without Optional still fails with ENOENT. This pins the fail-closed behaviour the fix relies on.
  • Locally: go test -race ./..., go vet ./... (plus Windows/darwin cross-vet) and gofmt -l . are all clean.

Not yet run: the root integration test (TestPrivilegedHelperRootIntegration) on RHEL 9 itself.

No overlap with #700: it doesn't touch privileged_worker_linux.go, and the one-row docs change is in a different section of docs/PRIVILEGED_HELPER.md.

🤖 Generated with Claude Code

/proc/sys/kernel/arch only exists since Linux 6.1, so on older kernels
such as RHEL 9 (5.14) the privileged worker failed to build its Landlock
policy for any command set including rshell:uname, and `uname -m`/`-a`
failed even without the helper.

- Mark the arch trusted path Optional in the worker policy. Optional
  only skips ENOENT; every other open error still fails closed.
- Fall back to the machine field of uname(2) when sys/kernel/arch is
  absent; other read errors are still reported.
- Allowlist the new procsyskernel symbols and document the behaviour.

Fixes #702

Environment: Datadog workspace

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@matt-dz
matt-dz marked this pull request as ready for review October 27, 2026 19:26
@matt-dz
matt-dz requested review from a team as code owners October 27, 2026 19:26
@matt-dz
matt-dz requested a review from Hitsuji-M October 27, 2026 19:26
@matt-dz matt-dz added the verified/analysis Human-reviewed static analysis changes label Oct 27, 2026
@matt-dz
matt-dz enabled auto-merge October 27, 2026 19:30

@julesmcrt julesmcrt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed c7d2fb1a.

Findings

  1. [Merge gate — not a code defect] This PR adds entries to analysis/symbols_internal.go (internalPerPackageSymbols["procsyskernel"] / internalAllowedSymbols): errors.Is, os.ErrNotExist, unix.ByteSliceToString, unix.Uname, unix.Utsname. Per AGENTS.md, the verified/allowed_symbols label is reserved for human manual approval, so this needs that review before merge. On the merits the set is minimal and every entry is used by machine_linux.go; unix.Uname is a read-only, file-free syscall and everything else is pure/constant. Side note: the policy is documented in AGENTS.md but not in docs/RULES.md — worth a pointer there so it's discoverable from the docs tree.
  2. [P3] builtins/internal/procsyskernel/machine_linux.go:25-26 — if unix.Uname fails, the returned error is the stale ENOENT from the arch read, masking the real cause (a future policy change denying Uname inside the worker would be misreported as a missing arch file). Still fails closed; consider wrapping both errors.
  3. [P3] machine_linux.go:20-21 — the fallback also fires when the configured --proc-path itself is missing, so uname -m no longer surfaces a bogus proc configuration (the other four fields still do). No security impact since the machine value comes from the same kernel either way; worth a line in the PR notes.
  4. [P3, test coverage] TestOpenRulesSkipsOnlyOptionalMissingExactFile pins the ENOENT-skip and the required-ENOENT failure, but not "Optional + path exists as the wrong type (e.g. a directory) → still fails closed". The code does fail closed there (restrict_linux.go:281 runs the fstat type check unconditionally after the open), and a subtest pinning it would guard against future Optional misuse on security-critical paths.

Verified

  • Optional skips exactly one case: the O_PATH open of the exact file returning ENOENT (internal/sandbox/landlock/restrict_linux.go:260). Every other open error fails closed, and a successful open is still type-validated (:281) even with Optional set — so a directory, FIFO, or EACCES at that path cannot ride the flag. TrustedPaths are hard-coded literals with no glob support; the only other Optional user is the pre-existing journal-directory case. Behavior on 6.1+ is byte-identical to before.
  • uname(2) inside the Landlock'd worker: not on the seccomp denylist (default-allow filter, internal/sandbox/seccomp/seccomp.go), performs no path access so needs no Landlock rule, and setns/unshare/execve/non-runtime clone flags are all denied — the worker cannot leave or spoof its UTS namespace. The fallback triggers only on os.ErrNotExist from opening the exact sys/kernel/arch path.
  • Trust equivalence: /proc/sys/kernel/arch exposes init_uts_ns.name.machine — the same kernel field uname(2) returns in the worker's namespace, and the same source GNU uname uses. No new spoofing vector.
  • Platform gating is right: machine_linux.go is Linux-only by filename constraint, machine_other.go is //go:build !linux, and uname_linux_test.go is filename-gated plus requireLinux(t). go build/go vet clean on darwin, linux/amd64, and windows/amd64; the full darwin-host go test ./... passes and the Linux-only suites are correctly tag-gated.
  • Docs (docs/PRIVILEGED_HELPER.md, SHELL_FEATURES.md, the uname.go header) match the code, including the removal of the incorrect "available since Linux 2.6" comment.
  • No sandbox weakening found: the skip grants nothing that didn't exist before, there are no new seccomp allowances, and trusted paths remain derived from the verified effective command allowlist rather than worker input.

Approving on the code; merging still needs the human verified/allowed_symbols sign-off (finding 1).

@matt-dz
matt-dz added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit d659d39 Oct 28, 2026
49 of 50 checks passed
@matt-dz
matt-dz deleted the matthew.deguzman/issue-702-optional-proc-arch branch October 28, 2026 10:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

verified/analysis Human-reviewed static analysis changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

privileged helper: uname fails on RHEL 9 when /proc/sys/kernel/arch is absent

2 participants