Setting a Proxy as its own prototype creates a cycle in the [[GetPrototypeOf]] chain. Any property lookup on the object enters ecma_proxy_object_get → ecma_op_object_get_with_receiver → ecma_proxy_object_get without bound. JerryScript has no prototype-cycle guard and no recursive stack-depth check in the Proxy [[Get]] path.
Trigger (minimal):
var a = new Proxy({length: 2}, {});
a.__proto__ = a;
a[1]; // or Array.prototype.forEach.call(a, ()=>{})
Crash type: stack-overflow (SIGABRT / rc=1)
Component: jerry-core/ecma/operations/ecma-proxy-object.c, ecma_proxy_object_get
JerryScript revision
git hash b706935 on master branch
Build platform
Ubuntu 24.04.4 LTS (Linux 6.8.0-106-generic x86_64)
Build steps
tools/build.py --builddir build-asan --build-type Debug \
--compile-flag=-fsanitize=address \
--compile-flag=-fno-omit-frame-pointer \
--compile-flag=-g \
--linker-flag=-fsanitize=address
Test case
echo "var a=new Proxy({length:2},{}); a.__proto__=a; a[1];" \
| ASAN_OPTIONS=halt_on_error=0 build-asan/bin/jerry -
Output
==ERROR: AddressSanitizer: stack-overflow on address 0x7ffc0d0e8ff8
(pc 0x...bd736195 bp 0x7ffc0d0e9040 sp 0x7ffc0d0e8ff0 T0)
Backtrace
#0 __asan_stack_malloc_0 [asan_fake_stack.cpp:273]
#7 ecma_builtin_try_to_instantiate_property
ecma/builtin-objects/ecma-builtins.c:886
#8 ecma_op_object_find_own ecma-objects.c:652
#9 ecma_op_object_get_with_receiver ecma-objects.c:819
#10 ecma_op_object_get ecma-objects.c:788
#11 ecma_op_get_method ecma-objects.c:1018
#12 ecma_op_get_method_by_magic_id ecma-objects.c:1081
#13 ecma_validate_proxy_object ecma-proxy-object.c:248
#14 ecma_proxy_object_get ecma-proxy-object.c:1151
#15 ecma_op_object_get_with_receiver ecma-objects.c:815 ┐
#16 ecma_proxy_object_get ecma-proxy-object.c:1163 │ repeats
#17 ecma_op_object_get_with_receiver ecma-objects.c:815 │ ~120×
#18 ecma_proxy_object_get ecma-proxy-object.c:1163 ┘
... [truncated — frames #15–#251 are the same alternating pair]
SUMMARY: AddressSanitizer: stack-overflow
ecma-proxy-object.c:1163 in ecma_proxy_object_get
The entry into the cycle is at ecma_validate_proxy_object (ecma-proxy-object.c:248), which calls ecma_op_get_method_by_magic_id to look up the handler trap on the Proxy. Because the Proxy's [[Prototype]] is itself, that lookup re-enters ecma_proxy_object_get → ecma_op_object_get_with_receiver indefinitely.
Expected behavior
JerryScript should detect the prototype cycle and throw a RangeError , rather than overflowing the native stack. The fix should either reject circular prototype chains in Reflect.setPrototypeOf / Object.setPrototypeOf, or add a recursion-depth counter to ecma_op_object_get_with_receiver.
Setting a Proxy as its own prototype creates a cycle in the
[[GetPrototypeOf]]chain. Any property lookup on the object entersecma_proxy_object_get→ecma_op_object_get_with_receiver→ecma_proxy_object_getwithout bound. JerryScript has no prototype-cycle guard and no recursive stack-depth check in the Proxy[[Get]]path.Trigger (minimal):
Crash type: stack-overflow (SIGABRT / rc=1)
Component:
jerry-core/ecma/operations/ecma-proxy-object.c,ecma_proxy_object_getJerryScript revision
git hash b706935 on master branch
Build platform
Ubuntu 24.04.4 LTS (Linux 6.8.0-106-generic x86_64)
Build steps
Test case
Output
Backtrace
The entry into the cycle is at
ecma_validate_proxy_object(ecma-proxy-object.c:248), which callsecma_op_get_method_by_magic_idto look up the handler trap on the Proxy. Because the Proxy's[[Prototype]]is itself, that lookup re-entersecma_proxy_object_get→ecma_op_object_get_with_receiverindefinitely.Expected behavior
JerryScript should detect the prototype cycle and throw a
RangeError, rather than overflowing the native stack. The fix should either reject circular prototype chains inReflect.setPrototypeOf/Object.setPrototypeOf, or add a recursion-depth counter toecma_op_object_get_with_receiver.