You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found by the package audit (compiling real npm packages from source instead of Perry's native bindings) on
Perry e6dcb62 (v0.5.1587), Linux x64. Reflect.apply(fn, thisArg, args) passes at most the first four elements of args; the rest are dropped without an error.
node main.ts
PERRY_NO_AUTO_OPTIMIZE=1 perry compile main.ts -o main && ./main
Expected (Node 26.5.1)
6
7:1,2,3,4,5,6,7
6
7:1,2,3,4,5,6,7
Actual (Perry)
4
4:1,2,3,4
6
7:1,2,3,4,5,6,7
Reflect.construct and Function.prototype.apply are correct; only Reflect.apply truncates.
Impact
Silent wrong results in any code that forwards argument lists through Reflect.apply — common in Proxy apply
handlers, decorators/wrappers, instrumentation and polyfills (Reflect.apply(target, thisArg, argumentsList) is the
canonical way to forward a call).
Notes
Cause (verified by reading the code): js_reflect_apply (crates/perry-runtime/src/proxy/reflect_misc.rs:99)
builds the full list with create_list_from_array_like, then calls call_with_this_and_args
(crates/perry-runtime/src/proxy.rs:902), whose dispatch is match args.len() { 0..=3 => js_closure_callN, _ => js_closure_call4(closure, a(0), a(1), a(2), a(3)) } — every
length ≥ 4 goes through the 4-argument call. It needs the variadic call path used by Function.prototype.apply.
The same _ => js_closure_call4 shape exists in call_trap (proxy.rs:720), but Proxy traps take at most four
arguments, so that site is not affected.
Found by the package audit (compiling real npm packages from source instead of Perry's native bindings) on
Perry e6dcb62 (v0.5.1587), Linux x64.
Reflect.apply(fn, thisArg, args)passes at most the first four elements ofargs; the rest are dropped without an error.Reproduction
main.ts:node main.ts PERRY_NO_AUTO_OPTIMIZE=1 perry compile main.ts -o main && ./mainExpected (Node 26.5.1)
Actual (Perry)
Reflect.constructandFunction.prototype.applyare correct; onlyReflect.applytruncates.Impact
Silent wrong results in any code that forwards argument lists through
Reflect.apply— common in Proxyapplyhandlers, decorators/wrappers, instrumentation and polyfills (
Reflect.apply(target, thisArg, argumentsList)is thecanonical way to forward a call).
Notes
js_reflect_apply(crates/perry-runtime/src/proxy/reflect_misc.rs:99)builds the full list with
create_list_from_array_like, then callscall_with_this_and_args(
crates/perry-runtime/src/proxy.rs:902), whose dispatch ismatch args.len() { 0..=3 => js_closure_callN, _ => js_closure_call4(closure, a(0), a(1), a(2), a(3)) }— everylength ≥ 4 goes through the 4-argument call. It needs the variadic call path used by
Function.prototype.apply._ => js_closure_call4shape exists incall_trap(proxy.rs:720), but Proxy traps take at most fourarguments, so that site is not affected.
apply/call/spread pass0for parameters 17+).