Fix rewrite_extern_constant_load crash on values exceeding int32 range#1054
Fix rewrite_extern_constant_load crash on values exceeding int32 range#1054
Conversation
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>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughAdds 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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment Tip Migrating from UI to YAML configuration.Use the |
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
src/io/elf_extern_resolve.cppsrc/test/test_elf_loader.cpp
Signed-off-by: Michael Agun <danielagun@microsoft.com>
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
src/io/elf_extern_resolve.cppsrc/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) { |
There was a problem hiding this comment.
can you add an appropriate abstraction to num_safety.hpp?
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
Tests