Alta

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

  • Publicada: 17.08.2026
  • Actualizado: 11.10.2026 10:22

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

js
'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        ',
    () => vm2.run('Buffer.concat([Buffer.from("a")], 50 * 1024 * 1024)'));

const vm3 = new NodeVM({ bufferAllocLimit: 1024 });
tryBypass('NodeVM Buffer.concat 50MB    ',
    () => vm3.run('module.exports = Buffer.concat([Buffer.from("a")], 50 * 1024 * 1024);'));

const vm4 = new VM({ bufferAllocLimit: 1024 });
tryBypass('VM Buffer.from({length: 8MB})',
    () => vm4.run('Buffer.from({length: 8 * 1024 * 1024})'));

Run with node poc.js against vm2@3.11.3:

Cap is configured at 1024 bytes.

[VM Buffer.alloc(50MB)        ] CAPPED -- RangeError: Buffer allocation size 52428800 exceeds bufferAllocLimit 1024
[VM Buffer.concat 50MB        ] BYPASSED -- got 52428800 bytes (external +50 MB)
[NodeVM Buffer.concat 50MB    ] BYPASSED -- got 52428800 bytes (external +50 MB)
[VM Buffer.from({length: 8MB})] BYPASSED -- got 8388608 bytes (external +8 MB)

Process RSS climbs by the same amount each call, confirming a real host C++ allocation rather than a sandbox-realm-only effect.

Impact

This is the same DoS class GHSA-6785-pvv7-mvg7 was filed for: untrusted sandbox code amplifying a small payload into a large synchronous host external-memory allocation that V8's timeout cannot preempt. In the environments the advisory cites -- Docker memory limits, Kubernetes pods, AWS Lambda -- a single 200-byte sandbox payload can drive a multi-hundred-megabyte RSS jump and OOM the host process.

The Category 23 fix was specifically scoped to "cap host Buffer external allocation" and embedders are documented to opt into bufferAllocLimit as their layered defense against this class. The two paths above are uncapped, so an embedder that has configured bufferAllocLimit: 32 * 1024 * 1024 (the value recommended in the README's Hardening recommendations) is still vulnerable to the exact attack the option was designed to prevent. The mitigation invariant -- "every Buffer external allocation driven by sandbox code is capped by bufferAllocLimit" -- does not hold.

No sandbox escape; pure DoS.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de npm vm2 < 3.11.6 3.11.6

Referencias

Puntuación CVSS -
Vector CVSS
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Debilidad (CWE)
CWE-770
Plataforma
npm
Paquete
vm2
Publicada
17.08.2026
Actualizado
11.10.2026 10:22

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-gmc2-2x9w-cgh9 - CC BY 4.0

Historial de vulnerabilidades: vm2

Todas las vulnerabilidades publicadas de este paquete (Paquete de npm), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (78) en el archivo
Entradas en total
78
Primera → última
10.2021 10.2026
Sin corrección
1
Explotada en ataques
0

Distribución por gravedad

  • Crítica 51
  • Alta 14
  • Media 12
  • Baja 1

Divulgaciones por año

  1. 2021: 1
  2. 2022: 4
  3. 2023: 7
  4. 2024: 0
  5. 2025: 0
  6. 2026: 66
Gravedad:
  1. vm2's bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike esta entrada
    Alta CVE-2026-47683 < 3.11.6 3.11.6
  2. Crítica · 10.0 CVE-2026-93605 < 3.12.1 3.12.1
  3. Crítica · 10.0 CVE-2026-100721 < 3.12.2 3.12.2
  4. Crítica · 10.0 CVE-2026-92955 < 3.11.8 3.11.8
  5. Crítica · 10.0 CVE-2026-92956 >= 3.10.1, < 3.11.7 3.11.7
  6. Crítica · 9.9 CVE-2026-92948 >= 3.9.6, < 3.11.7 3.11.7
  7. Crítica · 10.0 CVE-2026-92940 >= 3.11.3, < 3.11.7 3.11.7
  8. Crítica · 9.0 CVE-2026-92935 >= 3.11.4, < 3.11.7 3.11.7
  9. Crítica · 10.0 CVE-2026-92937 >= 3.11.6, < 3.11.7 3.11.7
Mostrar más (20) Mostrando 21 de 65

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.