Skip to content

Migrate cardwire-ebpf (C) to aya-ebpf (Rust) - #137

Merged
luytan merged 38 commits into
mainfrom
c-ebpf-to-aya-ebpf
Aug 2, 2026
Merged

Migrate cardwire-ebpf (C) to aya-ebpf (Rust)#137
luytan merged 38 commits into
mainfrom
c-ebpf-to-aya-ebpf

Conversation

@luytan

@luytan luytan commented Aug 2, 2026

Copy link
Copy Markdown
Member

Description

Rewrite the ebpf program present inside cardwire-ebpf to be written in Rust using aya-ebpf

Why ?

  • Easier to maintain
  • I'm more comfortable with Rust
  • Safer

Fixes # (issue)

TODO

  • Copy-Paste this line

Checklist:

  • My code follows the style guidelines of this project (cargo fmt)
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the mdBook documentation
  • My changes generate no new warnings (clippy/clang)
  • New and existing unit tests pass locally with my changes (either use nix flake check or wait for the ci)

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added Rust-based GPU access control, process tracking, directory filtering, and event reporting.
    • Added process whitelisting and forced-process handling.
    • Included userspace and GUI components in the default workspace build.
  • Bug Fixes

    • Improved GPU identification across manual and smart modes.
    • Improved handling of GPU-related environment settings.
  • Chores

    • Updated development, packaging, and CI workflows for nightly Rust and bpf-linker.
    • Added improved eBPF logging and error handling.

Walkthrough

The PR replaces C eBPF components with Rust programs and a userspace loader. It updates daemon PID classification, GPU map handling, workspace membership, CI, Nix builds, and Arch packaging for nightly Rust and bpf-linker.

Changes

Rust eBPF migration

