Skip to content

proc allow root and fakeroot guests to switch to any UID/GID#217

Merged
jserv merged 1 commit into
sysprog21:mainfrom
open-sources-port:uid-gid-permission
Jul 18, 2026
Merged

proc allow root and fakeroot guests to switch to any UID/GID#217
jserv merged 1 commit into
sysprog21:mainfrom
open-sources-port:uid-gid-permission

Conversation

@doanbaotrung

@doanbaotrung doanbaotrung commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Guest processes running as root (effective UID 0) or in fakeroot mode should have the privilege to change their UID or GID to any value. Previously, elfuse restricted UID/GID changes strictly to the currently configured active credentials (emu_uid, emu_euid, etc.), aborting privilege-dropping calls with EPERM.

We now permit any UID/GID switches in uid_is_permitted() and gid_is_permitted() if the guest process has effective UID 0 or fakeroot is active.

Fix #190


Summary by cubic

Allow root (euid 0) and fakeroot guests to switch to any UID/GID; privileged setuid/setgid now reset real, effective and saved IDs, and setreuid/setregid may change the real ID, matching Linux and avoiding EPERM when dropping privileges. Fixes #190.

Written for commit e39d92f. Summary will update on new commits.

Review in cubic

cubic-dev-ai[bot]

This comment was marked as resolved.

@jserv
jserv requested a review from Max042004 July 17, 2026 16:03
Comment thread src/syscall/proc-identity.c Outdated
{ \
uint32_t old_real = real; \
if (r != (uint32_t) -1 && r != real && r != eff) \
if (r != (uint32_t) -1 && r != real && r != eff && !perm_fn(r)) \

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.

setreuid/setregid's real-ID (r) check now falls back to perm_fn(r), which for unprivileged callers also accepts the saved ID via uid_is_permitted/gid_is_permitted. Real Linux setreuid(2)/setregid(2) restrict the real-ID argument to {current real, current effective} only — saved ID is only valid there via setresuid/setresgid.

Verified against a real Linux kernel (aarch64, 7.0.5): after setresuid(1000,1000,0) (drop euid, keep suid=0), setreuid(0,-1) returns EPERM on Linux but succeeds here, letting an unprivileged process restore its real UID through a call a real kernel would reject. Suggest a narrower permission check for r (real/effective only, bypassed when privileged) instead of reusing uid_is_permitted/gid_is_permitted as-is.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated

Comment thread tests/test-credentials.c
EXPECT_TRUE(rc > 0 && (mask & 1), "CPU0 not in mask");
}

if (expected_id == 0) {

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.

These root-only assertions never run in CI: test-credentials isn't in tests/manifest.txt (not part of make check), test-matrix.sh invokes it without --fakeroot (default guest UID 1000), and it's listed in QEMU_SKIP for the real-root qemu lane — so expected_id == 0 is unreachable everywhere.

Running it manually with --fakeroot fails 7/27 cases: the earlier setuid(other)/setgid(other) root-branch calls (lines 73/156) now permanently drop identity to 1000 per this PR's own "reset all three fields" fix, but the later setfsuid/setfsgid/getgroups assertions still assume the stale expected_id == 0 identity. Please wire --fakeroot into a test runner so this doesn't silently bit-rot, and fix the stale-identity assumption in those later checks.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated

@doanbaotrung

Copy link
Copy Markdown
Collaborator Author

I have addressed both review items:

  1. Narrowed Real-ID Check: Replaced the reuse of perm_fn(r) for real-ID validation in DEFINE_SETRE at proc-identity.c with a narrow check !(emu_euid == 0 || proc_fakeroot_enabled()). This restricts unprivileged callers to setting the real ID strictly to the current real or effective ID (preventing them from using the saved ID via setreuid/setregid), while preserving unrestricted switches for privileged callers.
  2. Prevented down-stream test pollution via fork: Refactored credential-dropping and privilege tests in test-credentials.c to run in fork-isolated child processes. This ensures the parent process retains its initial credentials throughout the entire test suite execution, fixing the errors when running the test under --fakeroot.
  3. Enabled testing in CI: Added test-credentials-fakeroot to test-matrix.sh (and listed it under QEMU_SKIP to skip it on native QEMU runners which do not support the elfuse fakeroot flag). This ensures the fakeroot mode identity assertions are run and validated under the Rosetta matrix.

Guest processes running as root (effective UID 0) or in fakeroot mode
should have the privilege to change their UID or GID to any value.
Previously, elfuse restricted UID/GID changes strictly to the
currently configured active credentials (emu_uid, emu_euid, etc.),
aborting privilege-dropping calls with EPERM.

We now permit any UID/GID switches in uid_is_permitted() and
gid_is_permitted() if the guest process has effective UID 0 or
fakeroot is active.

Fix sysprog21#190
@jserv
jserv merged commit ef2cf25 into sysprog21:main Jul 18, 2026
10 checks passed
@doanbaotrung
doanbaotrung deleted the uid-gid-permission branch July 18, 2026 17:13
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.

UID/GID permission switching restrictions under Fakeroot return EPERM

3 participants