Skip to content

feat: run memtrack memory profiling without sudo via file capabilities#407

Open
not-matthias wants to merge 9 commits into
mainfrom
feat/memtrack-sudoless
Open

feat: run memtrack memory profiling without sudo via file capabilities#407
not-matthias wants to merge 9 commits into
mainfrom
feat/memtrack-sudoless

Conversation

@not-matthias

@not-matthias not-matthias commented Jun 15, 2026

Copy link
Copy Markdown
Member

No description provided.

Raising RLIMIT_MEMLOCK requires CAP_SYS_RESOURCE, which unprivileged agents and many containers lack. On kernels >= 5.11 BPF memory is accounted against the cgroup, so the limit is irrelevant; treat a failed setrlimit as a warning instead of bailing.
Without an IPC server nothing toggles the tracking_enabled map, so the eBPF is_enabled() check drops every event. Enable tracking up front in that path.
@codspeed-hq

codspeed-hq Bot commented Jun 15, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 7 untouched benchmarks


Comparing feat/memtrack-sudoless (05b53e2) with main (9e21a9c)

Open in CodSpeed

codspeed setup --mode memory now grants codspeed-memtrack the capabilities it needs (cap_bpf, cap_perfmon, cap_dac_read_search, cap_sys_admin, cap_sys_resource) via setcap, idempotently and with a single sudo prompt. At run time the memory executor no longer wraps memtrack in sudo: it requires root or those capabilities, otherwise failing with guidance to run codspeed setup. Lets agents run memory benchmarks unattended.

Related: COD-1801
setup status now prints a privileges line under the memory executor:
satisfied when running as root or the codspeed-memtrack binary carries
the required file capabilities, missing (with remediation) otherwise.
Lets agents confirm sudo-less memory profiling is ready before a run.

Related: COD-1801
memtrack-based memory profiling is Linux-only; the caps crate fails to
build on macOS. Move caps/memtrack/ipc-channel to Linux-only dependencies
and cfg-gate the memory module and its wiring so macOS builds compile.
@not-matthias not-matthias force-pushed the feat/memtrack-sudoless branch from 6b0e488 to d729c92 Compare June 15, 2026 16:33
@not-matthias not-matthias marked this pull request as ready for review June 15, 2026 16:36
@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces the previous sudo-wrapped memtrack invocation with file capabilities (setcap), so codspeed-memtrack can be run directly without a per-run sudo prompt. A one-time setcap is performed during codspeed setup, the granted capabilities are verified via the security.capability xattr before each run, and a hard-fail guard in executor.run() gives an actionable error when privileges are missing.

  • permitted_caps_from_xattr / binary_has_capabilities \u2014 a hand-rolled xattr decoder checks VFS_CAP_REVISION_1/2/3 layouts and correctly handles both 32-bit cap words, backed by targeted unit tests.
  • ensure_memtrack_capabilities \u2014 best-effort setup helper that runs setcap via sudo once and warns (but returns Ok(())) on failure; the hard enforcement lives in MemoryExecutor::ensure_privileges() at run time.
  • crates/memtrack/src/main.rs \u2014 fixes a silent data-drop: without an IPC channel (standalone mode), the eBPF is_enabled() guard now has tracking enabled up-front.

Confidence Score: 5/5

Safe to merge. The capability-granting path is idempotent and best-effort; the hard privilege gate in run() ensures memtrack never silently runs without the needed caps.

The xattr parsing is correct for all VFS_CAP revisions, the two-layer design (best-effort grant at setup, hard-fail at run) is sound, all tracing-macro and git-pin rules are followed, and the no-IPC tracking fix is clearly correct. The only open points are non-blocking UX refinements.

src/executor/memory/setup.rs — ensure_memtrack_capabilities silently returns Ok(()) when setcap or the post-grant xattr verification fails, so codspeed setup exits 0 even when caps were not actually written.

Important Files Changed