Layer / File(s) Summary
Kernel eBPF programs and maps
crates/cardwire-ebpf/src/*, crates/cardwire-ebpf/Cargo.toml, crates/cardwire-ebpf/build.rs
Rust LSM hooks, tracepoints, policy helpers, maps, and event buffers now implement the eBPF programs.
Userspace loader and API
Cargo.toml, crates/cardwire-ebpf-userspace/*
The new crate builds and loads the eBPF object, attaches programs, exposes typed maps and ring buffers, and defines error handling.
Daemon policy and GPU integration
crates/cardwire-daemon/Cargo.toml, crates/cardwire-daemon/src/*
The daemon uses the userspace crate, separates allowed and forced PIDs, parses numeric environment values, and passes actual GPU IDs to eBPF operations.
Build automation and packaging
.github/workflows/cicd.yml, flake.nix, nix/default.nix, packages/arch-linux/*, crates/cardwire-gui/src/ui.rs
Builds use nightly Rust and bpf-linker. C checks are removed. Rust checks cover relevant workspace changes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CardwireDaemon
  participant EbpfBlocker
  participant RustEbpfPrograms
  CardwireDaemon->>EbpfBlocker: create blocker
  EbpfBlocker->>RustEbpfPrograms: load and attach programs
  RustEbpfPrograms-->>EbpfBlocker: expose maps and ring buffers
  EbpfBlocker-->>CardwireDaemon: return typed eBPF handles
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: migrating the CardWire eBPF program from C to Rust with aya-ebpf.
Description check ✅ Passed The description summarizes the migration and motivations and includes the required sections, but issue and checklist items remain unresolved.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@luytan
luytan force-pushed the c-ebpf-to-aya-ebpf branch from be8ae46 to c7962f1 Compare August 2, 2026 08:00
@luytan
luytan marked this pull request as ready for review August 2, 2026 08:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/cardwire-daemon/src/analyzer/models.rs (1)

193-198: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

spawn_remove_analyzer forgets to sweep the newly conjured forced_map.

This function only calls pid_map.remove(&event.pid) on process exit. The new forced_map (introduced alongside PidType::Forced in this same file) is never cleaned up here.

Every process the daemon marks PidType::Forced leaves a permanent entry in CW_FORCED_PID, a bounded 16384-entry eBPF map. Over the lifetime of a long-running daemon, this map fills with stale entries for exited processes, and once the kernel recycles a PID, a stale forced-map entry can silently misroute an unrelated new process to whatever GPU the old, unrelated process was forced onto.

Remove from both maps on exit.

🔮 Proposed fix
     async fn spawn_remove_analyzer(&self, event: CloseEvent) -> () {
         let mut pid_map = self.pid_map.write().await;
         if pid_map.remove(&event.pid).is_ok() {
             debug!("REMOVE: pid: {}", event.pid);
         }
+        drop(pid_map);
+        let mut forced_map = self.forced_map.write().await;
+        let _ = forced_map.remove(&event.pid);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/analyzer/models.rs` around lines 193 - 198, Update
spawn_remove_analyzer to remove event.pid from both pid_map and the newly
introduced forced_map during process cleanup. Preserve the existing debug
logging behavior while ensuring stale forced-map entries are deleted whenever a
process exits.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/cicd.yml:
- Around line 82-87: Update the “Clippy Workspace” step’s check-args to restore
the --locked flag alongside --all-targets and --all-features, preserving
reproducible CI behavior unless the nightly migration explicitly requires
lockfile regeneration.
- Around line 70-81: Pin all floating action references in the rust-lint,
rust-test, and rust-format jobs: replace each dtolnay/rust-toolchain@nightly
reference with a full commit SHA and explicitly set toolchain: nightly, and
replace both taiki-e/install-action@v2 references with full commit SHAs. Apply
the changes at .github/workflows/cicd.yml lines 70-81 and 99-108.

In `@crates/cardwire-daemon/src/analyzer/models.rs`:
- Around line 171-177: Update the PidType::Forced handling to insert the
resolved dGPU ID assigned by read_gpu, rather than hardcoded 1, into forced_map.
In the process-exit cleanup path, remove each closed PID from both eBPF maps,
including forced_map, so stale entries cannot persist or affect recycled PIDs.

In `@crates/cardwire-ebpf-userspace/src/lib.rs`:
- Around line 217-248: Replace the unwraps in get_exec_ring, get_close_ring,
get_report_ring, get_pid_map, get_forced_pid_map, and get_mode_map with the
established fallible error-handling pattern: convert missing take_map results
using CardwireEbpfError::missing_map and map try_from failures with
CardwireEbpfError::aya, propagating both through CardwireEbpfResult without
panicking.
- Around line 118-164: Update is_inode_blocked to treat successful lookup of the
inode in CW_BLOCKED_INO as blocked regardless of the stored GPU key; return
false only for MapError::KeyNotFound and preserve existing error propagation.
Remove the value == 1 check and update the nearby comment/doc wording so it
reflects presence-based blocking rather than fixed 0/1 values.

In `@crates/cardwire-ebpf/build.rs`:
- Around line 15-16: Update the bpf-linker lookup in the build script to replace
the bare unwrap with an explicit failure message naming the missing bpf-linker
executable and instructing users to install it or add it to PATH. Keep the
existing rerun-if-changed behavior unchanged when the lookup succeeds.

In `@crates/cardwire-ebpf/src/helpers.rs`:
- Around line 99-125: Extract the repeated CW_REPORT_EVENTS reserve/write/submit
sequence into an #[inline(always)] report_blocked helper accepting pid and
constructing ReportEvent internally. Replace all three duplicated reporting
blocks in the surrounding logic with calls to this helper, preserving the
existing conditional submission behavior.
- Around line 21-28: Update the map accesses in the inode-checking logic and
related settings/directory-entry operations to pass references as keys: use
borrowed keys for CW_BLOCKED_INO, CW_EXP_BLK_INO, CW_SETTINGS, CW_DIRENT.insert,
and CW_DIRENT.get. Preserve the existing lookup and insertion behavior while
applying the reference form consistently.
- Around line 55-61: The kernel and daemon use incompatible ReportEvent layouts
across the ring-buffer boundary, causing all reports to be rejected by the
daemon size check. Align the ReportEvent definition and
serialization/deserialization between the eBPF helper and daemon, either by
using one shared matching layout or by parsing the kernel’s 8-byte pid-only
record; update affected tests to validate the corrected ABI.

In `@crates/cardwire-ebpf/src/main.rs`:
- Around line 291-293: Update the surrounding function to convert the i64 errors
from TracePointContext::read_at and CW_DIRENT.insert into the function’s i32
error type before returning, using the required ReturnCode::SUCCESS success
value; pass references to pid and dirp_ptr to CW_DIRENT.insert instead of
values.
- Line 223: Wrap both unsafe operations in explicit unsafe blocks in the
function containing the path_ptr assignment, including the ctx.arg(0) call and
its companion call. Keep the existing mod vmlinux allowance unchanged, since it
does not cover this function.

In `@nix/default.nix`:
- Around line 47-63: Update the aya-build patch commands in postPatch so each
expected replacement is verified and the build fails if its target pattern is
absent. Preserve the existing replacements for "+nightly", "-Z", and
"build-std=core", and make the corresponding config.toml cleanup fail when its
expected build-std entry is missing, ensuring future vendored-source changes
surface during patching.

In `@packages/arch-linux/cardwire-PKGBUILD`:
- Around line 12-18: Update the Arch package build flow in prepare() to run the
locked dependency fetch through the nightly toolchain using rustup run nightly
cargo fetch --locked, and update build() to invoke cargo through rustup run
nightly as well. Apply the same explicit-nightly changes to the corresponding
cardwire-git-PKGBUILD while preserving the existing build arguments.

---

Outside diff comments:
In `@crates/cardwire-daemon/src/analyzer/models.rs`:
- Around line 193-198: Update spawn_remove_analyzer to remove event.pid from
both pid_map and the newly introduced forced_map during process cleanup.
Preserve the existing debug logging behavior while ensuring stale forced-map
entries are deleted whenever a process exits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 61c277b1-9404-4adf-87aa-a65ad96a5d88

📥 Commits

Reviewing files that changed from the base of the PR and between 9b32122 and c7962f1.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • flake.lock is excluded by !**/*.lock
📒 Files selected for processing (31)
  • .github/workflows/cicd.yml
  • Cargo.toml
  • crates/cardwire-daemon/Cargo.toml
  • crates/cardwire-daemon/src/analyzer/models.rs
  • crates/cardwire-daemon/src/core/errors.rs
  • crates/cardwire-daemon/src/interface/config.rs
  • crates/cardwire-daemon/src/interface/debug.rs
  • crates/cardwire-daemon/src/interface/gpu.rs
  • crates/cardwire-daemon/src/interface/mode.rs
  • crates/cardwire-daemon/src/interface/switcheroo.rs
  • crates/cardwire-daemon/src/models.rs
  • crates/cardwire-daemon/src/tasks/watch_power_state.rs
  • crates/cardwire-ebpf-userspace/Cargo.toml
  • crates/cardwire-ebpf-userspace/build.rs
  • crates/cardwire-ebpf-userspace/src/errors.rs
  • crates/cardwire-ebpf-userspace/src/lib.rs
  • crates/cardwire-ebpf/Cargo.toml
  • crates/cardwire-ebpf/build.rs
  • crates/cardwire-ebpf/src/c/bpf.c
  • crates/cardwire-ebpf/src/c/bpf.h
  • crates/cardwire-ebpf/src/c/helpers.h
  • crates/cardwire-ebpf/src/helpers.rs
  • crates/cardwire-ebpf/src/lib.rs
  • crates/cardwire-ebpf/src/main.rs
  • crates/cardwire-ebpf/src/maps.rs
  • crates/cardwire-ebpf/src/vmlinux.rs
  • crates/cardwire-gui/src/ui.rs
  • flake.nix
  • nix/default.nix
  • packages/arch-linux/cardwire-PKGBUILD
  • packages/arch-linux/cardwire-git-PKGBUILD
