Skip to content

Conversation

@elazarg
Copy link
Collaborator

@elazarg elazarg commented Sep 16, 2025

Summary

  • add helpers in the transformer test fixture to construct program info for alternate platforms and to scope map descriptor mutations
  • extend the transformer Catch2 suite with map-load/map-lookup/callx scenarios and with stack writable-argument and packet-reallocation call semantics

Testing

  • cmake --build build --target tests
  • ./tests "[transformer]"

https://chatgpt.com/codex/tasks/task_e_68c897a330288329bd41b7fed0dbb44a

Summary by CodeRabbit

  • New Features
    • No user-facing changes.
  • Tests
    • Added extensive test coverage for the eBPF transformer, spanning arithmetic, pointer/stack behavior, bitwise and shift operations, endianness, assumptions, packet and map interactions, subprogram calls, and memory round-trips. Improves reliability and guards against regressions.
  • Chores
    • Enabled the new tests in the build pipeline.

Signed-off-by: Prevail AI <prevail-ai@example.com>
@coderabbitai
Copy link

coderabbitai bot commented Sep 16, 2025

Walkthrough

Adds a new eBPF transformer test suite, wires it into the build when tests are enabled, and exposes internal state of EbpfDomain to tests via a forward declaration and friend class.

Changes

Cohort / File(s) Summary of edits
Build/Test wiring
CMakeLists.txt
Includes test_ebpf_transformer.cpp in the test sources within the VERIFIER_ENABLE_TESTS block.
Domain test access
src/crab/ebpf_domain.hpp
Adds forward declaration class EbpfDomainInspector; and friend class EbpfDomainInspector; inside EbpfDomain.
New eBPF transformer tests
src/test/test_ebpf_transformer.cpp
Introduces comprehensive tests covering arithmetic, bitwise, shifts, pointer/stack semantics, endianness, assumptions, packets, maps (incl. maps of maps, program arrays), calls/subprograms, and helper dispatch, along with test utilities and inspectors.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Add EbpfTransformer Catch2 test suite" succinctly and accurately summarizes the PR's primary change: adding a Catch2-based test suite for the eBPF transformer and integrating it into the build. It is specific to the main intent and avoids unnecessary detail such as file lists or internal implementation. A reviewer scanning project history will understand the PR focus from this title.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/add-catch2-test-suite-for-ebpftransformer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@elazarg elazarg changed the title Expand transformer map and call coverage Test EbpfTransformer Sep 16, 2025
@elazarg elazarg changed the title Test EbpfTransformer Add EbpfTransformer Catch2 test suite Sep 16, 2025
@coveralls
Copy link

Pull Request Test Coverage Report for Build 17776994019

Details

  • 545 of 545 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall first build on codex/add-catch2-test-suite-for-ebpftransformer at 88.796%

Totals Coverage Status
Change from base Build 17748859600: 88.8%
Covered Lines: 9114
Relevant Lines: 10264

💛 - Coveralls

@elazarg elazarg marked this pull request as ready for review September 16, 2025 20:58
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
CMakeLists.txt (1)

203-224: Define PREVAIL_TESTING for test builds to enable the gated friend.

Propagate a test-only compile definition to both the library and tests.

Apply this diff:

 if (VERIFIER_ENABLE_TESTS)
+    # Enable test-only access (gated friend) in headers when building tests
+    target_compile_definitions(prevail PUBLIC PREVAIL_TESTING)
     target_compile_options(check PRIVATE ${COMMON_FLAGS})
@@
     target_link_libraries(tests PRIVATE Threads::Threads)
     target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
+    target_compile_definitions(tests PRIVATE PREVAIL_TESTING)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50855d6 and 6fd42fe.

