Skip to content

fix: prevent capability loss from UID elevation for non-root users#495

Open
xroche wants to merge 1 commit intoDataDog:mainfrom
algolia:fix/capability-loss-uid-override
Open

fix: prevent capability loss from UID elevation for non-root users#495
xroche wants to merge 1 commit intoDataDog:mainfrom
algolia:fix/capability-loss-uid-override

Conversation

@xroche
Copy link

@xroche xroche commented Feb 27, 2026

Summary

Fixes #494

When ddprof runs as a non-root user with file capabilities (e.g. cap_ipc_lock,cap_perfmon,cap_sys_ptrace=+ep), profiling targets that have their own file capabilities (e.g. nginx with cap_net_bind_service) permanently destroys all ddprof worker capabilities.

The UID round-trip in open_proc_maps() and open_proc_comm() (e.g. 1000→0→1000) triggers the Linux kernel's capability clearing rule: when all UIDs become nonzero after at least one was zero, ALL capabilities are cleared from permitted+effective+ambient sets.

The fix

Guard the UID elevation with is_root() so non-root users skip it entirely. This is safe because:

  1. For root users: the round-trip preserves caps (saved UID stays 0)
  2. For non-root users: UID elevation is both useless (can't read non-dumpable /proc/<pid>/maps without CAP_SYS_PTRACE regardless of UID) and destructive (clears all caps)

Changes

  • src/dso_hdr.ccopen_proc_maps(): guard UID elevation with is_root()
  • src/ddprof_process.ccopen_proc_comm(): same guard
  • test/user_override_caps-ut.cc — new test verifying capability preservation
  • test/CMakeLists.txt — register the new test

Test plan

  • Build succeeds (cmake -B build && cd build && make -j ddprof)
  • Existing CI tests pass
  • Manual Docker test: non-root ddprof with file caps profiling a process with file caps — worker retains CapEff ≠ 0
  • The new demonstrates_capability_loss_bug test can be run manually with sudo setcap 'cap_setuid,cap_setgid,cap_ipc_lock=+ep' ./build/test/user_override_caps-ut to confirm the kernel behavior

When ddprof runs as a non-root user with file capabilities (e.g.
CAP_IPC_LOCK, CAP_PERFMON), the UID round-trip in open_proc_maps()
and open_proc_comm() permanently destroys all capabilities:

1. fopen("/proc/<pid>/maps") fails (target is non-dumpable)
2. stat() shows file owned by root (st_uid=0)
3. setresuid(0, 0, -1) sets real+effective to 0, saved stays 1000
4. Retry fopen still fails (needs CAP_SYS_PTRACE, not just UID 0)
5. setresuid(1000, 1000, -1) restores UIDs — all nonzero
6. Kernel clears ALL capabilities (Linux cap clearing rule)
7. Worker permanently loses IPC_LOCK + PERFMON → profiling breaks

The UID elevation only works when running as actual root (saved UID
stays 0, so capabilities are preserved on restore). For non-root
users, it is both useless and destructive.

Guard the UID elevation with is_root() so non-root users skip the
elevation attempt entirely, preserving their file capabilities.

Also adds test/user_override_caps-ut.cc to verify capability
preservation across the fixed code path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@xroche xroche force-pushed the fix/capability-loss-uid-override branch from 4a3f974 to 842e217 Compare February 27, 2026 12:02
@xroche xroche marked this pull request as ready for review February 27, 2026 12:02
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

1 participant