Skip to content

Skip UID elevation for non-root users to preserve capabilities#495

Closed
xroche wants to merge 1 commit into
DataDog:mainfrom
algolia:fix/capability-loss-uid-override
Closed

Skip UID elevation for non-root users to preserve capabilities#495
xroche wants to merge 1 commit into
DataDog:mainfrom
algolia:fix/capability-loss-uid-override

Conversation

@xroche

@xroche xroche commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes #494

Guards the UID elevation in open_proc_maps() and open_proc_comm() with is_root() so non-root users skip it entirely.

Motivation

When ddprof runs as a non-root user with file capabilities (cap_perfmon, cap_sys_ptrace, etc.), profiling a target that owns different file capabilities (e.g. nginx with cap_net_bind_service) triggers a UID round-trip through 0 in open_proc_maps(). On the way back (setresuid(1000, 1000, -1)), the kernel permanently clears all capabilities from the permitted, effective, and ambient sets.

This is documented in capabilities(7) under "Effect of user ID changes on capabilities":

If one or more of the real, effective, or saved set user IDs was previously 0, and as a result of the UID changes all of these IDs have a nonzero value, then all capabilities are cleared from the permitted, effective, and ambient capability sets.

The rule exists because the kernel treats the transition "was root, now isn't" as a credential downgrade and drops everything. See cap_emulate_setxuid() in kernel/security/commoncap.c (called from setresuid).

For root users the round-trip is safe: the saved-set-user-ID stays 0, so the condition never fires. For non-root users the elevation is also pointless: reading a non-dumpable process's /proc/<pid>/maps requires CAP_SYS_PTRACE regardless of UID.

Additional Notes

Rebased on current main. Simplified test compared to v1.

How to test the change?

The new user_override_caps-ut test verifies that a failed user_override(0, 0) call (the non-root CI case) does not destroy existing capabilities.

To reproduce the full bug path manually:

sudo setcap 'cap_setuid,cap_setgid,cap_ipc_lock,cap_perfmon=+ep' ./ddprof
# run ddprof as non-root against a target with file caps
# observe CapEff drops to 0 in /proc/<ddprof_pid>/status after first maps read

The UID round-trip in open_proc_maps() and open_proc_comm() clears all
process capabilities when the caller is non-root.  The kernel zeroes the
permitted, effective, and ambient sets whenever every UID becomes nonzero
after at least one was zero (capabilities(7), "Effect of user ID changes
on capabilities").  Guard both call sites with is_root().

Fixes DataDog#494

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@xroche
xroche force-pushed the fix/capability-loss-uid-override branch from 842e217 to d883137 Compare March 16, 2026 11:47
@xroche xroche changed the title fix: prevent capability loss from UID elevation for non-root users Skip UID elevation for non-root users to preserve capabilities Mar 16, 2026
@r1viollet

Copy link
Copy Markdown
Collaborator

Sorry for the late reply, I think the fix needs to be slightly more subtle, I'll post something.

@r1viollet

Copy link
Copy Markdown
Collaborator

closing in favour of #509

@r1viollet r1viollet closed this Mar 18, 2026
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.

Non-root users with file capabilities permanently lose all caps after UID elevation in open_proc_maps/open_proc_comm

2 participants