📒 Files selected for processing (3)
  • CMakeLists.txt (1 hunks)
  • src/crab/ebpf_domain.hpp (1 hunks)
  • src/test/test_ebpf_transformer.cpp (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
CMakeLists.txt

📄 CodeRabbit inference engine (AGENTS.md)

Maintain C++20 build configuration for the core verifier (e.g., CMAKE_CXX_STANDARD 20) in the top-level CMakeLists.txt

Files:

  • CMakeLists.txt
**/*.{c,cc,cpp,h,hpp}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{c,cc,cpp,h,hpp}: Run the project formatter (./scripts/format-code --staged) before committing to ensure consistent clang-format styling
Ensure new C/C++ sources include the standard SPDX license header (validate via ./scripts/check-license.sh)

Files:

  • src/crab/ebpf_domain.hpp
  • src/test/test_ebpf_transformer.cpp
**/*.{h,hpp}

📄 CodeRabbit inference engine (AGENTS.md)

Headers should prefer #pragma once for include guards; follow existing patterns within each subdirectory

Files:

  • src/crab/ebpf_domain.hpp
src/{asm_*,cfg,arith,crab,linux}/**/*.{cc,cpp,h,hpp}

📄 CodeRabbit inference engine (AGENTS.md)

Add new verifier logic under the matching subsystem directory (asm_*, cfg, arith, crab, linux) to keep separation of concerns

Files:

  • src/crab/ebpf_domain.hpp
src/test/**/*.{cc,cpp}

📄 CodeRabbit inference engine (AGENTS.md)

Place Catch2 unit/integration tests in src/test and add focused tests when modifying verifier behaviour

Files:

  • src/test/test_ebpf_transformer.cpp
**/*.{cc,cpp}

📄 CodeRabbit inference engine (AGENTS.md)

Favor explicit error handling and early exits in the verifier to surface problems rather than relying on implicit behaviour

Files:

  • src/test/test_ebpf_transformer.cpp
🧠 Learnings (2)
📚 Learning: 2025-09-15T22:00:28.296Z
Learnt from: CR
PR: vbpf/prevail#0
File: AGENTS.md:0-0
Timestamp: 2025-09-15T22:00:28.296Z
Learning: Applies to src/test/**/*.{cc,cpp} : Place Catch2 unit/integration tests in src/test and add focused tests when modifying verifier behaviour

Applied to files:

  • CMakeLists.txt
  • src/test/test_ebpf_transformer.cpp
📚 Learning: 2025-09-15T22:00:28.296Z
Learnt from: CR
PR: vbpf/prevail#0
File: AGENTS.md:0-0
Timestamp: 2025-09-15T22:00:28.296Z
Learning: Applies to CMakeLists.txt : Maintain C++20 build configuration for the core verifier (e.g., CMAKE_CXX_STANDARD 20) in the top-level CMakeLists.txt

Applied to files:

  • CMakeLists.txt
🧬 Code graph analysis (1)
src/test/test_ebpf_transformer.cpp (2)
src/platform.hpp (1)
  • map_fd (36-36)
src/crab/ebpf_domain.hpp (2)
  • ebpf_domain_transform (26-26)
  • ebpf_domain_initialize_loop_counter (31-31)
🔇 Additional comments (1)
src/crab/ebpf_domain.hpp (1)

33-34: Gate test-only inspector behind PREVAIL_TESTING

Wrap the forward declaration and friend with a test-only macro so test-only internals aren't exposed to production.

Location: src/crab/ebpf_domain.hpp (forward declaration + friend)

- class EbpfDomainInspector;
+ #ifdef PREVAIL_TESTING
+ class EbpfDomainInspector;
+ #endif
@@
   class EbpfDomain final {
     friend class EbpfChecker;
     friend class EbpfTransformer;
-    friend class EbpfDomainInspector;
+ #ifdef PREVAIL_TESTING
+     friend class EbpfDomainInspector;
+ #endif

Verify there are no non-test references to EbpfDomainInspector before applying (search in the sandbox was inconclusive).

Comment on lines +71 to +75
template <typename T>
void apply(EbpfDomain& dom, T instruction) {
ebpf_domain_transform(dom, Instruction{std::move(instruction)});
}

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Use perfect forwarding to avoid extra copies in apply().

Minor tidy-up; reduces copies of Instruction wrappers.

Apply this diff:

-template <typename T>
-void apply(EbpfDomain& dom, T instruction) {
-    ebpf_domain_transform(dom, Instruction{std::move(instruction)});
-}
+template <typename T>
+void apply(EbpfDomain& dom, T&& instruction) {
+    ebpf_domain_transform(dom, Instruction{std::forward<T>(instruction)});
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
template <typename T>
void apply(EbpfDomain& dom, T instruction) {
ebpf_domain_transform(dom, Instruction{std::move(instruction)});
}
template <typename T>
void apply(EbpfDomain& dom, T&& instruction) {
ebpf_domain_transform(dom, Instruction{std::forward<T>(instruction)});
}
🤖 Prompt for AI Agents
In src/test/test_ebpf_transformer.cpp around lines 71-75, the apply() template
takes its instruction by value causing an extra copy when constructing the
Instruction wrapper; change the signature to take T&& and forward the argument
into the Instruction construction using std::forward<T>(instruction) so the
wrapper is constructed in-place without unnecessary copies (i.e., make apply a
forwarding reference and use std::forward when passing into
ebpf_domain_transform).

Comment on lines +96 to +102
void set_scalar(EbpfDomain& dom, const Reg reg, const int64_t value) {
const uint64_t magnitude = value < 0 ? static_cast<uint64_t>(-value) : static_cast<uint64_t>(value);
apply(dom, Bin{.op = Bin::Op::MOV, .dst = reg, .v = Imm{magnitude}, .is64 = true, .lddw = false});
if (value < 0) {
apply(dom, Un{.op = Un::Op::NEG, .dst = reg, .is64 = true});
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Guard against INT64_MIN overflow in set_scalar().

Unary minus on INT64_MIN is UB; also allow 64-bit immediates via lddw.

Apply this diff:

 void set_scalar(EbpfDomain& dom, const Reg reg, const int64_t value) {
-    const uint64_t magnitude = value < 0 ? static_cast<uint64_t>(-value) : static_cast<uint64_t>(value);
-    apply(dom, Bin{.op = Bin::Op::MOV, .dst = reg, .v = Imm{magnitude}, .is64 = true, .lddw = false});
-    if (value < 0) {
-        apply(dom, Un{.op = Un::Op::NEG, .dst = reg, .is64 = true});
-    }
+    if (value == std::numeric_limits<int64_t>::min()) {
+        // Load raw 0x8000... and rely on the domain's signed view to interpret as -2^63.
+        apply(dom, Bin{.op = Bin::Op::MOV, .dst = reg, .v = Imm{0x8000000000000000ULL}, .is64 = true, .lddw = true});
+        return;
+    }
+    const uint64_t magnitude = value < 0 ? static_cast<uint64_t>(-(value)) : static_cast<uint64_t>(value);
+    const bool needs_lddw = magnitude > std::numeric_limits<uint32_t>::max();
+    apply(dom, Bin{.op = Bin::Op::MOV, .dst = reg, .v = Imm{magnitude}, .is64 = true, .lddw = needs_lddw});
+    if (value < 0) apply(dom, Un{.op = Un::Op::NEG, .dst = reg, .is64 = true});
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void set_scalar(EbpfDomain& dom, const Reg reg, const int64_t value) {
const uint64_t magnitude = value < 0 ? static_cast<uint64_t>(-value) : static_cast<uint64_t>(value);
apply(dom, Bin{.op = Bin::Op::MOV, .dst = reg, .v = Imm{magnitude}, .is64 = true, .lddw = false});
if (value < 0) {
apply(dom, Un{.op = Un::Op::NEG, .dst = reg, .is64 = true});
}
}
void set_scalar(EbpfDomain& dom, const Reg reg, const int64_t value) {
if (value == std::numeric_limits<int64_t>::min()) {
// Load raw 0x8000... and rely on the domain's signed view to interpret as -2^63.
apply(dom, Bin{.op = Bin::Op::MOV, .dst = reg, .v = Imm{0x8000000000000000ULL}, .is64 = true, .lddw = true});
return;
}
const uint64_t magnitude = value < 0 ? static_cast<uint64_t>(-(value)) : static_cast<uint64_t>(value);
const bool needs_lddw = magnitude > std::numeric_limits<uint32_t>::max();
apply(dom, Bin{.op = Bin::Op::MOV, .dst = reg, .v = Imm{magnitude}, .is64 = true, .lddw = needs_lddw});
if (value < 0) apply(dom, Un{.op = Un::Op::NEG, .dst = reg, .is64 = true});
}
🤖 Prompt for AI Agents
In src/test/test_ebpf_transformer.cpp around lines 96-102, set_scalar()
currently negates int64_t value which is undefined for INT64_MIN and doesn't use
lddw for full 64-bit immediates; change the logic to detect value == INT64_MIN
and handle it without performing unary minus: for INT64_MIN load the unsigned
64-bit immediate (uint64_t) representing INT64_MIN using a MOV with lddw = true
and do not emit a subsequent NEG; for other negative values compute magnitude as
static_cast<uint64_t>(-value) and for any immediate that requires full 64-bit
width set lddw = true on the MOV (otherwise keep lddw = false), and only emit
the NEG when value is negative and not INT64_MIN.

Comment on lines +538 to +545
descriptor.type = 1; // Regular data map.

apply(dom, LoadMapFd{.dst = r(1), .mapfd = map_fd});

const auto pack = reg_pack(r(1));
CHECK(reg_type(dom, r(1)) == T_MAP);
CHECK(interval(dom, pack.map_fd) == Interval(map_fd));
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Replace magic map-type numbers with named constants.

Improves readability and reduces coupling to UAPI numeric values.

Apply this diff to add local constants:

 namespace {
+// Minimal named aliases to avoid magic numbers in tests.
+constexpr int BPF_MAP_TYPE_ARRAY = 1;
+constexpr int BPF_MAP_TYPE_PROG_ARRAY = 3;
+constexpr int BPF_MAP_TYPE_ARRAY_OF_MAPS = 12;

Then update usages:

-    descriptor.type = 1; // Regular data map.
+    descriptor.type = BPF_MAP_TYPE_ARRAY; // Regular data map.
@@
-    descriptor.type = 3; // BPF_MAP_TYPE_PROG_ARRAY -> program maps.
+    descriptor.type = BPF_MAP_TYPE_PROG_ARRAY; // Program maps.
@@
-    descriptor.type = 1;
+    descriptor.type = BPF_MAP_TYPE_ARRAY;
@@
-    descriptor.type = 12; // ARRAY_OF_MAPS -> returns another map fd.
+    descriptor.type = BPF_MAP_TYPE_ARRAY_OF_MAPS; // Returns inner map fd.

Also applies to: 552-560, 566-574, 622-624

🤖 Prompt for AI Agents
In src/test/test_ebpf_transformer.cpp around lines 538-545 (and also update
occurrences at 552-560, 566-574, 622-624), replace hard-coded numeric map-type
literals (e.g. "descriptor.type = 1") with a named local constant (e.g.
constexpr int MAP_TYPE_REGULAR = <uapi value>), declare the constant near the
top of the test file or inside the test scope, and then update all usages in the
specified ranges to use the named constant instead of the magic number so the
code reads descriptively and avoids coupling to raw UAPI integers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants