FlawOpen
● Memory disclosure / CWE-908

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-constructor rule rejects the legacy Buffer() constructor; it does not detect unsafe partial writes. A project can use no-restricted-properties to ban Buffer.allocUnsafe where appropriate.

Step-by-Step Memory Disclosure Path

Step 1

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.

Step 2

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.

Step 3

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.

Step 4

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
// 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.
HARDENED DEFENSE
// 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

← Browse Full Security Directory Explore Vulnerability Playbooks → Node.js Buffer API: allocUnsafe(), pooling, and legacy constructor behavior → CWE-908: Use of Uninitialized Resource → ESLint no-restricted-properties rule → ESLint no-buffer-constructor rule and deprecation notice →