Skip to content

fix(daemon): trust the kernel overflow uid as an ancestor owner inside a restricted user namespace - #2103

Open
AmirF194 wants to merge 1 commit into
DeusData:mainfrom
AmirF194:fix/1830-userns-remap-ancestor-walk
Open

fix(daemon): trust the kernel overflow uid as an ancestor owner inside a restricted user namespace#2103
AmirF194 wants to merge 1 commit into
DeusData:mainfrom
AmirF194:fix/1830-userns-remap-ancestor-walk

Conversation

@AmirF194

@AmirF194 AmirF194 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Inside a single-uid Docker userns-remap container (or some WSL2 devcontainer
setups), cbm fails to start with "secure CLI coordination could not be
created" on every path. The ancestor-owner trust check in
posix_directory_owner_trusted (src/daemon/ipc.c) only accepts an
ancestor owned by uid 0 or the caller's own euid. With a single-uid
mapping, uid 0 has no entry in the container's uid map, so the kernel
renders any unmapped host owner, including real root which owns /,
/home and /tmp, as the kernel overflow uid (65534 by default,
configurable) instead. Neither branch matches, so every ancestor is
refused.

The fix extends the trust check to also accept the overflow uid, read
once from /proc/sys/kernel/overflowuid, but only when this process's
own namespace genuinely has no mapping for uid 0 (read from
/proc/self/uid_map). Outside a restricted namespace the overflow uid is
an ordinary account (a service dropped to "nobody"), not a stand-in for
unmapped root, so it is not trusted unconditionally. The leaf private
directory's own ownership check is untouched: it still requires an exact
match against the caller's euid before it is chmod'd to 0700.

Checklist

  • Every commit is signed off (git commit -s)
  • Tests pass locally (scripts/test.sh --suites daemon_ipc, 49/49)
  • Lint passes (clang-format-20 --dry-run --Werror, cppcheck)
  • New behavior is covered by a test (reproduce-first for bug fixes)

Verification

  • New test daemon_ipc_posix_private_directory_admits_overflow_uid_ancestor_inside_restricted_userns
    goes red when just this fix's hunk in posix_directory_owner_trusted is
    reverted (ASSERT(secured)), and passes with it restored.
  • New test ..._refuses_overflow_uid_ancestor_outside_restricted_userns is
    a negative control: it stays green either way (the ordinary case is
    unchanged), and I confirmed it is not vacuous by temporarily making the
    trust check always return true, which turns it red.
  • Full scripts/test.sh --suites daemon_ipc (49/49), clang-format-20
    and cppcheck all clean.
  • I have not tried this against an actual Docker userns-remap container.
    Building a real restricted user namespace needs CAP_SYS_ADMIN, which
    is unavailable here even inside a fresh container (unshare --user
    fails EPERM), so both new tests fabricate the ownership condition
    directly and pin the namespace state through a test-only seam rather
    than a real namespace.

Fixes #1830

…e a restricted user namespace

posix_directory_owner_trusted only accepted an ancestor owned by uid 0 or
the caller's own euid. Inside a single-uid Docker userns-remap (and some
WSL2 devcontainer setups), uid 0 has no entry in the container's uid map,
so the kernel renders any host-owned path whose real owner is unmapped,
including real root which owns "/", "/home" and "/tmp", as the kernel
overflow uid instead. Every ancestor of a private cache directory then
rendered as that overflow uid, so the trust check refused it and the
daemon could never secure its private directory: every path failed with
"secure CLI coordination could not be created".

Extend posix_directory_owner_trusted to also accept the kernel's own
overflow uid (read once from /proc/sys/kernel/overflowuid, falling back
to the 65534 default if unreadable), but only when this process's own
user namespace genuinely has no mapping for uid 0 (read from
/proc/self/uid_map). Outside a restricted namespace the overflow uid
names an ordinary account (services dropped to "nobody"), not a stand-in
for unmapped root, so trusting it unconditionally would let anything
writable by such an account own a trusted ancestor. The leaf private
directory's own ownership check is untouched: it still requires an exact
match against the caller's euid before it is chmod'd to 0700.

Tests: two new Linux-only, root-required tests (fabricating an
arbitrary-uid ancestor needs CAP_CHOWN). One confirms an overflow-uid
ancestor is admitted when the process's own namespace has no mapping for
uid 0; the other confirms the same ancestor still stays refused outside
one, so the fix cannot regress the ordinary case. A real restricted
namespace needs CAP_SYS_ADMIN to construct, which this sandbox does not
grant even inside a fresh container (confirmed live: unshare --user fails
EPERM here), so the namespace state is pinned through a test-only seam
instead of unshare(1), consistent with how the file's own Windows tests
already pin non-constructible OS state.

Fixes DeusData#1830

Signed-off-by: Amir Fathi <amirfathi.me@gmail.com>
@AmirF194
AmirF194 requested a review from DeusData as a code owner September 8, 2026 06:47
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

@DeusData DeusData added bug Something isn't working security Security vulnerabilities, hardening editor/integration Editor compatibility and CLI integration priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. labels Sep 9, 2026
@DeusData

DeusData commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Thank you for contributing the restricted-user-namespace case and the focused IPC tests. We need more time for a careful review of this ownership-policy change before giving a decision.

The review queue is currently full, so detailed feedback may take a little time. We are working through it carefully and appreciate the work you have put into supporting the project. Thank you for your patience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working editor/integration Editor compatibility and CLI integration priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. security Security vulnerabilities, hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux: ancestor walk fails unconditionally in Docker userns-remap containers (single-uid mapping, / owned by nobody/65534)

2 participants