Skip to content

Fix rewrite_extern_constant_load crash on values exceeding int32 range#1054

Open
mikeagun wants to merge 3 commits intovbpf:mainfrom
mikeagun:mov-imm-fix
Open

Fix rewrite_extern_constant_load crash on values exceeding int32 range#1054
mikeagun wants to merge 3 commits intovbpf:mainfrom
mikeagun:mov-imm-fix

Conversation

@mikeagun
Copy link
Contributor

@mikeagun mikeagun commented Mar 13, 2026

Fixes #1053

rewrite_extern_constant_load() rewrites LDDW + LDX instruction pairs into MOV imm when resolving .kconfig extern constants.
It called gsl::narrow<int32_t>(narrowed_value) to store the resolved value into the BPF instruction's 32-bit imm field.

BPF MOV imm has a 32-bit immediate that is sign-extended to 64 bits at runtime.
When the resolved constant doesn't fit in that range (e.g., 0x80000000 or larger as uint64_t), gsl::narrow throws narrowing_error.
This crashes the verifier on any ELF containing a LDDW + LDX DW extern-constant pattern with a large value.

Fix: Check whether the value fits in a sign-extended int32 before rewriting. If it doesn't fit, return false so the caller falls back to the original LDDW + LDX instruction sequence. Replace gsl::narrow with static_cast (now safe — guarded by the range check).

Test: Add regression test with 6 sections covering boundary values (INT32_MAX, 0x80000000, 0x100000000, 0xFFFFFFFF, 0xDEADBEEFCAFEBABE).
Confirms no exception is thrown and the function correctly returns false for out-of-range values.

Summary by CodeRabbit

  • Bug Fixes

    • Added a runtime safety check to ensure constants fit the target instruction encoding; values that cannot be safely represented are rejected and a fallback path is used to prevent incorrect instruction rewrites.
  • Tests

    • Added regression tests covering boundary, sign-extension, and overflow cases for constant-loading, verifying correct acceptance, graceful bail-out, and no unintended program-state changes.

BPF MOV imm has a 32-bit immediate field that is sign-extended to 64 bits.
When rewrite_extern_constant_load() rewrites a LDDW+LDX pair into a MOV
imm, it called gsl::narrow<int32_t>(narrowed_value) which throws
narrowing_error if the resolved 64-bit constant exceeds INT32_MAX (e.g.,
0x80000000 or larger).

Fix: check whether the value fits in a sign-extended int32 before
rewriting. If it doesn't fit, return false so the caller falls back to the
original LDDW+LDX instruction sequence.

Add regression test covering boundary values.

Signed-off-by: Michael Agun <danielagun@microsoft.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e1fefe11-5a62-42d1-8a8e-6472265c4de3

📥 Commits

Reviewing files that changed from the base of the PR and between 3aa4fab and 2823dab.

📒 Files selected for processing (1)
  • src/test/test_elf_loader.cpp

Walkthrough

Adds a sign-extension safety check when rewriting 64-bit extern constant loads into a 32-bit BPF MOV immediate, and a regression test that verifies successful rewrites for in-range values and graceful bail-out (return false, no mutation) for out-of-range values.

Changes

Cohort / File(s) Summary
Constant-load rewrite
src/io/elf_extern_resolve.cpp
Adds #include <cstdint> and a runtime check ensuring a narrowed value fits in a sign-extended int32 before writing the MOV immediate. Replaces an unconditional narrowing with validation and a local int32_t truncated variable; returns false to fall back when out-of-range.
Regression test
src/test/test_elf_loader.cpp
Adds TEST_CASE("rewrite_extern_constant_load bails out on values exceeding int32 range", "[elf][hardening]") covering in-range rewrites (e.g., 42, INT32_MAX) and verifying bail-out (returns false, no mutation) for out-of-range 64-bit values (e.g., 0x80000000, 0x100000000, 0xFFFFFFFF, 0xDEADBEEFCAFEBABE).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • elazarg
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and concisely describes the main fix: preventing crashes when values exceed int32 range in rewrite_extern_constant_load.
Linked Issues check ✅ Passed The PR fully implements the fix described in issue #1053: it adds a sign-extension range check before narrowing to int32_t, returns false when values exceed int32 range, and includes comprehensive regression tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the int32 range check in rewrite_extern_constant_load and adding regression tests; no unrelated modifications present.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

