Skip to content

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

@xroche

Description

@xroche

Summary

When ddprof runs as a non-root user with file capabilities (e.g. cap_ipc_lock,cap_perfmon,cap_sys_ptrace=+ep set via setcap), profiling targets that have their own file capabilities (e.g. nginx with cap_net_bind_service) causes the ddprof worker process to permanently lose ALL capabilities.

This breaks profiling silently — the worker continues running but can no longer call perf_event_open or mmap perf ring buffers.

Root Cause

The bug is in the UID elevation logic in open_proc_maps() (src/dso_hdr.cc:46-56) and open_proc_comm() (src/ddprof_process.cc:26-36).

When reading /proc/<pid>/maps for a target process fails (because the target has file capabilities, making it non-dumpable), these functions attempt UID elevation via user_override():

  1. fopen("/proc/<pid>/maps") fails — target is non-dumpable
  2. stat() shows file owned by root (st_uid=0)
  3. user_override(0, 0, &old_uids)setresuid(0, 0, -1) — UIDs become (0, 0, 1000) (saved UID stays at original)
  4. Retry fopenstill fails (needs CAP_SYS_PTRACE, not just UID 0)
  5. user_override(old_uids.uid, old_uids.gid)setresuid(1000, 1000, -1) — UIDs become (1000, 1000, 1000)
  6. Capability clearing rule: all UIDs are now nonzero (after at least one was 0) → kernel clears ALL capabilities from permitted, effective, and ambient sets
  7. Worker permanently loses CAP_IPC_LOCK + CAP_PERFMON → profiling breaks

Why UID elevation is useless for non-root

For root users, the UID round-trip is safe because the saved UID stays at 0, so the "all nonzero" condition is never met.

For non-root users, the UID elevation to root via setresuid(0, 0, -1) requires CAP_SETUID. Even when this succeeds, it doesn't help read /proc/<pid>/maps of non-dumpable processes — that requires CAP_SYS_PTRACE regardless of UID (the kernel performs a ptrace_may_access() check).

So for non-root users, the UID elevation is both useless (doesn't grant access) and destructive (destroys all capabilities).

Environment

  • ddprof 0.23.0 (current main)
  • Non-root user (UID 1000) with file capabilities on the ddprof binary
  • Target processes with file capabilities (e.g. nginx with cap_net_bind_service=+ep)
  • Kubernetes containers with capabilities in the bounding set

Proposed Fix

Guard the UID elevation with is_root() (already defined in user_override.hpp) so that non-root users skip the elevation entirely:

// In open_proc_maps (src/dso_hdr.cc)
if (!f && is_root()) {
    // UID elevation only works when running as root
    ...
}

// Same pattern in open_proc_comm (src/ddprof_process.cc)

PR: #495

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions