Repository navigation
fix(uname): tolerate missing /proc/sys/kernel/arch on pre-6.1 kernels (Fixes #702) - #703
Merged
Merged
Conversation
/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
marked this pull request as ready for review
October 27, 2026 19:26
matt-dz
enabled auto-merge
October 27, 2026 19:30
julesmcrt
approved these changes
Oct 28, 2026
julesmcrt
left a comment
Collaborator
There was a problem hiding this comment.
Reviewed c7d2fb1a.
Findings
- [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. PerAGENTS.md, theverified/allowed_symbolslabel 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 bymachine_linux.go;unix.Unameis a read-only, file-free syscall and everything else is pure/constant. Side note: the policy is documented inAGENTS.mdbut not indocs/RULES.md— worth a pointer there so it's discoverable from the docs tree. - [P3]
builtins/internal/procsyskernel/machine_linux.go:25-26— ifunix.Unamefails, the returned error is the stale ENOENT from the arch read, masking the real cause (a future policy change denyingUnameinside the worker would be misreported as a missing arch file). Still fails closed; consider wrapping both errors. - [P3]
machine_linux.go:20-21— the fallback also fires when the configured--proc-pathitself is missing, souname -mno 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. - [P3, test coverage]
TestOpenRulesSkipsOnlyOptionalMissingExactFilepins 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:281runs thefstattype check unconditionally after the open), and a subtest pinning it would guard against futureOptionalmisuse on security-critical paths.
Verified
Optionalskips exactly one case: theO_PATHopen of the exact file returningENOENT(internal/sandbox/landlock/restrict_linux.go:260). Every other open error fails closed, and a successful open is still type-validated (:281) even withOptionalset — so a directory, FIFO, orEACCESat that path cannot ride the flag. TrustedPaths are hard-coded literals with no glob support; the only otherOptionaluser 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, andsetns/unshare/execve/non-runtimecloneflags are all denied — the worker cannot leave or spoof its UTS namespace. The fallback triggers only onos.ErrNotExistfrom opening the exactsys/kernel/archpath.- Trust equivalence:
/proc/sys/kernel/archexposesinit_uts_ns.name.machine— the same kernel fielduname(2)returns in the worker's namespace, and the same source GNU uname uses. No new spoofing vector. - Platform gating is right:
machine_linux.gois Linux-only by filename constraint,machine_other.gois//go:build !linux, anduname_linux_test.gois filename-gated plusrequireLinux(t).go build/go vetclean on darwin, linux/amd64, and windows/amd64; the full darwin-hostgo test ./...passes and the Linux-only suites are correctly tag-gated. - Docs (
docs/PRIVILEGED_HELPER.md,SHELL_FEATURES.md, theuname.goheader) 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).
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.
What
Fixes #702: on Linux kernels older than 6.1 (e.g. RHEL 9, kernel 5.14), privileged
unamefailed because/proc/sys/kernel/archdoesn't exist there.Root cause
/proc/sys/kernel/archwas added in Linux 6.1. Two places assumed it always exists:trustedPathsForCommandsrequired the file, so building the policy for any command set that includesrshell:unamefailed with ENOENT, before any command ran.unamebuiltin.-mand-aread only that file, so they failed on older kernels even outside the helper. (The code comment saying "available since 2.6" was wrong.)Fix
archtrusted pathOptional.Optionalskips only ENOENT; any other open error still fails closed, as the issue asked. The other fourunamefiles are still required.procsyskernel.ReadMachinereadssys/kernel/archand, only when that file is missing, falls back to the machine field ofuname(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.procsyskernelsymbols (unix.Uname,unix.Utsname,unix.ByteSliceToString,errors.Is,os.ErrNotExist). This needs the human-onlyverified/allowed_symbolsreview.docs/PRIVILEGED_HELPER.mdandSHELL_FEATURES.md.Tests
TestTrustedPathsForCommands:archmust beOptional(fails before, passes after).TestUnameMachineFallsBackWithoutArchFile(Linux): a fake proc tree withoutarch;uname -manduname -amust succeed and print theuname(2)machine (fails before, passes after).TestOpenRulesSkipsOnlyOptionalMissingExactFile: a missing optional exact file is skipped, and the same file withoutOptionalstill fails with ENOENT. This pins the fail-closed behaviour the fix relies on.go test -race ./...,go vet ./...(plus Windows/darwin cross-vet) andgofmt -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 ofdocs/PRIVILEGED_HELPER.md.🤖 Generated with Claude Code