Skip to content

GC: the catch (e) parameter slot is reserved but never bound — the exception has no root across the catch body #7209

Description

@proggeramlug

Summary

The catch (e) parameter has a shadow slot reserved for it and never bound, so the exception object — almost always a heap Error — has no root at all for the whole catch body.

This is the sharpest single finding of the #7202 bare-alloca enumeration (PR #7207), because the frame is already sized for it: the fix is a bind, not a reservation.

Evidence

  • collectors/pointer_locals.rs:983-990 does assign_slot for the catch parameter (typed Any), so js_shadow_frame_enter's count includes it.
  • stmt/try_stmt.rs:114 allocates the parameter slot with alloca_entry and never emits js_shadow_slot_bind, so active[idx] stays false forever and the collector never dereferences it.
  • stmt/try_stmt.rs:107 calls js_clear_exception() — dropping the runtime's own reference — before lower_stmts(&clause.body) runs arbitrary allocating user code.

So between the clear and the first re-read, the only thing referring to the exception is an alloca nothing scans. Under a non-moving collector the conservative native-stack scan finds it; with PERRY_CONSERVATIVE_STACK_SCAN=off (the cons_scan_off matrix arm, and the direction the collector is moving) it is a live use-after-free, and under an evacuating minor it is a stale address.

Fix shape

Bind the existing reserved index — js_shadow_slot_bind(idx, catch_slot) right after the store — or route it through expr::scalar_slot_root::root_entry_alloca (generalized in #7207), whose contract also covers the undefined seed.

A reproducer: try { throw new Error(...) } catch (e) { churn(); use(e.message) } in a loop, under PERRY_GC_MOVING_LOOP_POLLS=1 with PERRY_CONSERVATIVE_STACK_SCAN=off.

Refs #7202, #7154, #7161, #7207, #6968.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions