diff --git a/lib/binary_parser.ts b/lib/binary_parser.ts index cf511f6..72f2055 100644 --- a/lib/binary_parser.ts +++ b/lib/binary_parser.ts @@ -1299,6 +1299,13 @@ export class Parser { `for (var ${counter} = offset + ${lengthInBytes}; offset < ${counter}; ) {`, ); } else { + // Reject a length field that claims more elements than the buffer can + // hold, so a tiny crafted input cannot force a huge allocation/loop. + ctx.pushCode(`if (${length} > buffer.length - offset) {`); + ctx.generateError( + `"Array length " + (${length}) + " exceeds buffer length"`, + ); + ctx.pushCode(`}`); ctx.pushCode( `for (var ${counter} = ${length}; ${counter} > 0; ${counter}--) {`, ); diff --git a/test/composite_parser.ts b/test/composite_parser.ts index da744cf..b23a89f 100644 --- a/test/composite_parser.ts +++ b/test/composite_parser.ts @@ -24,6 +24,19 @@ function compositeParserTests( message: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], }); }); + it("should reject an array length that exceeds the remaining buffer", () => { + const parser = Parser.start() + .uint32be("count") + .array("items", { + length: "count", + type: new Parser().buffer("payload", { length: 1 }), + }); + + const buffer = factory([0x00, 0x01, 0x86, 0xa0]); + throws(() => { + parser.parse(buffer); + }); + }); it("should parse array of primitive types with lengthInBytes", () => { const parser = Parser.start().uint8("length").array("message", { lengthInBytes: "length",