You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Gp3To5Importer.readBend and readTremoloBarEffect read a pointCount: int32 from the input stream and then loop pointCount times, reading 9 bytes per iteration. Neither validates that pointCount * 9 fits in the remaining stream:
constpointCount: number=IOHelper.readInt32LE(this.data);if(pointCount>0){for(leti: number=0;i<pointCount;i++){IOHelper.readInt32LE(this.data);IOHelper.readInt32LE(this.data);GpBinaryHelpers.gpReadBool(this.data);// ...allocates a BendPoint per iteration}}
When the parser becomes misaligned mid-stream (e.g. on a corrupted file where an earlier field shifts the cursor onto random bytes), pointCount is read from those random bytes and can be up to ~2^31. The loop then iterates hundreds of millions of times. ByteBuffer.readByte() returns -1 silently at EOF rather than throwing, so the loop never terminates from the input side; meanwhile each iteration allocates a BendPoint object — V8 heap grows until OOM-crash.
This is a practical DoS vector against any browser page that runs AlphaTab on user-supplied input: a single ~3 KB corrupt .gp5 can OOM-crash the tab in 10–30 seconds, taking down all sibling tabs in the same Chromium renderer process.
Expected Behavior
The parser should bail out with a typed UnsupportedFormatError when an input field is impossible by conservation of bytes (count × bytes-per-iteration exceeds remaining stream). Real bends have ≤ ~30 points (270 bytes); anything larger than the remaining stream is malformed input and should never enter the loop.
Steps To Reproduce
Obtain the reference fixture: test-data/guitarpro5/corrupted-bend-point-count.gp5 (a 3.8 KB GP5 file). The file's readBeatEffects flag byte mid-stream causes readTremoloBarEffect to be invoked on garbage, where pointCount reads as 587,530,544 against 1,413 remaining bytes — a 6-orders-of-magnitude mismatch. Fixture is committed in PR fix(importer): bound runaway loops on corrupt count fields in GP3-5 binary parser #2670.
Run ScoreLoader.loadScoreFromBytes(bytes) in Node with --max-old-space-size=512.
alphaTab: 1.9.0 (commit f8ef24f, current HEAD of `develop`)
Node.js: v22.14.0
OS: Ubuntu 22.04.3 LTS on WSL2 (kernel 6.6.87.2-microsoft-standard-WSL2)
Platform
Web
Anything else?
PyGuitarPro rejects the same fixture with a typed GPException because Python's struct.unpack raises on insufficient bytes — AlphaTab's ByteBuffer.readByte() → -1-on-EOF lacks the equivalent safety net. Fix proposed in PR #2670 (targeted bounds check in the two empirically-observed loops).
Is there an existing issue for this?
Current Behavior
Gp3To5Importer.readBendandreadTremoloBarEffectread apointCount: int32from the input stream and then looppointCounttimes, reading 9 bytes per iteration. Neither validates thatpointCount * 9fits in the remaining stream:When the parser becomes misaligned mid-stream (e.g. on a corrupted file where an earlier field shifts the cursor onto random bytes),
pointCountis read from those random bytes and can be up to ~2^31. The loop then iterates hundreds of millions of times.ByteBuffer.readByte()returns-1silently at EOF rather than throwing, so the loop never terminates from the input side; meanwhile each iteration allocates aBendPointobject — V8 heap grows until OOM-crash.This is a practical DoS vector against any browser page that runs AlphaTab on user-supplied input: a single ~3 KB corrupt
.gp5can OOM-crash the tab in 10–30 seconds, taking down all sibling tabs in the same Chromium renderer process.Expected Behavior
The parser should bail out with a typed
UnsupportedFormatErrorwhen an input field is impossible by conservation of bytes (count × bytes-per-iteration exceeds remaining stream). Real bends have ≤ ~30 points (270 bytes); anything larger than the remaining stream is malformed input and should never enter the loop.Steps To Reproduce
test-data/guitarpro5/corrupted-bend-point-count.gp5(a 3.8 KB GP5 file). The file'sreadBeatEffectsflag byte mid-stream causesreadTremoloBarEffectto be invoked on garbage, wherepointCountreads as 587,530,544 against 1,413 remaining bytes — a 6-orders-of-magnitude mismatch. Fixture is committed in PR fix(importer): bound runaway loops on corrupt count fields in GP3-5 binary parser #2670.ScoreLoader.loadScoreFromBytes(bytes)in Node with--max-old-space-size=512.FATAL ERROR: Reached heap limit Allocation failed).Link to jsFiddle, CodePen, Project
No response
Version and Environment
alphaTab: 1.9.0 (commit f8ef24f, current HEAD of `develop`) Node.js: v22.14.0 OS: Ubuntu 22.04.3 LTS on WSL2 (kernel 6.6.87.2-microsoft-standard-WSL2)Platform
Web
Anything else?
PyGuitarPro rejects the same fixture with a typed
GPExceptionbecause Python'sstruct.unpackraises on insufficient bytes — AlphaTab'sByteBuffer.readByte() → -1-on-EOF lacks the equivalent safety net. Fix proposed in PR #2670 (targeted bounds check in the two empirically-observed loops).