Node.js Buffer.allocUnsafe(): When Partial Writes Can Disclose Memory
Buffer.allocUnsafe() returns uninitialized bytes. If an application reads or sends bytes it did not overwrite, those bytes may expose data from prior allocations. The exact contents depend on runtime and allocation conditions.
💡 Plain English Explainer (ELI5)
Think of a shared workbench where a new sheet is handed over without being wiped. Buffer.alloc() gives you a clean sheet; Buffer.allocUnsafe() may give you a reused one for speed. If you write only part of it and later read or send the whole buffer, the untouched bytes may contain old data. Whether any sensitive value appears depends on the runtime and allocation history; never assume those bytes are zero.
Core Concepts & Key Terms
- Uninitialized memory
- Memory returned by an allocator without a guarantee that its bytes are zero. Node.js documents that an unsafe Buffer may contain old, potentially sensitive data.
- Node.js Buffer pool
- Node.js may serve small Buffer.allocUnsafe() allocations from a shared internal pool. The threshold is based on Buffer.poolSize; current documentation describes half the pool (32 KiB with its documented default). Do not assume a fixed 8 KB slab across Node.js versions.
Buffer.allocUnsafe()- A Node.js API that allocates a Buffer without zero-initializing it. It is safe only when code overwrites every byte before any operation can read or expose the buffer.
- ESLint rules
- The deprecated
no-buffer-constructorrule rejects the legacy Buffer() constructor; it does not detect unsafe partial writes. A project can useno-restricted-propertiesto banBuffer.allocUnsafewhere appropriate.
Step-by-Step Memory Disclosure Path
A small buffer contains application data
A Node.js service may place request-derived or other sensitive application data in a small Buffer during normal processing. This alone does not expose that data.
A later unsafe allocation is not zero-filled
A subsequent Buffer.allocUnsafe() returns uninitialized memory. Small allocations may use Node.js’s shared Buffer pool; the exact allocation path depends on size, pool settings, and Node.js version.
The application writes only a prefix
A handler creates Buffer.allocUnsafe(512) and writes a 27-byte greeting. The remaining bytes were not initialized by that write.
A later read or send includes the full length
If code serializes or otherwise reads all 512 bytes instead of only the bytes written, the untouched range may disclose previous memory contents. This is a conditional risk, not a guarantee that a particular user’s credentials will be present.
Source Code: Flaw vs. Secure Implementation
// VULNERABLE PATTERN: partial initialization leaves bytes unspecified.
const greeting = "Hello, authenticated user!";
const buffer = Buffer.allocUnsafe(512);
const bytesWritten = buffer.write(greeting, 0, "utf8");
// DANGEROUS: any later read or send of the full buffer includes
// bytes [bytesWritten, buffer.length), which this code did not initialize.
// Never expose buffer.length bytes after writing only a prefix.
// SECURE: create an exact-length buffer from the data being returned.
const greeting = "Hello, authenticated user!";
const exactOutput = Buffer.from(greeting, "utf8");
// If a fixed-size allocation is required, zero-fill it and track the write.
const preAllocated = Buffer.alloc(512);
const bytesWritten = preAllocated.write(greeting, 0, "utf8");
const exactLengthOutput = preAllocated.subarray(0, bytesWritten);
// Pass exactOutput or exactLengthOutput to the intended consumer;
// do not serialize the unused tail or the entire backing ArrayBuffer.
Engineering Hardening Checklist
- Never use the deprecated
new Buffer()constructor. UseBuffer.from()for data orBuffer.alloc()for zero-initialized storage. - Avoid
Buffer.allocUnsafe()unless measurement justifies it and code overwrites every byte before any read, conversion, or output. - Track the byte count returned by write/copy operations and expose only
.subarray(0, bytesWritten); never serialize uninitialized tail bytes. - If passing a pooled Buffer to an API that expects an ArrayBuffer, account for
byteOffsetandbyteLength; do not expose the whole backing store. - Use ESLint
no-restricted-propertiesto prohibitBuffer.allocUnsafewhere your policy requires it. Treatno-buffer-constructoras a deprecated-constructor check, not a partial-write detector.