Every CVE Search/ Get API key

CVE-2026-47683

vm2: bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike

8.7 HighFix available

vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.6, the bufferAllocLimit enforcement in lib/setup-sandbox.js does not cover Buffer.concat(list, totalLength) or Buffer.from(arrayLike) with an attacker-controlled length, allowing sandbox code to perform large synchronous host external-memory allocations that bypass the configured cap and can exhaust the host process. This issue is fixed in version 3.11.6.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
None
Availability
High

Exploit likelihood

0.54%Up 0.2 points since Aug 18, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.7

vm2: bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike

vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.6, the bufferAllocLimit enforcement in lib/setup-sandbox.js does not cover Buffer.concat(list, totalLength) or Buffer.from(arrayLike) with an attacker-controlled length, allowing sandbox code to perform large synchronous host external-memory allocations that bypass the configured cap and can exhaust the host process. This issue is fixed in version 3.11.6.

Record
CVE-2026-47683
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-770
Updated
2026-11-02
EUVDEUVD-2026-60461 8.7

vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.6, the bufferAllocLimit enforcement in lib/setup-sandbox.js does not cover Buffer.concat(list, totalLength) or Buffer.from(arrayLike) with an attacker-controlled length, allowing sandbox code to perform large synchronous host external-memory allocations that bypass the configured cap and can exhaust the host process. This issue is fixed in version 3.11.6.

Record
EUVD-2026-60461
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-gmc2-2x9w-cgh9 8.7

vm2's bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike

Summary

vm2 bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike

The bufferAllocLimit option introduced in 3.11.0 (GHSA-6785-pvv7-mvg7) caps host-side Buffer allocations driven by sandbox code, the way embedders opt into timeout. The cap wraps Buffer.alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow, and the deprecated Buffer(N) / new Buffer(N) forms. Two other API paths reach the same host C++ allocator with an attacker-controlled size and are not capped: Buffer.concat(list, totalLength) and Buffer.from(arrayLike) with a fake length. Sandbox code can use either to allocate an arbitrary number of host external bytes in a single call, defeating the explicit DoS mitigation the embedder configured.

Details

lib/setup-sandbox.js installs checkBufferAllocLimit at every wrapped entry to host Buffer allocation:

  • alloc() at lib/setup-sandbox.js:474 and the connect(alloc, host.Buffer.alloc) at line 480.
  • allocUnsafe() at line 488 and connect(allocUnsafe, host.Buffer.allocUnsafe) at line 496.
  • allocUnsafeSlow() at line 504 and connect(allocUnsafeSlow, host.Buffer.allocUnsafeSlow) at line 510.
  • BufferHandler.apply at line 424 and BufferHandler.construct at line 433 for the deprecated Buffer(N) / new Buffer(N) numeric-first-arg paths.

Buffer.concat is not wrapped. The sandbox-visible Buffer.concat is therefore the bridge proxy of the host Buffer.concat, which calls into Node's Buffer.allocUnsafe(totalLength) internally without going through the sandbox-side allocUnsafe wrapper. Same for Buffer.from when the argument is array-like ({length: N}): Node's fromArrayLike allocates a buffer of size N before the iteration that fills it. Neither of those allocator paths consult localBufferAllocLimit.

The mitigation rationale documented in docs/ATTACKS.md Category 23 explicitly enumerates the surfaces that were considered and either capped (Buffer.alloc family) or punted to follow-up (new Uint8Array(N), new ArrayBuffer(N), String.prototype.repeat). Buffer.concat(list, totalLength) is not listed in either group, and Buffer.from(arrayLike) is mentioned only as "bounded by source array size which had to be allocated through some other path first" -- which is not true for the {length: N} form, because no array of length N actually exists.

A single call from sandbox to Buffer.concat([Buffer.from('a')], 50 * 1024 * 1024) allocates 50 MiB of host external memory. The allocation itself is a single synchronous host C++ call that timeout cannot interrupt, exactly like the original advisory. The zero-fill that follows is interruptible, but the memory is already committed by the time the interrupt could fire, so the embedder's container memory budget is the only ceiling. The same pattern in a loop, or with a larger totalLength, drives RSS up by hundreds of megabytes per call.

