diff --git a/packages/alphatab/src/importer/Gp3To5Importer.ts b/packages/alphatab/src/importer/Gp3To5Importer.ts index cc74873be..7dbf63617 100644 --- a/packages/alphatab/src/importer/Gp3To5Importer.ts +++ b/packages/alphatab/src/importer/Gp3To5Importer.ts @@ -1039,6 +1039,8 @@ export class Gp3To5Importer extends ScoreImporter { IOHelper.readInt32LE(this.data); // value const pointCount: number = IOHelper.readInt32LE(this.data); + // 9 bytes per point (i32 offset + i32 value + 1 byte vibrato) + Gp3To5Importer._requireFits(this.data, pointCount, 9, 'whammy bar point count'); if (pointCount > 0) { for (let i: number = 0; i < pointCount; i++) { const point: BendPoint = new BendPoint(0, 0); @@ -1053,6 +1055,15 @@ export class Gp3To5Importer extends ScoreImporter { } } + private static _requireFits(data: IReadable, count: number, bytesPerItem: number, fieldName: string): void { + const remaining = data.length - data.position; + if (count < 0 || count * bytesPerItem > remaining) { + throw new UnsupportedFormatError( + `${fieldName}=${count} (${count * bytesPerItem} bytes) exceeds remaining ${remaining} bytes` + ); + } + } + private static _toStrokeValue(value: number): number { switch (value) { case 1: @@ -1337,6 +1348,8 @@ export class Gp3To5Importer extends ScoreImporter { IOHelper.readInt32LE(this.data); // value const pointCount: number = IOHelper.readInt32LE(this.data); + // 9 bytes per point (i32 offset + i32 value + 1 byte vibrato) + Gp3To5Importer._requireFits(this.data, pointCount, 9, 'bend point count'); if (pointCount > 0) { for (let i: number = 0; i < pointCount; i++) { const point: BendPoint = new BendPoint(0, 0); diff --git a/packages/alphatab/test-data/guitarpro5/corrupted-bend-point-count.gp5 b/packages/alphatab/test-data/guitarpro5/corrupted-bend-point-count.gp5 new file mode 100755 index 000000000..4effb7d97 Binary files /dev/null and b/packages/alphatab/test-data/guitarpro5/corrupted-bend-point-count.gp5 differ diff --git a/packages/alphatab/test/importer/Gp5Importer.test.ts b/packages/alphatab/test/importer/Gp5Importer.test.ts index 4ef957e89..4507897ba 100644 --- a/packages/alphatab/test/importer/Gp5Importer.test.ts +++ b/packages/alphatab/test/importer/Gp5Importer.test.ts @@ -1,4 +1,5 @@ import { Settings } from '@coderline/alphatab/Settings'; +import { UnsupportedFormatError } from '@coderline/alphatab/importer/UnsupportedFormatError'; import { type Beat, BeatBeamingMode } from '@coderline/alphatab/model/Beat'; import { Direction } from '@coderline/alphatab/model/Direction'; import { Ottavia } from '@coderline/alphatab/model/Ottavia'; @@ -569,4 +570,13 @@ describe('Gp5ImporterTest', () => { } } }); + + it('corrupted-bend-point-count', async () => { + // Regression: misaligned read produced pointCount ~5e8, OOM-crashing the + // import. Now rejected upfront with a typed error. + const reader = await GpImporterTestHelper.prepareImporterWithFile( + 'guitarpro5/corrupted-bend-point-count.gp5' + ); + expect(() => reader.readScore()).to.throw(UnsupportedFormatError); + }); });