💤 Files with no reviewable changes (3)
  • crates/cardwire-ebpf/src/c/helpers.h
  • crates/cardwire-ebpf/src/c/bpf.c
  • crates/cardwire-ebpf/src/c/bpf.h

Comment thread .github/workflows/cicd.yml
Comment thread .github/workflows/cicd.yml
Comment thread crates/cardwire-daemon/src/analyzer/models.rs
Comment thread crates/cardwire-ebpf-userspace/src/lib.rs Outdated
Comment thread crates/cardwire-ebpf-userspace/src/lib.rs
Comment thread crates/cardwire-ebpf/src/helpers.rs Outdated
Comment thread crates/cardwire-ebpf/src/main.rs
Comment thread crates/cardwire-ebpf/src/main.rs Outdated
Comment thread nix/default.nix
Comment thread packages/arch-linux/cardwire-PKGBUILD

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/cardwire-daemon/src/interface/gpu.rs (1)

62-62: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Update the hotplug caller for the GPU map-key contract.

By the GPU runes, block_gpu now uses value as the GPU map key. crates/cardwire-daemon/src/interface/debug.rs Line 150 still calls gpu.block_gpu(1). If a re-discovered blocked GPU has an ID other than 1, hotplug writes its inode policy under GPU key 1.

Iterate over gpu_interfaces with its key and pass that key to block_gpu, or make block_gpu use self.id.

