Skip to content

js_class_method_bind: perry-stdlib's handle-property dispatch has the same interior-pointer bug in eight sub-dispatchers #8178

Description

@proggeramlug

Summary

#8133 named four js_class_method_bind sites binding a movable heap string's interior. Fixing it (#8177) surfaced six in perry-runtime — and the same defect systemically in perry-stdlib's handle-property dispatch, where eight sub-dispatchers capture the forwarded name pointer directly.

The contract being violated

js_class_method_bind(instance, name_ptr, name_len) stores the name pointer in the bound closure's capture, and dispatch_bound_method (closure/dispatch/bound.rs:12) re-reads it at call time. So the pointer must outlive the bind — it cannot point into a movable GC heap string, whose contents are unreachable once a collection moves it.

Where it still happens

Eight sub-dispatchers in perry-stdlib forward the caller's pointer straight into the bind:

  • sqlite — four sites
  • tls — two sites
  • emitter_als — two sites

Reachable from ordinary code that stores a method rather than calling it:

const f = db.run;        // sqlite
const g = emitter.on;    // emitter_als
const h = als.getStore;  // emitter_als

Also found on perry-runtime's primitive-receiver arm ((5).toString), which #8177 did not cover.

Why this will not show up by accident

#8177 established the reproduction conditions, and they are narrow:

  • A literal dec.decode lowers to rodata and never reaches these arms at all. Only a computed key does — const k = "dec" + "ode"; obj[k].
  • Pre-fix, the symptom was a silent wrong answer: decode=undefined followed by a throw, where node prints decode=hi. No instrument fired.
  • Under PERRY_GC_ZEAL=1 PERRY_GC_PROTECT_FROMSPACE=1 it became a SIGBUS reported as RETIRED FROM-SPACE … retired_by_minor=#0.

So a test that binds a literal name and calls it immediately will pass on broken code. Any fixture here needs a computed key and enough allocation to actually move the string; #8177's used ~400k allocations.

Suggested shape

#8177's fix is worth copying rather than re-deriving: it replaced the is_timer_handle_method_key predicate with a 'static-returning lookup and removed key_ptr/key_len from text_handle_property entirely. With no predicate and no pointer parameter, the bug is unwritable rather than merely fixed. The stdlib dispatchers can take the same treatment.

Related

#8133 (closed by #8177), #7747 (the first two sites), docs/src/internals/gc-rooting-invariant.md.

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