While fixing a CodeRabbit finding on #6650 (unrooted NaN-boxed operands held across GC-capable coercions in the NEW js_dynamic_ushr), the same latent pattern shows in the PRE-EXISTING dynamic operators of crates/perry-runtime/src/value/dynamic_arith.rs:
pub unsafe extern "C" fn js_dynamic_pow(a: f64, b: f64) -> f64 {
let a = to_numeric(a); // can run user ToPrimitive → allocate → GC
let b = to_numeric(b); // raw `b` (heap pointer?) held unrooted across the above
...
to_numeric on an object operand runs user ToPrimitive/valueOf (can allocate and trigger GC/evacuation). Any operator with the to_numeric(a); to_numeric(b) prelude holds raw b — and the coerced a, when it is a BigInt pointer — unrooted across potentially-moving operations. The correct discipline exists in the same file (dynamic_bigint_binary_op: RuntimeHandleScope + root_nanbox_f64 + reload via get_nanbox_f64) and was applied to js_dynamic_ushr/throw_mix_bigint in #6650 (commit 169456b).
Ask: audit every exported js_dynamic_* (and any other f64-operand entry point in the file) for operands held across GC-capable calls; apply the rooting pattern; consider a repro via a valueOf that forces allocation pressure (the fast path at ~line 424 documents when the scope can legitimately be skipped — primitives only).
Low observed frequency (needs GC inside ToPrimitive with a pointer operand pending), but it is exactly the stale-pointer class behind past campaigns (#6471 etc.) — worth closing file-wide.
While fixing a CodeRabbit finding on #6650 (unrooted NaN-boxed operands held across GC-capable coercions in the NEW
js_dynamic_ushr), the same latent pattern shows in the PRE-EXISTING dynamic operators ofcrates/perry-runtime/src/value/dynamic_arith.rs:to_numericon an object operand runs userToPrimitive/valueOf(can allocate and trigger GC/evacuation). Any operator with theto_numeric(a); to_numeric(b)prelude holds rawb— and the coerceda, when it is a BigInt pointer — unrooted across potentially-moving operations. The correct discipline exists in the same file (dynamic_bigint_binary_op: RuntimeHandleScope + root_nanbox_f64 + reload via get_nanbox_f64) and was applied tojs_dynamic_ushr/throw_mix_bigintin #6650 (commit 169456b).Ask: audit every exported
js_dynamic_*(and any other f64-operand entry point in the file) for operands held across GC-capable calls; apply the rooting pattern; consider a repro via avalueOfthat forces allocation pressure (the fast path at ~line 424 documents when the scope can legitimately be skipped — primitives only).Low observed frequency (needs GC inside ToPrimitive with a pointer operand pending), but it is exactly the stale-pointer class behind past campaigns (#6471 etc.) — worth closing file-wide.