CVE-2020-8244
Description
A buffer over-read in the bl npm package versions <1.2.3, <2.2.1, <3.0.1, and <4.0.3 allows attackers to read uninitialized memory by passing a negative value to consume().
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A buffer over-read in the `bl` npm package versions <1.2.3, <2.2.1, <3.0.1, and <4.0.3 allows attackers to read uninitialized memory by passing a negative value to `consume()`.
Vulnerability
Overview
CVE-2020-8244 is a buffer over-read vulnerability in the bl npm package, affecting versions earlier than 1.2.3, 2.2.1, 3.0.1, and 4.0.3 [1]. The root cause lies in the consume() method, which accepts a bytes argument that can become negative. When this negative value is used internally, it corrupts the BufferList state and causes subsequent .slice() calls to return uninitialized heap memory, effectively leaking sensitive data [2][3][4].
Exploitation
An attacker can trigger the vulnerability by supplying user input that ends up as the bytes argument to consume(). If that argument becomes negative (e.g., due to arithmetic on user-controlled data), the method fails to normalize the value and proceeds with corrupted logic [2][3][4]. No authentication is required if the attacker can inject such input into a vulnerable application. The attack surface is primarily server-side Node.js code that uses the bl library to handle streams or buffers from untrusted sources.
Impact
Successful exploitation allows an attacker to read uninitialized memory regions that may contain sensitive information such as cryptographic keys, session tokens, or other secrets from the process heap [1]. Because the leaked data is obtained through regular .slice() calls, the attacker can extract it without triggering obvious errors, making detection difficult.
Mitigation
The vulnerability has been patched in bl versions 1.2.3, 2.2.1, 3.0.1, and 4.0.3. The fix adds input validation in consume() using Math.trunc() and a check that returns early for non-positive or NaN values, and also ensures that the copy() method does not return uninitialized memory by slicing only up to bufoff [2][3][4]. Users should upgrade immediately; no known workarounds exist.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
blnpm | < 1.2.3 | 1.2.3 |
blnpm | >= 2.0.0, < 2.2.1 | 2.2.1 |
blnpm | >= 3.0.0, < 3.0.1 | 3.0.1 |
blnpm | >= 4.0.0, < 4.0.3 | 4.0.3 |
Affected products
2- bl/bldescription
Patches
3d3e240e3b8baFix unintialized memory access
2 files changed · +26 −1
BufferList.js+10 −1 modified@@ -134,19 +134,23 @@ BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) { if (bytes > l) { this._bufs[i].copy(dst, bufoff, start) + bufoff += l } else { this._bufs[i].copy(dst, bufoff, start, start + bytes) + bufoff += l break } - bufoff += l bytes -= l if (start) { start = 0 } } + // safeguard so that we don't return uninitialized memory + if (dst.length > bufoff) return dst.slice(0, bufoff) + return dst } @@ -188,6 +192,11 @@ BufferList.prototype.toString = function toString (encoding, start, end) { } BufferList.prototype.consume = function consume (bytes) { + // first, normalize the argument, in accordance with how Buffer does it + bytes = Math.trunc(bytes) + // do nothing if not a positive number + if (Number.isNaN(bytes) || bytes <= 0) return this + while (this._bufs.length) { if (bytes >= this._bufs[0].length) { bytes -= this._bufs[0].length
test/test.js+16 −0 modified@@ -463,6 +463,22 @@ tape('test toString encoding', function (t) { t.end() }) +tape('uninitialized memory', function (t) { + const secret = crypto.randomBytes(256) + for (let i = 0; i < 1e6; i++) { + const clone = Buffer.from(secret) + const bl = new BufferList() + bl.append(Buffer.from('a')) + bl.consume(-1024) + const buf = bl.slice(1) + if (buf.indexOf(clone) !== -1) { + t.fail(`Match (at ${i})`) + break + } + } + t.end() +}) + !process.browser && tape('test stream', function (t) { const random = crypto.randomBytes(65534)
dacc4ac7d5fcFix unintialized memory access
2 files changed · +26 −1
bl.js+10 −1 modified@@ -186,18 +186,22 @@ BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) { if (bytes > l) { this._bufs[i].copy(dst, bufoff, start) + bufoff += l } else { this._bufs[i].copy(dst, bufoff, start, start + bytes) + bufoff += l break } - bufoff += l bytes -= l if (start) start = 0 } + // safeguard so that we don't return uninitialized memory + if (dst.length > bufoff) return dst.slice(0, bufoff) + return dst } @@ -233,6 +237,11 @@ BufferList.prototype.toString = function toString (encoding, start, end) { } BufferList.prototype.consume = function consume (bytes) { + // first, normalize the argument, in accordance with how Buffer does it + bytes = Math.trunc(bytes) + // do nothing if not a positive number + if (Number.isNaN(bytes) || bytes <= 0) return this + while (this._bufs.length) { if (bytes >= this._bufs[0].length) { bytes -= this._bufs[0].length
test/test.js+16 −0 modified@@ -431,6 +431,22 @@ tape('test toString encoding', function (t) { t.end() }) +tape('uninitialized memory', function (t) { + const secret = crypto.randomBytes(256) + for (let i = 0; i < 1e6; i++) { + const clone = Buffer.from(secret) + const bl = new BufferList() + bl.append(Buffer.from('a')) + bl.consume(-1024) + const buf = bl.slice(1) + if (buf.indexOf(clone) !== -1) { + t.fail(`Match (at ${i})`) + break + } + } + t.end() +}) + !process.browser && tape('test stream', function (t) { var random = crypto.randomBytes(65534) , rndhash = hash(random, 'md5')
8a8c13c880e2Fix unintialized memory access
2 files changed · +26 −1
bl.js+10 −1 modified@@ -185,18 +185,22 @@ BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) { if (bytes > l) { this._bufs[i].copy(dst, bufoff, start) + bufoff += l } else { this._bufs[i].copy(dst, bufoff, start, start + bytes) + bufoff += l break } - bufoff += l bytes -= l if (start) start = 0 } + // safeguard so that we don't return uninitialized memory + if (dst.length > bufoff) return dst.slice(0, bufoff) + return dst } @@ -232,6 +236,11 @@ BufferList.prototype.toString = function toString (encoding, start, end) { } BufferList.prototype.consume = function consume (bytes) { + // first, normalize the argument, in accordance with how Buffer does it + bytes = Math.trunc(bytes) + // do nothing if not a positive number + if (Number.isNaN(bytes) || bytes <= 0) return this + while (this._bufs.length) { if (bytes >= this._bufs[0].length) { bytes -= this._bufs[0].length
test/test.js+16 −0 modified@@ -431,6 +431,22 @@ tape('test toString encoding', function (t) { t.end() }) +tape('uninitialized memory', function (t) { + const secret = crypto.randomBytes(256) + for (let i = 0; i < 1e6; i++) { + const clone = Buffer.from(secret) + const bl = new BufferList() + bl.append(Buffer.from('a')) + bl.consume(-1024) + const buf = bl.slice(1) + if (buf.indexOf(clone) !== -1) { + t.fail(`Match (at ${i})`) + break + } + } + t.end() +}) + !process.browser && tape('test stream', function (t) { var random = crypto.randomBytes(65534) , rndhash = hash(random, 'md5')
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-pp7h-53gx-mx7rghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-8244ghsaADVISORY
- github.com/rvagg/bl/commit/8a8c13c880e2bef519133ea43e0e9b78b5d0c91eghsaWEB
- github.com/rvagg/bl/commit/d3e240e3b8ba4048d3c76ef5fb9dd1f8872d3190ghsaWEB
- github.com/rvagg/bl/commit/dacc4ac7d5fcd6201bcf26fbd886951be9537466ghsaWEB
- hackerone.com/reports/966347ghsax_refsource_MISCWEB
- lists.debian.org/debian-lts-announce/2021/06/msg00028.htmlghsamailing-listx_refsource_MLISTWEB
News mentions
0No linked articles in our index yet.