Filename Overview
src/executor/helpers/run_with_sudo.rs Adds permitted_caps_from_xattr, binary_has_capabilities, and has_sufficient_privileges helpers for xattr-based file-capability inspection, plus a new run_with_sudo function. Parsing logic correctly maps VFS_CAP_REVISION_1/2/3 layouts; tests are thorough.
src/executor/memory/setup.rs Adds MEMTRACK_REQUIRED_CAPS / MEMTRACK_SETCAP_SPEC constants and ensure_memtrack_capabilities which is best-effort: setcap failure and post-grant cap verification failure both return Ok(()) with only a warning, so codspeed setup exits 0 even when capabilities were not actually granted.
src/executor/memory/executor.rs Removes wrap_with_sudo from the run path, adds ensure_privileges() hard-gate before execution, and implements privilege_status() / grant_privileges() for the Executor trait. Logic is clean.
src/executor/mod.rs Gates memory module and MemoryExecutor behind cfg(target_os = linux), adds PrivilegeStatus enum and privilege_status / grant_privileges default no-op trait methods, calls grant_privileges in run_executor.
crates/memtrack/src/main.rs Enables tracking up-front when no IPC channel is present (standalone mode), fixing a silent data-drop regression where events would be filtered by the eBPF is_enabled() guard.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as codspeed CLI
    participant Setup as setup_executor / run_executor
    participant EC as ensure_memtrack_capabilities
    participant EP as ensure_privileges (run)
    participant Memtrack as codspeed-memtrack (eBPF)

    User->>CLI: codspeed setup --mode memory
    CLI->>Setup: setup_executor()
    Setup->>Setup: executor.setup() install memtrack binary
    Setup->>EC: executor.grant_privileges()
    EC->>EC: memtrack_path() via which
    EC->>EC: binary_has_capabilities()? No
    EC->>EC: run_with_sudo(setcap, spec + path)
    EC-->>Setup: Ok(()) warns on setcap failure
    Setup-->>CLI: Ok(()) exit 0

    User->>CLI: codspeed run --mode memory
    CLI->>Setup: run_executor()
    Setup->>EC: executor.grant_privileges() if not skip_setup
    EC->>EC: binary_has_capabilities()? Yes no-op
    EC-->>Setup: Ok(())
    Setup->>EP: executor.run() ensure_privileges()
    EP->>EP: is_root_user() or has_memtrack_capabilities()
    alt privileges satisfied
        EP-->>Setup: Ok(())
        Setup->>Memtrack: spawn codspeed-memtrack track no sudo
        Memtrack->>Memtrack: load eBPF via file caps
        Memtrack-->>Setup: exit status
    else privileges missing
        EP-->>Setup: Err run codspeed setup
        Setup-->>CLI: bail
    end
Loading

Reviews (3): Last reviewed commit: "fixup! fix(runner): gate memory executor..." | Re-trigger Greptile

Comment on lines +26 to +29
/// `setcap` text form of [`MEMTRACK_REQUIRED_CAPS`]. Kept as an explicit string
/// because it must match libcap's grammar exactly.
const MEMTRACK_SETCAP_SPEC: &str =
"cap_bpf,cap_perfmon,cap_dac_read_search,cap_sys_admin,cap_sys_resource+ep";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 MEMTRACK_SETCAP_SPEC and MEMTRACK_REQUIRED_CAPS must be kept in sync manually

These two constants encode the same set of capabilities in two different representations — one as a &[Capability] array (used by memtrack_required_caps_mask() to build the xattr verification mask) and one as a libcap grammar string (passed verbatim to setcap). If a future contributor adds a new capability to MEMTRACK_REQUIRED_CAPS without updating MEMTRACK_SETCAP_SPEC, setcap will grant the old set, the post-grant binary_has_capabilities check will fail (because the required mask is now larger), the "capabilities did not stick" warning fires, and every subsequent codspeed run will hit the ensure_privileges bail with "run codspeed setup" — even though setup was just run successfully. A compile-time or unit-test assertion that MEMTRACK_SETCAP_SPEC names exactly the caps in MEMTRACK_REQUIRED_CAPS would catch this class of drift.

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.

1 participant