Skip to content

Harden ObjectWrapper iterator helpers against foreign and revoked receivers#2681

Merged
lahma merged 1 commit into
sebastienros:mainfrom
lahma:proxy-wrapper-iterator-hardening
Jul 14, 2026
Merged

Harden ObjectWrapper iterator helpers against foreign and revoked receivers#2681
lahma merged 1 commit into
sebastienros:mainfrom
lahma:proxy-wrapper-iterator-hardening

Conversation

@lahma

@lahma lahma commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Two engine-crash modes in the ObjectWrapper interop helpers, both reachable from script:

  1. Foreign this — the Symbol.iterator ClrFunction and the length getter are script-extractable (Object.getOwnPropertyDescriptor(clrList, 'length').get.call({})), and both hard-cast (ObjectWrapper) thisObject → uncaught InvalidCastException.
  2. Revoked proxy — the proxy-unwrap recursion passed the revoked proxy's null target straight into the cast (null passes reference casts) → NullReferenceException on first member access.

Both helpers now share an UnwrapReceiver that unwraps nested proxy chains iteratively, throws TypeError: Cannot perform '[Symbol.iterator]' on a proxy that has been revoked for revoked proxies (V8 wording), and TypeError: Method '…' called on incompatible receiver for foreign receivers — including primitive this, where no engine is reachable and the deferred-realm TypeError path is used (verified catchable and instanceof TypeError from script).

for...of directly over a revoked proxy still throws from the proxy's own [[Get]] guard first, matching node — covered by test. Regression tests confirm iteration over single and nested proxies wrapping CLR lists still works.

6 new tests; full Jint.Tests, PublicInterface, and test262 ~Proxy green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EsHuKuE4UihKZp5HYapDHE

@lahma
lahma force-pushed the proxy-wrapper-iterator-hardening branch from 6eb1363 to 876f9a3 Compare July 13, 2026 22:14
…eivers

The static Iterator and GetLength helpers installed on wrapped CLR objects
(Symbol.iterator and the length accessor getter) hard-cast thisObject to
ObjectWrapper. Two crash modes were reachable from script:

1. Foreign receiver: extracting the function and re-targeting it
   (var f = clrList[Symbol.iterator]; f.call({}) or
   Object.getOwnPropertyDescriptor(clrList, 'length').get.call({}))
   threw an uncaught InvalidCastException out of the engine.

2. Revoked proxy receiver: extracting the function through a live
   Proxy.revocable wrapper, revoking, then calling with the revoked proxy
   as this recursed with the nulled proxy target; the null passed the
   reference cast and crashed with NullReferenceException on first member
   access.

Both helpers now resolve their receiver through UnwrapReceiver, which
unwraps (nested) proxy chains with a loop, throws a JS TypeError
"Cannot perform '...' on a proxy that has been revoked" for revoked
proxies (realm taken from the proxy), and a JS TypeError
"Method '...' called on incompatible receiver" for non-wrapper receivers
(realm taken from the receiving ObjectInstance when available, falling
back to Throw.TypeErrorNoEngine for primitive receivers which the
interpreter materializes as a TypeError). Valid receivers - wrapper,
proxy-over-wrapper, proxy-over-proxy-over-wrapper - behave as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EsHuKuE4UihKZp5HYapDHE
@lahma
lahma force-pushed the proxy-wrapper-iterator-hardening branch from 876f9a3 to e2e1058 Compare July 13, 2026 22:31
@lahma
lahma merged commit fa9a9ee into sebastienros:main Jul 14, 2026
4 checks passed
@lahma
lahma deleted the proxy-wrapper-iterator-hardening branch July 14, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant