Skip to content

fix(runtime): fetch string_from_header must reject handle-band ids (EXC_BAD_ACCESS)#5560

Merged
proggeramlug merged 1 commit into
mainfrom
fix/fetch-header-handle-band
Jun 23, 2026
Merged

fix(runtime): fetch string_from_header must reject handle-band ids (EXC_BAD_ACCESS)#5560
proggeramlug merged 1 commit into
mainfrom
fix/fetch-header-handle-band

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Problem

In the fetch implementation, string_from_header dereferenced its argument as a StringHeader pointer without checking that it is a real heap pointer. When the value is a handle-band id (a fetch Headers/Request/Response/Blob handle — a small registry id in the fetch handle band 0x40000..=0xFFFFF, not a StringHeader*), the deref reads unmapped memory → EXC_BAD_ACCESS (SIGSEGV).

This crashed a real bundle's doctor/network paths (which build/inspect fetch headers); non-deterministic, depending on what's mapped near the small id.

Fix

string_from_header now treats a value whose raw payload falls in the handle band (<= addr_class::HANDLE_BAND_MAX / within FETCH_HANDLE_BAND_START..) as "not a string" and returns the not-a-string result instead of dereferencing the id as a StringHeader pointer. Same family as the inline .length handle-band guard and the js_get_iterator near-null guard (#5549) — fast/native paths must reject handle-band ids before treating them as heap pointers.

Tests

string_from_header_rejects_handle_band_ids (a fetch handle id at the band start, mid-band, and HANDLE_BAND_MAX - 1 are all rejected without deref). cargo test -p perry-runtime --lib: 1072 passed.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed a crash that occurred when non-string arguments were passed to URL operations.
  • Tests

    • Added a regression test to verify crash prevention for invalid input types.

…octor/mcp SIGSEGV)

The doctor / mcp-list startup segfault (EXC_BAD_ACCESS at the fetch-handle
address, e.g. 0x40005/0x40006) root-causes to perry_stdlib::fetch::
string_from_header dereferencing a native registry id as a *StringHeader.

lldb on a debug bundle (doctor):
  frame #0 perry_stdlib::fetch::string_from_header  ldr w9,[x1,#0x4]  x1=0x40002
  frame #1 js_fetch_with_options
  frame #2 global_this_fetch_thunk

fetch() was called with a non-string first argument — a Request/Headers object
(a Web Fetch handle, registry id in [0x40000, 0xE0000)). The codegen passes the
bare handle id into the url_ptr *StringHeader slot of
js_fetch_with_options(url_ptr, method_ptr, body_ptr, headers_json_ptr), and
string_from_header reads (*ptr).byte_len at id+4 -> deref of unmapped low
address -> SIGSEGV. Its only guard was `ptr < 0x1000` (the TAG_UNDEFINED 0x1
remnant); a 0x40002 handle clears that floor.

Non-deterministic for mcp list because whether the id+4 page is mapped at the
call depends on heap/page layout: when mapped, byte_len reads garbage and fetch
proceeds to the intended error (clean rc=1); when unmapped, SIGSEGV.

Fix: reject the whole handle band (is_handle_band, < 0x100000) in
string_from_header so a native handle is treated as 'not a string' (None)
instead of dereferenced — same robustness pattern as the inline .length /
IC-miss / class-field-guard band gates. Adds a regression test.

This is the doctor/mcp wall and is INDEPENDENT of the alias-new fix
(a9e41e163) and of the inline-.length handle-band fix (175fda498) — both touch
different code paths; a9e41e163 merely advanced doctor far enough to reach this
fetch() call.
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 674aa9cc-e3a0-4d9d-a200-2bbeb93ff5a3

📥 Commits

Reviewing files that changed from the base of the PR and between d8be3dd and d598ab0.

📒 Files selected for processing (1)
  • crates/perry-stdlib/src/fetch/mod.rs

📝 Walkthrough

Walkthrough

string_from_header in the fetch module gains an additional pointer-validity check using addr_class::is_handle_band, returning None early when a fetch handle-band registry id is passed as a *const StringHeader. A regression test iterates over handle-band ids and asserts None is returned for each.

Changes

Handle-band guard in string_from_header

Layer / File(s) Summary
Handle-band guard and regression test
crates/perry-stdlib/src/fetch/mod.rs
string_from_header calls addr_class::is_handle_band on the pointer cast to usize and returns None before any dereference. A new unit test string_from_header_rejects_handle_band_ids iterates over handle-band ids and asserts None is returned for each.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • PerryTS/perry#5175: Routes res.setHeaders through a JSON path to avoid treating Headers handle IDs as dereferenceable objects — the same class of handle-band misidentification this PR fixes in string_from_header.
  • PerryTS/perry#5549: Also calls addr_class::is_handle_band to bail out early when handle-band tagged addresses appear where valid inputs are expected, using the same detection primitive added here.

Poem

🐇 Hoppin' through the pointer fields, ears perked up with care,
A handle-band in disguise once caused a dreadful scare!
Now is_handle_band spots the ruse before a deref flies,
Returns a tidy None instead beneath the Rust-safe skies.
No SIGSEGV shall pass — this warren's fortified! 🛡️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description includes all essential sections: problem statement, fix explanation, and test details. However, it does not follow the repository's required template structure with explicit Summary/Changes/Related issue/Test plan sections and checkboxes. Restructure the description to match the provided template format, including Summary, Changes (bullet list), Related issue, Test plan with checkboxes, and Checklist sections.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically identifies the bug being fixed: the fetch string_from_header function must reject handle-band IDs to prevent EXC_BAD_ACCESS crashes, which directly matches the core change in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fetch-header-handle-band

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

@proggeramlug proggeramlug merged commit 02be9dd into main Jun 23, 2026
14 of 15 checks passed
@proggeramlug proggeramlug deleted the fix/fetch-header-handle-band branch June 23, 2026 03:00
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