Skip to content

Remove thread_local_program_info; make Program self-contained#1091

Merged
elazarg merged 5 commits intomainfrom
explicit-context
Apr 18, 2026
Merged

Remove thread_local_program_info; make Program self-contained#1091
elazarg merged 5 commits intomainfrom
explicit-context

Conversation

@elazarg
Copy link
Copy Markdown
Collaborator

@elazarg elazarg commented Apr 18, 2026

Summary

  • Thread EbpfProgramType explicitly through platform helper functions (is_helper_usable, get_helper_prototype), removing implicit thread_local_program_info reads from the platform layer
  • Thread ProgramInfo through get_map_descriptor platform function, eliminating thread_local_program_info from elf_loader.cpp
  • Move the map equivalence cache from ProgramInfo into a local variable in parse_maps_section_linux, fixing a pre-existing test-ordering bug where stale cache entries caused FD mismatches
  • Store ProgramInfo in Program (resolves Make prepared programs self-contained #1082), making prepared programs self-contained
  • Remove thread_local_program_info entirely — variable, extern declaration, LazyAllocator include, thread_local_analysis_context() bridge, and dead ebpf_domain_check overload

Continues #1088. thread_local_options and the domain-internal thread-locals (array_map, SplitDBM::scratch_) remain for a future PR.

Test plan

  • Full test suite passes with default seed
  • Full test suite passes with seed 2276513626 (previously exposed the map cache ordering bug)
  • Full test suite passes with seed 99999

🤖 Generated with Claude Code

elazarg and others added 3 commits April 18, 2026 23:58
Replace thread_local_program_info reads in platform helper functions
with explicit const EbpfProgramType& parameters on is_helper_usable,
get_helper_prototype, make_call, and parse_instruction. This removes
the implicit dependency on ambient thread-local state from the
platform layer and instruction parsing.

Change Program::from_sequence to take ProgramInfo& (non-const) so
callback targets are computed directly on the caller's ProgramInfo
rather than through the thread-local copy.

Move the map equivalence cache from ProgramInfo into a local variable
in parse_maps_section_linux, passed explicitly through create_map_linux
and create_map_crab. This fixes a pre-existing test-ordering bug where
stale cache entries from a previous test caused FD number mismatches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Elazar Gershuni <elazarg@gmail.com>
Add const ProgramInfo& parameter to ebpf_get_map_descriptor_fn and
make it return const EbpfMapDescriptor&. Update find_map_descriptor,
get_map_descriptor_linux, and the test platform implementation.

Change EbpfDomain map query methods (get_map_type, get_map_key_size,
get_map_value_size, get_map_inner_map_fd, get_map_max_entries) to take
const AnalysisContext& instead of const ebpf_platform_t&, since they
now need both platform and program_info for map descriptor lookup.

This eliminates thread_local_program_info from elf_loader.cpp entirely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Elazar Gershuni <elazarg@gmail.com>
Program now owns its ProgramInfo, populated by from_sequence. The
analyze(prog) shims construct AnalysisContext from prog.info() and
thread_local_options, eliminating all reads of thread_local_program_info.

Add explicit AnalysisContext parameter to AnalysisResult methods
(is_valid_after, check_observation_at_label, compute_failure_slices)
and const Program& to print_error for line-info lookup.

Remove thread_local_program_info variable, its extern declaration,
the LazyAllocator include, thread_local_analysis_context(), and the
dead 3-arg ebpf_domain_check overload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Elazar Gershuni <elazarg@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 18, 2026

Warning

Rate limit exceeded

@elazarg has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 28 minutes and 8 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 28 minutes and 8 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8a04bb4a-8911-4578-8b6d-44efdfe2bbe2

📥 Commits

Reviewing files that changed from the base of the PR and between db3c7a0 and abc59f7.

📒 Files selected for processing (9)
  • src/fwd_analyzer.cpp
  • src/io/elf_loader.cpp
  • src/io/elf_loader.hpp
  • src/ir/cfg_builder.cpp
  • src/ir/program.hpp
  • src/linux/linux_platform.cpp
  • src/test/ebpf_yaml.cpp
  • src/test/test_failure_slice.cpp
  • src/verifier.hpp
📝 Walkthrough

Walkthrough

Thread-local program state is eliminated by storing ProgramInfo directly in Program and threading AnalysisContext through analysis APIs. Helper/map descriptor lookups accept EbpfProgramType and full context instead of relying on platform-only or thread-local state. Analysis entry points construct AnalysisContext from prepared program metadata. Cache state is decoupled from immutable ProgramInfo.

Changes

Cohort / File(s) Summary
Thread-local state removal
src/analysis_context.hpp, src/spec/type_descriptors.hpp
Removed thread_local_analysis_context() function and thread_local_program_info global; eliminated ProgramInfo::cache member to decouple mutable cache from immutable program facts.
Program self-containment
src/ir/program.hpp, src/ir/cfg_builder.cpp
Added ProgramInfo m_info member and info() accessor to Program; updated Program::from_sequence to accept non-const ProgramInfo& and store mutated state into program.
Helper/call resolution context threading
src/linux/gpl/spec_prototypes.cpp, src/linux/linux_platform.hpp, src/ir/unmarshal.cpp, src/ir/unmarshal.hpp, src/ir/parse.cpp, src/ir/parse.hpp
is_helper_usable_linux, get_helper_prototype_linux, and make_call now accept EbpfProgramType; parse_instruction threads program type through call instruction unmarshaling.
Map descriptor context refactoring
src/crab/ebpf_domain.cpp, src/crab/ebpf_domain.hpp, src/io/elf_loader.cpp, src/io/elf_loader.hpp, src/linux/linux_platform.cpp
Map query methods (get_map_type, get_map_inner_map_fd, get_map_key_size, get_map_value_size, get_map_max_entries) now take AnalysisContext instead of ebpf_platform_t; create_map_crab and find_map_descriptor accept external cache and ProgramInfo; get_map_descriptor_linux requires ProgramInfo.
Analysis result API context threading
src/result.cpp, src/result.hpp, src/crab/ebpf_checker.cpp, src/crab/ebpf_checker.hpp
AnalysisResult methods (is_valid_after, check_observation_at_label, is_consistent_before, is_consistent_after, compute_failure_slices) now require AnalysisContext parameter; removed ebpf_domain_check overload without context.
Transformer and domain integration
src/crab/ebpf_transformer.cpp, src/platform.hpp
Map/helper lookups in transformer pass full AnalysisContext; platform function pointers updated to accept EbpfProgramType and ProgramInfo.
Forward analyzer and CLI entry points
src/fwd_analyzer.cpp, src/main.cpp
Removed dependency on thread_local_analysis_context(); analysis overloads now construct AnalysisContext explicitly from Program::info(), options, and platform.
Printing and error reporting
src/printing.cpp
print_error signature updated to require Program parameter; LineInfoPrinter constructor-injected with line_info_map and verbosity flag instead of reading thread-local state.
Test updates
src/test/ebpf_yaml.cpp, src/test/test_failure_slice.cpp, src/test/test_platform_tables.cpp, src/test/test_verify.hpp, src/test/test_verify_multithreading.cpp
Tests updated to construct/pass AnalysisContext; helper/map APIs called with program type; removed thread-local program info mutations; macro and helpers refactored for non-const program access.

Possibly related issues

  • Make prepared programs self-contained #1082: PR directly addresses this issue—makes Program self-contained with embedded ProgramInfo, threads AnalysisContext through analysis APIs, and decouples mutable cache state from immutable program facts.

Possibly related PRs

Suggested labels

codex, feature

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title directly summarizes the main change: removing thread_local_program_info and making Program self-contained by storing ProgramInfo.
Description check ✅ Passed Description clearly relates to the changeset, detailing the removal of thread-local state, threading of ProgramInfo/EbpfProgramType, cache refactoring, and Program self-containment.
Linked Issues check ✅ Passed PR fully addresses issue #1082 objectives: Program now stores ProgramInfo, AnalysisContext is derived from Program::info(), thread_local_program_info is removed, and mutable loader state is separated.
Out of Scope Changes check ✅ Passed All changes directly support the core objective: removing thread-local state and making Program self-contained. Helper threading, map cache relocation, and API signature updates are all required for this goal.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch explicit-context

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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db3c7a095f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/linux/linux_platform.cpp Outdated
Copy link
Copy Markdown

@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: 9

Caution

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

⚠️ Outside diff range comments (5)
src/printing.cpp (1)

106-127: 🧹 Nitpick | 🔵 Trivial

Avoid copying the entire line-info map per printer.

LineInfoPrinter stores line_info_map by value, so every DetailedPrinter and print_error construction copies all BTF line info. Store a const reference; prog outlives these short-lived printers.

Proposed change
 struct LineInfoPrinter {
     std::ostream& os;
-    const std::map<size_t, btf_line_info_t> line_info_map;
+    const std::map<size_t, btf_line_info_t>& line_info_map;
     bool print_line_info_enabled{};
     std::string previous_source_line;

Also applies to: 253-254

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/printing.cpp` around lines 106 - 127, LineInfoPrinter currently stores
line_info_map by value causing expensive copies; change its member to const
std::map<size_t, btf_line_info_t>& line_info_map and update its constructor to
accept that const reference, then update all call sites (e.g., DetailedPrinter
constructor and any print_error constructions) to pass prog.info().line_info by
reference instead of letting it be copied; keep other members
(print_line_info_enabled, previous_source_line) unchanged so behavior is
identical while avoiding the map copy.
src/ir/parse.cpp (1)

156-170: 🧹 Nitpick | 🔵 Trivial

Platform hardcoding in parse_instruction limits flexibility but does not impact current usage.

parse_instruction at line 170 hardcodes g_ebpf_platform_linux when calling make_call, despite accepting program_type as a parameter. This API inconsistency is documented in test code (ebpf_yaml.cpp:31–34), which explicitly works around it. The only real caller is test code that passes an empty program_type anyway, and the call at line 306 is inside the unused parse_program function (marked [[maybe_unused]]). If future code paths require platform flexibility during parsing, thread ebpf_platform_t through the signature; until then, this is design debt rather than a blocking issue.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ir/parse.cpp` around lines 156 - 170, The code in parse_instruction
hardcodes g_ebpf_platform_linux when calling make_call; update the call in the
parse_instruction function to use the platform information carried by the
EbpfProgramType parameter (program_type) instead of g_ebpf_platform_linux so
make_call(func, /*platform from program_type*/, program_type) is used; locate
the make_call invocation in parse_instruction and thread the program_type's
platform field (or otherwise expose an ebpf platform from program_type) into
that argument to remove the hardcoded g_ebpf_platform_linux.
src/main.cpp (1)

240-256: ⚠️ Potential issue | 🟠 Major

Synchronize verifier options before analysis.

compute_failure_slices receives ebpf_verifier_options, but the AnalysisResult is produced earlier by analyze(prog). Keep the analysis and slicing contexts on the same options; otherwise --failure-slice can request deps after analysis already ran without collecting them.

Proposed fix
-        auto result = analyze(prog);
+        thread_local_options = ebpf_verifier_options;
+        auto result = analyze(prog);
@@
-                const AnalysisContext context{prog.info(), ebpf_verifier_options, *prog.info().platform};
+                const AnalysisContext context{prog.info(), thread_local_options, *prog.info().platform};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.cpp` around lines 240 - 256, The analysis result is produced by
analyze(prog) before failure-slice uses ebpf_verifier_options, causing options
drift; ensure the verifier options are synchronized by running analysis with the
same options used for slicing: change the analyze call to accept/use
ebpf_verifier_options (e.g., analyze(prog, ebpf_verifier_options) or update
prog/info to reflect ebpf_verifier_options before calling analyze), so that the
AnalysisResult, compute_failure_slices, and AnalysisContext all operate with the
same ebpf_verifier_options values.
src/ir/cfg_builder.cpp (1)

647-721: ⚠️ Potential issue | 🟠 Major

Accept ProgramInfo by value instead of lvalue reference to make ownership transfer explicit.

The function signature accepts ProgramInfo& but moves from it (line 720). While no current call sites reuse the moved-from object, the API allows undefined behavior if a caller passes a reusable lvalue and accesses it afterward. Taking the parameter by value makes ownership semantics clear without requiring callers to write std::move.

Proposed change
-Program Program::from_sequence(const InstructionSeq& inst_seq, ProgramInfo& info,
+Program Program::from_sequence(const InstructionSeq& inst_seq, ProgramInfo info,
                                const ebpf_verifier_options_t& options) {

Update both the declaration in src/ir/program.hpp:58 and the definition in src/ir/cfg_builder.cpp:647.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ir/cfg_builder.cpp` around lines 647 - 721, Change Program::from_sequence
to take ProgramInfo by value (ProgramInfo info) instead of ProgramInfo& so
ownership is explicit; update the declaration in program.hpp and the definition
in cfg_builder.cpp accordingly, then keep the existing move into
builder.prog.m_info (std::move(info)) as the final transfer. Ensure all call
sites that currently pass an lvalue continue to compile (no extra std::move
required) and adjust any forwarding or validation that relied on a reference
(e.g., options.validate() and validate_instruction_feature_support(inst_seq,
info, ...) now receive the by-value info).
src/result.cpp (1)

619-643: ⚠️ Potential issue | 🟠 Major

Add a guard to bind the slice context to the Program being analyzed.

The function accepts separate prog and context parameters. While prog supplies the assertions and CFG, ebpf_domain_check(..., context) uses context.program_info. If a caller passes a context built from a different program's ProgramInfo, the failure-slice seeding replays checks against the wrong semantic environment. Add an assertion at the function entry to enforce that context.program_info matches prog.info():

Suggested fix
 std::vector<FailureSlice> AnalysisResult::compute_failure_slices(const Program& prog, const SliceParams params,
                                                                  const AnalysisContext& context) const {
+    assert(&context.program_info == &prog.info());
     const auto max_slices = params.max_slices;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/result.cpp` around lines 619 - 643, Add a guard in
AnalysisResult::compute_failure_slices to ensure the provided AnalysisContext is
bound to the same Program as the prog parameter: at the start of
compute_failure_slices check that context.program_info equals prog.info() (or
equivalent identity/comparison used by ProgramInfo) and assert or throw if they
differ; this prevents ebpf_domain_check and the assertion replay from using a
mismatched ProgramInfo when seeding failure slices.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/crab/ebpf_domain.cpp`:
- Around line 266-341: These helpers (get_map_type, get_map_inner_map_fd,
get_map_key_size, get_map_value_size, get_map_max_entries) currently call
context.platform.get_map_descriptor (which may throw if an fd is missing) while
iterating an abstract fd range; change them to detect missing descriptors and
return conservative fallbacks instead of allowing exceptions: for
get_map_type/get_map_inner_map_fd return std::nullopt when any fd in the range
has no descriptor, and for
get_map_key_size/get_map_value_size/get_map_max_entries return Interval::top()
when any descriptor is absent; implement this by replacing the direct call with
a safe lookup/check (or try/catch around
get_map_descriptor/get_map_descriptor_linux) and on failure immediately return
the conservative value.

In `@src/fwd_analyzer.cpp`:
- Around line 164-171: The two analyze(...) overloads currently build
AnalysisContext from thread_local_options, which makes a prepared Program
non-self-contained; change them to use the options stored on the Program itself
(e.g. use a getter like prog.prepared_options() or prog.preparation_options())
when constructing AnalysisContext (i.e., replace thread_local_options with the
Program's stored preparation options), or if no such field exists remove these
convenience overloads and require callers to call analyze(prog, entry_invariant,
AnalysisContext{...}) with an explicit AnalysisContext; update
constructors/usages around AnalysisContext and the Program API accordingly and
adjust callers/tests to pass the correct context if you remove the overloads.

In `@src/io/elf_loader.hpp`:
- Around line 25-26: The header declares create_map_crab(...) using
std::map<EquivalenceKey,int>& but does not include <map>, creating a
transitive-include dependency; open src/io/elf_loader.hpp and add `#include` <map>
to the top-of-file includes so std::map is defined for the create_map_crab
declaration (and any other uses of std::map in this header), keeping
EquivalenceKey and other includes unchanged.

In `@src/ir/program.hpp`:
- Around line 58-59: The declaration of Program::from_sequence currently takes
ProgramInfo& but the implementation in cfg_builder.cpp moves from the info
parameter (consumes it); change the API to take ProgramInfo by value or as
ProgramInfo&& to reflect consumption semantics, update the declaration of static
Program from_sequence(const InstructionSeq& inst_seq, ProgramInfo info, const
ebpf_verifier_options_t& options) (or use ProgramInfo&&) to match the
implementation, and update all call sites to pass std::move(raw_prog.info) (or
forward rvalues) so that callers explicitly transfer ownership; ensure the
implementation signature and all declarations match the chosen by-value or
rvalue-reference form.

In `@src/result.hpp`:
- Around line 156-162: The public overload of compute_failure_slices can receive
an AnalysisContext from a different Program which lets
context.options.total_stack_size() and other platform facts diverge from
prog.info(); fix by validating or deriving the program info: at the start of
compute_failure_slices(const Program& prog, const AnalysisContext& context) add
a runtime assertion that the context was built from the same program (e.g.,
assert(context.program_info == prog.info()) or equivalent accessor), and update
the header comment to state the precondition; alternatively, replace usage of
context.program_info inside the function by deriving ProgramInfo from
prog.info() and constructing/using a local AnalysisContext tied to prog to
eliminate the mismatch risk.

In `@src/test/ebpf_yaml.cpp`:
- Around line 420-421: The test constructs an explicit AnalysisContext named
context but then calls analyze(prog, test_case.assumed_pre_invariant), which
uses the thread-local options; change the call to use the overload that accepts
the context so analysis runs with that context (i.e., call analyze(prog,
context, test_case.assumed_pre_invariant) or the appropriate analyze(prog,
context, ...) overload) ensuring AnalysisContext context is passed into the
analyze invocation so result and subsequent observation checks use the same
environment.
- Line 297: The test is calling parse_instruction with an empty program type
(passing `{}`), which yields a null context_descriptor and causes
program-type-required helpers to be rejected; update raw_cfg_to_instruction_seq
to accept and thread an EbpfProgramType parameter and then pass that
EbpfProgramType through to parse_instruction so parse_instruction receives the
correct program type (not `{}`) when constructing Instruction instances; adjust
raw_cfg_to_instruction_seq signature/usages and the call site that currently
does const Instruction& ins = parse_instruction(line, label_name_to_label, {});
to supply the threaded EbpfProgramType so helpers resolve correctly.

In `@src/test/test_failure_slice.cpp`:
- Around line 256-260: The test uses elf.get_programs("xdp").back() without
asserting that the returned vector is non-empty, which can cause a crash outside
Catch2; update the test to first capture the vector (e.g., auto progs =
elf.get_programs("xdp")), assert its non-emptiness with REQUIRE(progs.size() >
0) (or REQUIRE_FALSE(progs.empty())), then use progs.back() to obtain raw_prog
before calling unmarshal/Program::from_sequence; ensure the check happens before
accessing raw_prog and before any use of InstructionSeq.
- Around line 30-33: Set the intended thread-local analysis options before
running analysis: assign or install the local options (the one with
collect_instruction_deps enabled) into thread_local_options prior to calling
analyze(prog) so analyze builds results with instruction dependencies; then
construct the AnalysisContext from options and call
result.compute_failure_slices(prog, context) as before (affects the block around
analyze(prog), the const AnalysisContext{...} construction, and
compute_failure_slices calls).

---

Outside diff comments:
In `@src/ir/cfg_builder.cpp`:
- Around line 647-721: Change Program::from_sequence to take ProgramInfo by
value (ProgramInfo info) instead of ProgramInfo& so ownership is explicit;
update the declaration in program.hpp and the definition in cfg_builder.cpp
accordingly, then keep the existing move into builder.prog.m_info
(std::move(info)) as the final transfer. Ensure all call sites that currently
pass an lvalue continue to compile (no extra std::move required) and adjust any
forwarding or validation that relied on a reference (e.g., options.validate()
and validate_instruction_feature_support(inst_seq, info, ...) now receive the
by-value info).

In `@src/ir/parse.cpp`:
- Around line 156-170: The code in parse_instruction hardcodes
g_ebpf_platform_linux when calling make_call; update the call in the
parse_instruction function to use the platform information carried by the
EbpfProgramType parameter (program_type) instead of g_ebpf_platform_linux so
make_call(func, /*platform from program_type*/, program_type) is used; locate
the make_call invocation in parse_instruction and thread the program_type's
platform field (or otherwise expose an ebpf platform from program_type) into
that argument to remove the hardcoded g_ebpf_platform_linux.

In `@src/main.cpp`:
- Around line 240-256: The analysis result is produced by analyze(prog) before
failure-slice uses ebpf_verifier_options, causing options drift; ensure the
verifier options are synchronized by running analysis with the same options used
for slicing: change the analyze call to accept/use ebpf_verifier_options (e.g.,
analyze(prog, ebpf_verifier_options) or update prog/info to reflect
ebpf_verifier_options before calling analyze), so that the AnalysisResult,
compute_failure_slices, and AnalysisContext all operate with the same
ebpf_verifier_options values.

In `@src/printing.cpp`:
- Around line 106-127: LineInfoPrinter currently stores line_info_map by value
causing expensive copies; change its member to const std::map<size_t,
btf_line_info_t>& line_info_map and update its constructor to accept that const
reference, then update all call sites (e.g., DetailedPrinter constructor and any
print_error constructions) to pass prog.info().line_info by reference instead of
letting it be copied; keep other members (print_line_info_enabled,
previous_source_line) unchanged so behavior is identical while avoiding the map
copy.

In `@src/result.cpp`:
- Around line 619-643: Add a guard in AnalysisResult::compute_failure_slices to
ensure the provided AnalysisContext is bound to the same Program as the prog
parameter: at the start of compute_failure_slices check that
context.program_info equals prog.info() (or equivalent identity/comparison used
by ProgramInfo) and assert or throw if they differ; this prevents
ebpf_domain_check and the assertion replay from using a mismatched ProgramInfo
when seeding failure slices.
🪄 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

Run ID: 7a9807b4-bd5c-415d-a026-e115bce15491

📥 Commits

Reviewing files that changed from the base of the PR and between 3ae3084 and db3c7a0.

📒 Files selected for processing (28)
  • src/analysis_context.hpp
  • src/crab/ebpf_checker.cpp
  • src/crab/ebpf_domain.cpp
  • src/crab/ebpf_domain.hpp
  • src/crab/ebpf_transformer.cpp
  • src/fwd_analyzer.cpp
  • src/io/elf_loader.cpp
  • src/io/elf_loader.hpp
  • src/ir/cfg_builder.cpp
  • src/ir/parse.cpp
  • src/ir/parse.hpp
  • src/ir/program.hpp
  • src/ir/unmarshal.cpp
  • src/ir/unmarshal.hpp
  • src/linux/gpl/spec_prototypes.cpp
  • src/linux/linux_platform.cpp
  • src/linux/linux_platform.hpp
  • src/main.cpp
  • src/platform.hpp
  • src/printing.cpp
  • src/result.cpp
  • src/result.hpp
  • src/spec/type_descriptors.hpp
  • src/test/ebpf_yaml.cpp
  • src/test/test_failure_slice.cpp
  • src/test/test_platform_tables.cpp
  • src/test/test_verify.hpp
  • src/test/test_verify_multithreading.cpp
💤 Files with no reviewable changes (2)
  • src/analysis_context.hpp
  • src/spec/type_descriptors.hpp

Comment thread src/crab/ebpf_domain.cpp
Comment thread src/fwd_analyzer.cpp Outdated
Comment thread src/io/elf_loader.hpp Outdated
Comment thread src/ir/program.hpp Outdated
Comment thread src/result.hpp
Comment thread src/test/ebpf_yaml.cpp
Comment thread src/test/ebpf_yaml.cpp Outdated
Comment thread src/test/test_failure_slice.cpp Outdated
Comment thread src/test/test_failure_slice.cpp Outdated
elazarg and others added 2 commits April 19, 2026 01:18
Replace per-section mock map cache with allocate_mock_map_fd, a pure
function that deduplicates against the accumulated map_descriptors
vector. This fixes FD collisions when an ELF has multiple legacy map
sections, since each parse_maps_section call now sees all previously
assigned FDs. Remove create_map_crab (now unused).

Restore const ProgramInfo& on Program::from_sequence to avoid silently
consuming the caller's ProgramInfo via move. The function copies into
a local, mutates callback targets there, and moves into Program.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Elazar Gershuni <elazarg@gmail.com>
Add analyze(prog, options) and verify(prog, options) so callers can
pass options explicitly without constructing AnalysisContext. The
thread_local_options shims delegate to these.

Fix test_failure_slice and ebpf_yaml to pass options explicitly to
analyze instead of relying on thread_local_options, ensuring
collect_instruction_deps and other test-specific options take effect.

Add missing REQUIRE size check in test_failure_slice before .back().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Elazar Gershuni <elazarg@gmail.com>
@elazarg elazarg merged commit 4465b3f into main Apr 18, 2026
16 checks passed
@elazarg elazarg deleted the explicit-context branch April 18, 2026 23:46
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.

Make prepared programs self-contained

1 participant