Migrating from UI to YAML configuration.

Use the @coderabbitai configuration command in a PR comment to get a dump of all your UI settings in YAML format. You can then edit this YAML file and upload it to the root of your repository to configure CodeRabbit programmatically.

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: 2

🤖 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/io/elf_extern_resolve.cpp`:
- Around line 91-93: Replace the current bounds check that compares signed_value
to int32_t limits with an explicit sign-extension round-trip check: compute
int32_t truncated = static_cast<int32_t>(narrowed_value) and then compare
static_cast<int64_t>(truncated) to signed_value (or the int64_t view of
narrowed_value); return false if they differ. Update the comment to state we
perform an explicit sign-extension round-trip for clarity (rather than claiming
implementation-defined behavior), and reference the variables narrowed_value and
signed_value in the code change.

In `@src/test/test_elf_loader.cpp`:
- Around line 547-574: The tests that call rewrite_extern_constant_load (in
sections for 0x80000000, 0x100000000, 0xFFFFFFFF, and 0xDEADBEEFCAFEBABE) must
also assert the instruction list is unchanged when the function bails out; for
each SECTION, make a deep copy of the instructions produced by make_instructions
(e.g., saved_insts = insts or a cloned copy) before calling
rewrite_extern_constant_load, then after the CHECK_NOTHROW and CHECK_FALSE calls
assert equality between the post-call inst list and the saved copy (use the
existing instruction equality comparator used elsewhere in tests) to ensure no
mutation occurred by rewrite_extern_constant_load.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0da84078-18fb-4f7a-b065-eefb0b28ff45

📥 Commits

Reviewing files that changed from the base of the PR and between 4790e81 and f64c1dd.

📒 Files selected for processing (2)
  • src/io/elf_extern_resolve.cpp
  • src/test/test_elf_loader.cpp

Signed-off-by: Michael Agun <danielagun@microsoft.com>
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: 2

🤖 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/test/test_elf_loader.cpp`:
- Around line 556-570: Add a positive success case for the sign-extension path:
create a SECTION that uses make_instructions() and calls
rewrite_extern_constant_load(insts, 0, 0xFFFFFFFFFFFFFFFFULL) and assert it
returns true (e.g., via REQUIRE) to verify that -1 (all-ones uint64) survives
the sign-extension round-trip and is rewritable; place this alongside the
existing SECTIONs and reference the helper check functions (make_instructions,
rewrite_extern_constant_load) used in the surrounding tests.
- Around line 538-544: The bail-out test lambda check_bailout_preserves_program
redundantly calls rewrite_extern_constant_load twice on the same insts; change
it to call rewrite_extern_constant_load(insts, 0, value) once, store the
returned bool, assert the call did not throw and that the returned value is
false (e.g., CHECK_NOTHROW and then CHECK(result == false) or
CHECK_FALSE(result)), and finally CHECK(insts == original) to verify no
mutation; update the lambda around rewrite_extern_constant_load, insts, original
to use the single-call pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c45fd28a-9fb3-4f12-8c6f-6a8391914be5

📥 Commits

Reviewing files that changed from the base of the PR and between f64c1dd and 3aa4fab.

📒 Files selected for processing (2)
  • src/io/elf_extern_resolve.cpp
  • src/test/test_elf_loader.cpp

…uccess case

Signed-off-by: Michael Agun <danielagun@microsoft.com>
// sign-extension round-trip; the caller will fall back to the original
// LDDW+LDX instruction sequence.
const auto truncated = static_cast<int32_t>(narrowed_value);
if (static_cast<uint64_t>(static_cast<int64_t>(truncated)) != narrowed_value) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you add an appropriate abstraction to num_safety.hpp?

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.

rewrite_extern_constant_load throws gsl::narrowing_error on values exceeding INT32_MAX

2 participants