Skip to content

runtime: the js_string_coerce-as-property-key family has the same unrooted receiver/value gap as #6935 (defineProperty, descriptors, Reflect) #6943

Description

@proggeramlug

Follow-up to #6935 / #6941, which fixed the js_to_property_key sites. While sweeping for those I found the same shape in a second, adjacent family that #6941 deliberately left alone: entry points that use js_string_coerce(key) as the property-key coercion, without an earlier ToPropertyKey.

js_string_coerce on an object argument runs the user toString / valueOf, and allocates the stringified form for every primitive — so it is GC-capable and can evacuate exactly like js_to_property_key. Where the receiver (and, on the define/set paths, the descriptor or value) is a raw Rust local across it, the same corruption applies: a stale receiver drops the write onto a forwarding stub, a stale stored value plants a dangling pointer inside a live object.

Sites, all on main @ 0bb03e8, none of them preceded by a js_to_property_key call:

  • crates/perry-runtime/src/object/object_ops/define_property.rs:606, 790, 893Object.defineProperty(obj, key, desc); obj / obj_value and the descriptor object are held across js_string_coerce(key_value).
  • crates/perry-runtime/src/object/descriptors.rs:196, 271, 471, 615, 950
  • crates/perry-runtime/src/object/descriptor_state.rs:733
  • crates/perry-runtime/src/object/reflect_support.rs:59, 77, 110 — e.g. the typed-array and array arms hold obj / obj_addr across js_string_coerce(key) and then dereference them.
  • crates/perry-runtime/src/object/array_object_ops.rs:266
  • crates/perry-runtime/src/object/typed_array_define.rs:137
  • crates/perry-runtime/src/proxy.rs:1385

The fix is mechanical and identical to #6941: crate::gc::RuntimeHandleScope + root_heap_word_u64 / root_raw_mut_ptr / root_nanbox_f64 around the coercion, re-reading the receiver and any stored operand through their handles afterwards, with the property_key_coercion_is_inert fast path (added in #6941, object/property_key.rs) keeping the common already-a-heap-string key on the pre-fix path.

Two things worth deciding while doing it:

  1. Several of these are spec-level ToPropertyKey(P) steps (Object.defineProperty step 2, Reflect.*), so they arguably want js_to_property_key rather than a bare js_string_coerce — that would also fix Symbol-key handling on those paths. Worth checking against test262 before choosing.
  2. Verification is currently blocked the same way fix(runtime): root receivers and stored values across GC-capable property-key coercions (#6935) #6941's was — see gc: no reachable configuration exercises an evacuating minor with unpinned runtime locals — the #6655/#6935 bug class is untestable #6942.

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