Proposed fix in `crates/cardwire-daemon/src/interface/debug.rs`
-            for gpu in gpu_interfaces.values_mut() {
+            for (id, gpu) in gpu_interfaces.iter_mut() {
                 let should_block = match mode {
                     Modes::Integrated | Modes::Smart => !gpu.device.is_default(),
                     Modes::Hybrid => false,
@@
-                    if let Err(e) = gpu.block_gpu(1).await {
+                    if let Err(e) = gpu.block_gpu(*id as u32).await {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/interface/gpu.rs` at line 62, Update the hotplug
caller in the GPU interface handling around gpu_interfaces to iterate with each
map key and pass that key to block_gpu instead of the hardcoded value 1,
preserving the correct GPU key for re-discovered devices.
.github/workflows/cicd.yml (1)

78-83: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Cast a dedicated nightly Clippy check for crates/cardwire-ebpf.

crates/cardwire-ebpf is not a default member. The action does not pass --workspace, so -D warnings does not lint this crate. Add a dedicated nightly check for it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cicd.yml around lines 78 - 83, Add a dedicated nightly
Clippy workflow step targeting the non-default-member crate
`crates/cardwire-ebpf`, ensuring it runs with warnings treated as errors and
uses the existing bpf-linker setup. Keep the existing `Clippy Workspace` check
unchanged.

Source: MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/cicd.yml:
- Around line 78-83: Add a dedicated nightly Clippy workflow step targeting the
non-default-member crate `crates/cardwire-ebpf`, ensuring it runs with warnings
treated as errors and uses the existing bpf-linker setup. Keep the existing
`Clippy Workspace` check unchanged.

In `@crates/cardwire-daemon/src/interface/gpu.rs`:
- Line 62: Update the hotplug caller in the GPU interface handling around
gpu_interfaces to iterate with each map key and pass that key to block_gpu
instead of the hardcoded value 1, preserving the correct GPU key for
re-discovered devices.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6a94be4f-1244-4e41-8d76-7c989daa0333

📥 Commits

Reviewing files that changed from the base of the PR and between c7962f1 and 0e90db5.

📒 Files selected for processing (6)
  • .github/workflows/cicd.yml
  • crates/cardwire-daemon/src/analyzer/dynamic_analysis.rs
  • crates/cardwire-daemon/src/analyzer/models.rs
  • crates/cardwire-daemon/src/interface/debug.rs
  • crates/cardwire-daemon/src/interface/gpu.rs
  • crates/cardwire-daemon/src/models.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/cardwire-ebpf-userspace/src/lib.rs (2)

78-82: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧙 Correct the missing tracepoint name in the error.

The lookup uses tracepoint_exit_getdents64, but the error reports try_tracepoint_exit_getdents64. When the program is missing, the diagnostic names a program that does not exist. Use the lookup name in CardwireEbpfError::missing_lsm.

🔧 Proposed fix
-            .ok_or_else(|| CardwireEbpfError::missing_lsm("try_tracepoint_exit_getdents64"))?
+            .ok_or_else(|| CardwireEbpfError::missing_lsm("tracepoint_exit_getdents64"))?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-ebpf-userspace/src/lib.rs` around lines 78 - 82, Update the
CardwireEbpfError::missing_lsm argument in the tracepoint_exit_getdents64 lookup
to report the exact lookup name, tracepoint_exit_getdents64, instead of the
mismatched try_tracepoint_exit_getdents64 value.

118-145: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧙 Preserve shared PCI-inode ownership when unblocking.

pci_to_inode walks parent PCI devices without deduplication, so GPUs can share a parent inode. CW_BLOCKED_INO stores one GPU ID per inode, but unblock_inode removes entries by inode only. Unblocking one GPU can stop blocking a shared parent inode for another GPU. Pass the GPU ID to unblock_inode and use reference counting or multi-owner storage. gpu_blocked does not check parent PCI inodes and cannot detect this condition.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-ebpf-userspace/src/lib.rs` around lines 118 - 145, Update
unblock_inode and its callers to accept the GPU ID alongside the inode, and
preserve CW_BLOCKED_INO entries still owned by other GPUs when unblocking a
shared parent PCI inode. Replace inode-only removal with ownership-aware
reference counting or multi-owner storage, and update block_inode consistently
so shared inode ownership is retained until the final GPU is unblocked.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/cardwire-ebpf-userspace/src/lib.rs`:
- Around line 78-82: Update the CardwireEbpfError::missing_lsm argument in the
tracepoint_exit_getdents64 lookup to report the exact lookup name,
tracepoint_exit_getdents64, instead of the mismatched
try_tracepoint_exit_getdents64 value.
- Around line 118-145: Update unblock_inode and its callers to accept the GPU ID
alongside the inode, and preserve CW_BLOCKED_INO entries still owned by other
GPUs when unblocking a shared parent PCI inode. Replace inode-only removal with
ownership-aware reference counting or multi-owner storage, and update
block_inode consistently so shared inode ownership is retained until the final
GPU is unblocked.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5beaecb8-5f0e-47b3-bda9-2286d87bb426

📥 Commits

Reviewing files that changed from the base of the PR and between 0e90db5 and 9fb34d4.

📒 Files selected for processing (2)
  • crates/cardwire-daemon/src/interface/gpu.rs
  • crates/cardwire-ebpf-userspace/src/lib.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
crates/cardwire-ebpf/src/main.rs (1)

183-183: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cast explicit unsafe blocks around the bare ctx.arg(0) calls.

Two sites still call ctx.arg(0) outside an unsafe block, even though the enclosing function is unsafe fn:

  • Line 183 in try_inode_permission.
  • Line 241 in try_inode_getattr (raised in a prior review round, still unresolved here).

Edition 2024 emits unsafe_op_in_unsafe_fn for these bare calls, and the crate's #[allow] only covers mod vmlinux. A build with -D warnings fails on both sites.

🪄 Proposed fix
-    let inode_ptr: *mut inode = ctx.arg(0);
+    let inode_ptr: *mut inode = unsafe { ctx.arg(0) };
-    let path_ptr: *const path = ctx.arg(0);
+    let path_ptr: *const path = unsafe { ctx.arg(0) };

Also applies to: 241-241

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-ebpf/src/main.rs` at line 183, Wrap each bare ctx.arg(0) call
in explicit unsafe blocks in try_inode_permission and try_inode_getattr. Update
both inode_ptr initializations so Edition 2024 no longer reports
unsafe_op_in_unsafe_fn warnings, without changing surrounding logic.
crates/cardwire-ebpf-userspace/src/lib.rs (1)

46-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename the missing-program error for tracepoints; "missing lsm" is the wrong rune for them.

exec_program, close_program, cardwire_sys_enter_getdents64, and cardwire_sys_exit_getdents64 (lines 46-92) all use CardwireEbpfError::missing_lsm(...) when program_mut returns None. These are TracePoint programs, not LSM hooks, yet the resulting message reads "missing lsm: tracepoint_sched_process_exec". Anyone debugging a failed daemon startup gets a misleading diagnostic pointing at the wrong subsystem.

Add a missing_program (or similarly generic) variant and constructor in errors.rs, and use it for the four TracePoint lookups here, keeping missing_lsm for the three actual LSM hooks at lines 35-44.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-ebpf-userspace/src/lib.rs` around lines 46 - 92, add a
generic missing_program error variant and constructor in errors.rs, then replace
CardwireEbpfError::missing_lsm with it for the exec_program, close_program,
cardwire_sys_enter_getdents64, and cardwire_sys_exit_getdents64 TracePoint
lookups. Leave missing_lsm unchanged for the actual LSM hook lookups.
crates/cardwire-daemon/src/analyzer/models.rs (1)

236-241: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Cast the Forced decision from presence, not GPU ID

CARDWIRE_FORCE_GPU accepts arbitrary GPU IDs, but value == 1 blocks IDs such as 0 and 2. Use Some(_) for the boolean decision and preserve value as the target GPU ID.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/analyzer/models.rs` around lines 236 - 241, Update
the CARDWIRE_FORCE_DGPU and CARDWIRE_FORCE_GPU branches in the environment
decision logic so the boolean result is true whenever the variable is present,
regardless of its numeric GPU ID. Preserve each parsed value as the returned
target GPU ID.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cardwire-daemon/src/analyzer/models.rs`:
- Around line 457-484: Update the stale size comment in
test_report_event_deserialization_from_valid_bytes to describe ReportEvent as
containing only the 4-byte pid field. Rename
test_report_event_comm_extraction_with_full_length_name to reflect that it
validates ReportEvent deserialization or pid extraction, while preserving its
current assertions.

In `@crates/cardwire-ebpf-userspace/src/lib.rs`:
- Around line 362-368: In the AsyncFd::with_interest match, replace the Err(err)
arm’s Err(CardwireEbpfError::aya(err))? expression with a direct return
Err(CardwireEbpfError::aya(err)); while preserving the existing error log.
- Around line 218-350: Introduce a shared generic helper on the containing type,
such as take_typed_map<T, E>, to take a named map, return
CardwireEbpfError::missing_map when absent, and convert TryFrom failures through
CardwireEbpfError::aya. Refactor get_exec_ring, get_close_ring, get_report_ring,
get_pid_map, get_forced_pid_map, and get_mode_map to call this helper while
preserving their existing map names and return types.

In `@crates/cardwire-ebpf/src/main.rs`:
- Around line 308-317: Verify the eBPF-target build for
try_tracepoint_enter_getdents64, specifically the ? operators on ctx.read_at and
CW_DIRENT.insert, using the aya_build::build_ebpf path. If their i64 errors
cannot convert into the function’s i32 error type, explicitly map each error to
i32 before propagation while preserving successful values and the Result<i32,
i32> contract.
- Around line 505-508: Update the error message in
try_tracepoint_sched_process_exit to name tracepoint_sched_process_exit instead
of tracepoint_sched_process_exec, leaving the corresponding message in
try_tracepoint_sched_process_exec unchanged.

---

Outside diff comments:
In `@crates/cardwire-daemon/src/analyzer/models.rs`:
- Around line 236-241: Update the CARDWIRE_FORCE_DGPU and CARDWIRE_FORCE_GPU
branches in the environment decision logic so the boolean result is true
whenever the variable is present, regardless of its numeric GPU ID. Preserve
each parsed value as the returned target GPU ID.

In `@crates/cardwire-ebpf-userspace/src/lib.rs`:
- Around line 46-92: add a generic missing_program error variant and constructor
in errors.rs, then replace CardwireEbpfError::missing_lsm with it for the
exec_program, close_program, cardwire_sys_enter_getdents64, and
cardwire_sys_exit_getdents64 TracePoint lookups. Leave missing_lsm unchanged for
the actual LSM hook lookups.

In `@crates/cardwire-ebpf/src/main.rs`:
- Line 183: Wrap each bare ctx.arg(0) call in explicit unsafe blocks in
try_inode_permission and try_inode_getattr. Update both inode_ptr
initializations so Edition 2024 no longer reports unsafe_op_in_unsafe_fn
warnings, without changing surrounding logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 06fecf5a-d46e-459c-b832-4a203c46e48d

📥 Commits

Reviewing files that changed from the base of the PR and between 9fb34d4 and f72627a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • crates/cardwire-daemon/Cargo.toml
  • crates/cardwire-daemon/src/analyzer/models.rs
  • crates/cardwire-daemon/src/interface/debug.rs
  • crates/cardwire-daemon/src/interface/gpu.rs
  • crates/cardwire-ebpf-userspace/src/lib.rs
  • crates/cardwire-ebpf/src/helpers.rs
  • crates/cardwire-ebpf/src/main.rs

Comment thread crates/cardwire-daemon/src/analyzer/models.rs
Comment thread crates/cardwire-ebpf-userspace/src/lib.rs
Comment thread crates/cardwire-ebpf-userspace/src/lib.rs
Comment thread crates/cardwire-ebpf/src/main.rs
Comment thread crates/cardwire-ebpf/src/main.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/cardwire-daemon/src/analyzer/models.rs (1)

240-245: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Allow non-1 GPU IDs through the force path.

CARDWIRE_FORCE_GPU=2 returns (false, PidType::Forced, 2). spawn_exec_analyzer then skips the forced-map insertion because it requires result.0 to be true.

Treat a parsed CARDWIRE_FORCE_GPU value as a force request. Preserve the value as the GPU map ID. Validate the ID separately if required.

Proposed fix
         if let Some(value) = check_env("CARDWIRE_FORCE_GPU", &environ) {
-            return Some((value == 1, PidType::Forced, value));
+            return Some((true, PidType::Forced, value));
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/analyzer/models.rs` around lines 240 - 245, Update
the CARDWIRE_FORCE_GPU branch in the environment-selection logic so any
successfully parsed value requests the forced path, rather than setting the
boolean from value == 1; preserve the parsed value as the GPU map ID and leave
CARDWIRE_FORCE_DGPU behavior unchanged. Ensure spawn_exec_analyzer can insert
non-1 forced GPU IDs, with separate validation only if required by existing ID
rules.
crates/cardwire-ebpf-userspace/src/lib.rs (1)

196-203: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the kernel’s terminating NUL byte in CW_ALLOWED_COMM. bpf_get_current_comm() returns [u8; 16] with at most 15 command bytes and a trailing NUL. For inputs of 16 bytes or more, this code stores 16 non-NUL bytes, so the lookup fails. Limit the copied length to 15 bytes or reject longer names.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-ebpf-userspace/src/lib.rs` around lines 196 - 203, Update
allow_comm so the generated 16-byte key always reserves the final byte for the
kernel’s terminating NUL: cap the copied command length at 15 bytes, or reject
inputs longer than 15 bytes while preserving the existing result behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cardwire-ebpf/src/helpers.rs`:
- Around line 152-160: Replace the authorization logic in is_comm_whitelisted so
it no longer uses the mutable bpf_get_current_comm value or CW_ALLOWED_COMM as
the trust decision. Base authorization on a daemon-controlled identity, such as
verified executable identity or managed PID/cgroup membership, and preserve the
boolean contract used by the hooks in main.rs.

---

Outside diff comments:
In `@crates/cardwire-daemon/src/analyzer/models.rs`:
- Around line 240-245: Update the CARDWIRE_FORCE_GPU branch in the
environment-selection logic so any successfully parsed value requests the forced
path, rather than setting the boolean from value == 1; preserve the parsed value
as the GPU map ID and leave CARDWIRE_FORCE_DGPU behavior unchanged. Ensure
spawn_exec_analyzer can insert non-1 forced GPU IDs, with separate validation
only if required by existing ID rules.

In `@crates/cardwire-ebpf-userspace/src/lib.rs`:
- Around line 196-203: Update allow_comm so the generated 16-byte key always
reserves the final byte for the kernel’s terminating NUL: cap the copied command
length at 15 bytes, or reject inputs longer than 15 bytes while preserving the
existing result behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4f25f4c3-8b53-4bc4-8bf7-f5105a00ce88

📥 Commits

Reviewing files that changed from the base of the PR and between f72627a and c4991ad.

📒 Files selected for processing (7)
  • crates/cardwire-daemon/src/analyzer/models.rs
  • crates/cardwire-daemon/src/models.rs
  • crates/cardwire-ebpf-userspace/src/lib.rs
  • crates/cardwire-ebpf/Cargo.toml
  • crates/cardwire-ebpf/src/helpers.rs
  • crates/cardwire-ebpf/src/main.rs
  • crates/cardwire-ebpf/src/maps.rs

Comment thread crates/cardwire-ebpf/src/helpers.rs
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