Summary
DsfParser.parseChunks skips an unrecognised chunk's payload with an un-awaited call:
this.tokenizer.ignore(Number(chunkHeader.size) - ChunkHeader.len); // lib/dsf/DsfParser.js:51 — no await
ChunkHeader.len is 12. A crafted .dsf chunk with id != 'fmt ' and size in 0..11 makes the
argument negative; strtok3 (≥ 10.3.5) throws RangeError on a negative ignore. Because the call
is fire-and-forget, the rejection is detached from the parseBuffer() promise chain → unhandled
rejection → Node's default (≥ 15) crashes the process — after parseBuffer() already
resolved, so a caller's try/catch catches nothing and is still taken down.
Residual of GHSA-v6c2-xwv6-8xf7: the ASF site was fixed in 11.12.3 (size validation) and strtok3
now throws on negative ignore; the DSF site was never validated, and its missing await
escalates that throw into an uncatchable crash.
Root cause (lib/dsf/DsfParser.js:34-56)
while (bytesRemaining >= ChunkHeader.len) { // ChunkHeader.len = 12
const chunkHeader = await this.tokenizer.readToken(ChunkHeader); // { id, size }
switch (chunkHeader.id) {
case 'fmt ': { ...; return; }
default: this.tokenizer.ignore(Number(chunkHeader.size) - ChunkHeader.len); break; // size<12 -> negative, no await
}
bytesRemaining -= chunkHeader.size;
}
strtok3 AbstractTokenizer.ignore (L78-79): if (length < 0) throw new RangeError('ignore length must be ≥ 0 bytes');
Steps to reproduce
repro/ — public API only, Node's default unhandled-rejection mode, try/catch around the parse:
npm install && node poc.mjs
Confirmed on 11.14.0:
[app] parseBuffer() RESOLVED — the caller saw no error to catch.
RangeError: ignore length must be ≥ 0 bytes
at DsfParser.parseChunks (.../lib/dsf/DsfParser.js:51)
<process exits non-zero — the "process survived" line never prints>
Impact
DoS: a single crafted .dsf (or any file with the DSD magic) crashes the Node process of any
app parsing untrusted audio with music-metadata (2.2M weekly downloads). The crash bypasses the
caller's error handling, so even apps that correctly try/catch per-file parsing are killed — one
malicious upload can take down a shared server/worker.
Remediation
Add await on line 51 (makes the RangeError a catchable parse error), and validate
chunkHeader.size >= ChunkHeader.len before the skip (as the ASF fix did; also guards the loop
counter). Audit other parsers for un-awaited tokenizer.ignore()/readToken().
Scope / honesty
Requires the DSF path (a DSD -magic file — normal auto-detection). Relies on Node's default
unhandled-rejection mode (throw, default since Node 15); the point is that the standard defensive
per-parse try/catch does not protect against it. Crash (availability), not disclosure/RCE.
Negatives confirmed alongside: ASF infinite loop fixed; negative-ignore infinite-loop class
closed at strtok3; unbounded allocation bounded by strtok3's read bound-check.
Credits
Issue also reported by @ryu7eroo
References
Summary
DsfParser.parseChunksskips an unrecognised chunk's payload with an un-awaited call:ChunkHeader.lenis 12. A crafted.dsfchunk withid != 'fmt 'andsizein0..11makes theargument negative; strtok3 (≥ 10.3.5) throws
RangeErroron a negativeignore. Because the callis fire-and-forget, the rejection is detached from the
parseBuffer()promise chain → unhandledrejection → Node's default (≥ 15) crashes the process — after
parseBuffer()alreadyresolved, so a caller's
try/catchcatches nothing and is still taken down.Residual of GHSA-v6c2-xwv6-8xf7: the ASF site was fixed in 11.12.3 (size validation) and strtok3
now throws on negative
ignore; the DSF site was never validated, and its missingawaitescalates that throw into an uncatchable crash.
Root cause (
lib/dsf/DsfParser.js:34-56)strtok3
AbstractTokenizer.ignore(L78-79):if (length < 0) throw new RangeError('ignore length must be ≥ 0 bytes');Steps to reproduce
repro/— public API only, Node's default unhandled-rejection mode,try/catcharound the parse:Confirmed on 11.14.0:
Impact
DoS: a single crafted
.dsf(or any file with theDSDmagic) crashes the Node process of anyapp parsing untrusted audio with music-metadata (2.2M weekly downloads). The crash bypasses the
caller's error handling, so even apps that correctly
try/catchper-file parsing are killed — onemalicious upload can take down a shared server/worker.
Remediation
Add
awaiton line 51 (makes theRangeErrora catchable parse error), and validatechunkHeader.size >= ChunkHeader.lenbefore the skip (as the ASF fix did; also guards the loopcounter). Audit other parsers for un-awaited
tokenizer.ignore()/readToken().Scope / honesty
Requires the DSF path (a
DSD-magic file — normal auto-detection). Relies on Node's defaultunhandled-rejection mode (
throw, default since Node 15); the point is that the standard defensiveper-parse
try/catchdoes not protect against it. Crash (availability), not disclosure/RCE.Negatives confirmed alongside: ASF infinite loop fixed; negative-
ignoreinfinite-loop classclosed at strtok3; unbounded allocation bounded by strtok3's read bound-check.
Credits
Issue also reported by @ryu7eroo
References