fix(runtime): js_get_iterator must reject a handle-band near-null pointer as non-iterable#5549
Conversation
…able A pointer-tagged value whose payload lies in the small-handle band (e.g. a near-null POINTER_TAG|1) is not a dereferenceable heap object. js_get_iterator previously read [Symbol.iterator] off it, found nothing, and returned the bogus value UNCHANGED as its own iterator; the lazy for-of then called .next() on it, got undefined, and threw a misleading late 'Iterator result is not an object' far from the real fault. Throw the correct 'is not iterable' at the point of iteration instead, and stop manufacturing a bogus iterator from a non-object. This clears a corpus wall where a deep async config-resolution chain feeds a corrupted .errors value (POINTER_TAG|1) into a for-of: the binary now advances past that iterator wall to the sibling WeakMap-key wall (same upstream value-wiring corruption, separate manifestation). 1072 runtime lib tests pass.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughIn ChangesIterator Handle-Band Guard
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Possibly related PRs
Poem
🚥 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 docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/perry-runtime/src/symbol.rs`:
- Around line 2263-2267: The guard that checks is_handle_band(raw) and calls
throw_value_not_iterable() is incorrectly blocking iteration dispatch for
handle-backed iterable values like fetch Headers. Remove the entire if statement
that checks is_handle_band(raw) and calls throw_value_not_iterable(), allowing
the code to continue to the iterator resolver path. This will permit
Symbol.iterator dispatch to work correctly for small handles as intended by the
code at lines 1724-1730.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 711406a6-0113-4fd4-9ef9-b072086d9970
📒 Files selected for processing (1)
crates/perry-runtime/src/symbol.rs
…okup The initial guard ran before the `[Symbol.iterator]` lookup and used is_handle_band (`addr < 0x100000`), which also covers the Web Fetch band [0x40000, 0xE0000) (Headers/Request/Response), the zlib band, and the revocable-Proxy band. Those are genuinely iterable handle-backed values that resolve @@iterator via the small-handle dispatch in js_object_get_symbol_property, so the early guard threw "not iterable" before that dispatch ran — breaking `for (const [k,v] of headers)`, `[...headers]`, and the addr_class_handle_bands integration test. Move the rejection to the no-method fallthrough, just before `return val_f64`: iterable handles resolve @@iterator and return early; only a handle-band pointer that resolved no iterator method (a corrupt near-null reference) now throws, instead of being returned as its own bogus iterator. Addresses CodeRabbit review feedback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H689Q1ongNpQmg9HdYQayS
Problem
for…of(and other iteration) over a pointer-tagged value whose payload is a small handle-band address (e.g.POINTER_TAG | 1→ a "pointer" to address 1) did not throw.js_get_iteratorread[Symbol.iterator]off the near-null address, found nothing, and returned the bogus value as its own iterator;.next()then yieldedundefined, producing a misleading, far-removedTypeError: Iterator result is not an objectinstead of a correct "is not iterable" at the point of iteration.js_get_iterator(undefined)already throws correctly; the gap was specifically the non-dereferenceable handle-band pointer.Fix
In
js_get_iterator(after the primitive check, before the[Symbol.iterator]lookup), reject a pointer-tagged value whose raw address is in the handle band (addr_class::is_handle_band) with the normal "is not iterable" TypeError. This stops the runtime manufacturing an iterator from a near-null pointer and surfaces the error at the correct location.Verification
cargo test -p perry-runtime --lib: 1072 passed. Found while driving a large bundle whose deep config path produced such a value through an upstream binding-wiring defect; with this guard the iteration throws cleanly at the right place instead of much later. (No isolated TS repro: the trigger is an internal mis-tagged value, not something source code can directly construct; the guard is defensive and spec-aligned regardless.)Summary by CodeRabbit