Executive brief
vm2 is a sandboxing library that executes untrusted JavaScript in an isolated context with configurable execution time limits. This vulnerability allows sandboxed code to run arbitrary code after the timeout expires and outside vm2's control, blocking the entire host application's event loop indefinitely. An attacker can abuse garbage-collection callbacks to execute a busy loop that freezes all operations on the server, causing a denial of service.
Technical details
vm2 implements execution timeouts by wrapping only the synchronous VM#run() call with doWithTimeout(), which bounds the primary script execution. However, FinalizationRegistry and WeakRef—exposed unmodified in the sandbox global scope—allow sandboxed code to register callbacks scheduled by the V8 garbage collector outside vm2's timeout accounting. An attacker registers a FinalizationRegistry cleanup callback, drops the object reference to make it GC-eligible, and then vm.run() returns normally within the configured timeout. When the garbage collector later invokes the cleanup callback (forcibly triggerable via --expose-gc or under memory pressure), the sandboxed code runs unbounded and unmetered, blocking the host event loop indefinitely. The vulnerability is a denial-of-service condition that breaks the timeout promise on which vm2's security model relies. Patched in version 3.11.7 by removing or hardening FinalizationRegistry and WeakRef from the sandbox global scope.
Affected products
- vm2 vm2 <= 3.11.6
Timeline
- 2026-08-24: disclosed
- 2026-09-17: advisory