Low severity2.0NVD Advisory· Published Jul 8, 2024· Updated Apr 15, 2026
CVE-2024-38372
CVE-2024-38372
Description
Undici is an HTTP/1.1 client, written from scratch for Node.js. Depending on network and process conditions of a fetch() request, response.arrayBuffer() might include portion of memory from the Node.js process. This has been patched in v6.19.2.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
undicinpm | >= 6.14.0, < 6.19.2 | 6.19.2 |
Patches
1f979ec3204caperf: avoid unnecessary clone (#3117)
3 files changed · +46 −13
benchmarks/fetch/body-arraybuffer.mjs+24 −0 added@@ -0,0 +1,24 @@ +import { group, bench, run } from 'mitata' +import { Response } from '../../lib/web/fetch/response.js' + +const settings = { + small: 2 << 8, + middle: 2 << 12, + long: 2 << 16 +} + +for (const [name, length] of Object.entries(settings)) { + const buffer = Buffer.allocUnsafe(length).map(() => (Math.random() * 100) | 0) + group(`${name} (length ${length})`, () => { + bench('Response#arrayBuffer', async () => { + return await new Response(buffer).arrayBuffer() + }) + + // for comparison + bench('Response#text', async () => { + return await new Response(buffer).text() + }) + }) +} + +await run()
lib/web/fetch/body.js+11 −9 modified@@ -309,7 +309,7 @@ function bodyMixinMethods (instance) { // Return a Blob whose contents are bytes and type attribute // is mimeType. return new Blob([bytes], { type: mimeType }) - }, instance) + }, instance, false) }, arrayBuffer () { @@ -318,20 +318,21 @@ function bodyMixinMethods (instance) { // given a byte sequence bytes: return a new ArrayBuffer // whose contents are bytes. return consumeBody(this, (bytes) => { - return new Uint8Array(bytes).buffer - }, instance) + // Note: arrayBuffer already cloned. + return bytes.buffer + }, instance, true) }, text () { // The text() method steps are to return the result of running // consume body with this and UTF-8 decode. - return consumeBody(this, utf8DecodeBytes, instance) + return consumeBody(this, utf8DecodeBytes, instance, false) }, json () { // The json() method steps are to return the result of running // consume body with this and parse JSON from bytes. - return consumeBody(this, parseJSONFromBytes, instance) + return consumeBody(this, parseJSONFromBytes, instance, false) }, formData () { @@ -383,7 +384,7 @@ function bodyMixinMethods (instance) { throw new TypeError( 'Content-Type was not one of "multipart/form-data" or "application/x-www-form-urlencoded".' ) - }, instance) + }, instance, false) } } @@ -399,8 +400,9 @@ function mixinBody (prototype) { * @param {Response|Request} object * @param {(value: unknown) => unknown} convertBytesToJSValue * @param {Response|Request} instance + * @param {boolean} [shouldClone] */ -async function consumeBody (object, convertBytesToJSValue, instance) { +async function consumeBody (object, convertBytesToJSValue, instance, shouldClone) { webidl.brandCheck(object, instance) // 1. If object is unusable, then return a promise rejected @@ -432,13 +434,13 @@ async function consumeBody (object, convertBytesToJSValue, instance) { // 5. If object’s body is null, then run successSteps with an // empty byte sequence. if (object[kState].body == null) { - successSteps(new Uint8Array()) + successSteps(Buffer.allocUnsafe(0)) return promise.promise } // 6. Otherwise, fully read object’s body given successSteps, // errorSteps, and object’s relevant global object. - await fullyReadBody(object[kState].body, successSteps, errorSteps) + await fullyReadBody(object[kState].body, successSteps, errorSteps, shouldClone) // 7. Return promise. return promise.promise
lib/web/fetch/util.js+11 −4 modified@@ -1043,7 +1043,7 @@ function iteratorMixin (name, object, kInternalIterator, keyIndex = 0, valueInde /** * @see https://fetch.spec.whatwg.org/#body-fully-read */ -async function fullyReadBody (body, processBody, processBodyError) { +async function fullyReadBody (body, processBody, processBodyError, shouldClone) { // 1. If taskDestination is null, then set taskDestination to // the result of starting a new parallel queue. @@ -1069,8 +1069,7 @@ async function fullyReadBody (body, processBody, processBodyError) { // 5. Read all bytes from reader, given successSteps and errorSteps. try { - const result = await readAllBytes(reader) - successSteps(result) + successSteps(await readAllBytes(reader, shouldClone)) } catch (e) { errorSteps(e) } @@ -1118,8 +1117,9 @@ function isomorphicEncode (input) { * @see https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes * @see https://streams.spec.whatwg.org/#read-loop * @param {ReadableStreamDefaultReader} reader + * @param {boolean} [shouldClone] */ -async function readAllBytes (reader) { +async function readAllBytes (reader, shouldClone) { const bytes = [] let byteLength = 0 @@ -1128,6 +1128,13 @@ async function readAllBytes (reader) { if (done) { // 1. Call successSteps with bytes. + if (bytes.length === 1) { + const { buffer, byteOffset, byteLength } = bytes[0] + if (shouldClone === false) { + return Buffer.from(buffer, byteOffset, byteLength) + } + return Buffer.from(buffer.slice(byteOffset, byteOffset + byteLength), 0, byteLength) + } return Buffer.concat(bytes, byteLength) }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-3g92-w8c5-73pqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-38372ghsaADVISORY
- github.com/nodejs/undici/commit/f979ec3204ca489abf30e7d20e9fee9ea7711d36nvdWEB
- github.com/nodejs/undici/issues/3328nvdWEB
- github.com/nodejs/undici/issues/3337nvdWEB
- github.com/nodejs/undici/pull/3338nvdWEB
- github.com/nodejs/undici/security/advisories/GHSA-3g92-w8c5-73pqnvdWEB
- security.netapp.com/advisory/ntap-20240828-0009/nvd
News mentions
0No linked articles in our index yet.