The fix uses the existing checkBufferAllocLimit(size) helper and a sandbox-side wrapper installed via connect(...) -- one for Buffer.concat that sums the totalLength (or falls back to summing list lengths) and one for Buffer.from that recognises the array-like-with-numeric-length branch.

PoC

'use strict';
const { VM, NodeVM } = require('vm2');

function ext() { return Math.round(process.memoryUsage().external / 1024 / 1024); }
function tryBypass(label, code) {
    const ext0 = ext();
    let buf;
    try { buf = code(); }
    catch (e) {
        console.log(`[${label}] CAPPED -- ${String(e).split('\n')[0]}`);
        return;
    }
    console.log(`[${label}] BYPASSED -- got ${buf && buf.length} bytes (external +${ext() - ext0} MB)`);
}

console.log('Cap is configured at 1024 bytes.\n');

const vm1 = new VM({ bufferAllocLimit: 1024 });
tryBypass('VM Buffer.alloc(50MB)        ',
    () => vm1.run('Buffer.alloc(50 * 1024 * 1024)'));

const vm2 = new VM({ bufferAllocLimit: 1024 });
tryBypass('VM Buffer.concat 50MB        ',
    () =>…
Record
GHSA-gmc2-2x9w-cgh9
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-770
Updated
2026-11-02
NVD 8.7

vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.6, the bufferAllocLimit enforcement in lib/setup-sandbox.js does not cover Buffer.concat(list, totalLength) or Buffer.from(arrayLike) with an attacker-controlled length, allowing sandbox code to perform large synchronous host external-memory allocations that bypass the configured cap and can exhaust the host process. This issue is fixed in version 3.11.6.

Record
CVE-2026-47683
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Weakness
CWE-770
Updated
2026-11-02
OSV 8.7

vm2: bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike

vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.6, the bufferAllocLimit enforcement in lib/setup-sandbox.js does not cover Buffer.concat(list, totalLength) or Buffer.from(arrayLike) with an attacker-controlled length, allowing sandbox code to perform large synchronous host external-memory allocations that bypass the configured cap and can exhaust the host process. This issue is fixed in version 3.11.6.

Record
CVE-2026-47683
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-770
Updated
2026-11-02
OSVGHSA-gmc2-2x9w-cgh9 8.7

vm2's bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike

Summary

vm2 bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike

The bufferAllocLimit option introduced in 3.11.0 (GHSA-6785-pvv7-mvg7) caps host-side Buffer allocations driven by sandbox code, the way embedders opt into timeout. The cap wraps Buffer.alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow, and the deprecated Buffer(N) / new Buffer(N) forms. Two other API paths reach the same host C++ allocator with an attacker-controlled size and are not capped: Buffer.concat(list, totalLength) and Buffer.from(arrayLike) with a fake length. Sandbox code can use either to allocate an arbitrary number of host external bytes in a single call, defeating the explicit DoS mitigation the embedder configured.

Details

lib/setup-sandbox.js installs checkBufferAllocLimit at every wrapped entry to host Buffer allocation:

  • alloc() at lib/setup-sandbox.js:474 and the connect(alloc, host.Buffer.alloc) at line 480.
  • allocUnsafe() at line 488 and connect(allocUnsafe, host.Buffer.allocUnsafe) at line 496.
  • allocUnsafeSlow() at line 504 and connect(allocUnsafeSlow, host.Buffer.allocUnsafeSlow) at line 510.
  • BufferHandler.apply at line 424 and BufferHandler.construct at line 433 for the deprecated Buffer(N) / new Buffer(N) numeric-first-arg paths.

Buffer.concat is not wrapped. The sandbox-visible Buffer.concat is therefore the bridge proxy of the host Buffer.concat, which calls into Node's Buffer.allocUnsafe(totalLength) internally without going through the sandbox-side allocUnsafe wrapper. Same for Buffer.from when the argument is array-like ({length: N}): Node's fromArrayLike allocates a buffer of size N before the iteration that fills it. Neither of those allocator paths consult localBufferAllocLimit.

The mitigation rationale documented in docs/ATTACKS.md Category 23 explicitly enumerates the surfaces that were considered and either capped (Buffer.alloc family) or punted to follow-up (new Uint8Array(N), new ArrayBuffer(N), String.prototype.repeat). Buffer.concat(list, totalLength) is not listed in either group, and Buffer.from(arrayLike) is mentioned only as "bounded by source array size which had to be allocated through some other path first" -- which is not true for the {length: N} form, because no array of length N actually exists.

A single call from sandbox to Buffer.concat([Buffer.from('a')], 50 * 1024 * 1024) allocates 50 MiB of host external memory. The allocation itself is a single synchronous host C++ call that timeout cannot interrupt, exactly like the original advisory. The zero-fill that follows is interruptible, but the memory is already committed by the time the interrupt could fire, so the embedder's container memory budget is the only ceiling. The same pattern in a loop, or with a larger totalLength, drives RSS up by hundreds of megabytes per call.

The fix uses the existing checkBufferAllocLimit(size) helper and a sandbox-side wrapper installed via connect(...) -- one for Buffer.concat that sums the totalLength (or falls back to summing list lengths) and one for Buffer.from that recognises the array-like-with-numeric-length branch.

PoC

'use strict';
const { VM, NodeVM } = require('vm2');

function ext() { return Math.round(process.memoryUsage().external / 1024 / 1024); }
function tryBypass(label, code) {
    const ext0 = ext();
    let buf;
    try { buf = code(); }
    catch (e) {
        console.log(`[${label}] CAPPED -- ${String(e).split('\n')[0]}`);
        return;
    }
    console.log(`[${label}] BYPASSED -- got ${buf && buf.length} bytes (external +${ext() - ext0} MB)`);
}

console.log('Cap is configured at 1024 bytes.\n');

const vm1 = new VM({ bufferAllocLimit: 1024 });
tryBypass('VM Buffer.alloc(50MB)        ',
    () => vm1.run('Buffer.alloc(50 * 1024 * 1024)'));

const vm2 = new VM({ bufferAllocLimit: 1024 });
tryBypass('VM Buffer.concat 50MB        ',
    () =>…
Record
GHSA-gmc2-2x9w-cgh9
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-770
Updated
2026-11-02
OSVCLSA-2026-1787860907 No score

TuxCare security update for vm2 (34 CVEs)

TuxCare rebuilt vm2 to address 34 CVEs. Fixed in 3.10.0-tuxcare.1.

Record
CLSA-2026-1787860907
Updated
2026-11-02
OSVROOT-APP-NPM-CVE-2026-47683 No score

CVE-2026-47683 in vm2 - Patched by Root

Root has patched CVE-2026-47683 in the vm2 package for Root:npm. Multiple fixed versions available.

Record
ROOT-APP-NPM-CVE-2026-47683
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
vm2
npm
through 3.11.5
3.11.6
vm2
TuxCare:npm
From an advisory covering 30 CVEs
3.10.0-tuxcare.0 up to 3.10.0-tuxcare.1, plus listed versions
3.10.0
3.10.0-tuxcare.1
vm2
npm
before 3.11.6
3.11.6
vm2
Root:npm
before 3.9.17-aikido.12
3.9.17-aikido.123.9.17-aikido.133.10.5-aikido.73.9.7-aikido.43.9.7-aikido.53.9.7-aikido.63.10.5-aikido.8
@rootio/vm2
Root:npm
before 3.9.17-root.io.12
3.9.17-root.io.123.9.17-root.io.133.10.5-root.io.73.9.7-root.io.43.9.7-root.io.53.9.7-root.io.63.10.5-root.io.8

History

History
RecordedChangedSources
affected
facts
affected, aliases, referencesEUVD
first indexedCVE List, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-47683 is a high vulnerability (CVSS 8.7) in Patriksimek Vm2, published August 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.54%, the 45th percentile. Fixed in vm2 3.11.6.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open