Skip to content

Commit d8be3dd

Browse files
proggeramlugRalph Küpperclaude
authored
fix(runtime): js_get_iterator must reject a handle-band near-null pointer as non-iterable (#5549)
* fix(runtime): js_get_iterator rejects handle-band pointer as not-iterable 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. * fix(runtime): move handle-band iterator rejection after @@iterator lookup 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 --------- Co-authored-by: Ralph Küpper <ralph2@skelpo.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 981b324 commit d8be3dd

1 file changed

Lines changed: 20 additions & 0 deletions

File tree

crates/perry-runtime/src/symbol.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,26 @@ pub extern "C" fn js_get_iterator(val_f64: f64) -> f64 {
23112311
}
23122312
}
23132313
}
2314+
// We reach here only when NO `[Symbol.iterator]` method resolved. A
2315+
// pointer-tagged value whose payload lies in the small-handle band
2316+
// (`< HANDLE_BAND_MAX`, e.g. a near-null `POINTER_TAG | 1`) is NOT a
2317+
// dereferenceable heap object, and with no iterator method it cannot be
2318+
// iterable. Returning it `val_f64` below would manufacture the bogus value
2319+
// as its own "iterator"; the lazy for-of then calls `.next()` on it, gets
2320+
// `undefined`, and throws a misleading late "Iterator result is not an
2321+
// object" far from the real fault. Throw the correct "not iterable" here
2322+
// instead. Genuinely-iterable handle-backed values (fetch `Headers`,
2323+
// proxies, …) resolve their `@@iterator` via the small-handle dispatch in
2324+
// `js_object_get_symbol_property` above and already returned — only a
2325+
// corrupt/non-iterable handle reaches this point.
2326+
{
2327+
let jsv = crate::value::JSValue::from_bits(val_f64.to_bits());
2328+
if jsv.is_pointer()
2329+
&& crate::value::addr_class::is_handle_band(jsv.as_pointer::<u8>() as usize)
2330+
{
2331+
throw_value_not_iterable();
2332+
}
2333+
}
23142334
val_f64
23152335
}
23162336

0 commit comments

Comments
 (0)