From ec4a7e9584f15285e39c18a862a7df3e86e1dd7f Mon Sep 17 00:00:00 2001 From: fru1tworld Date: Tue, 5 May 2026 15:27:41 +0900 Subject: [PATCH] test_runner: prevent infinite loop on malformed v8 header Refs: https://github.com/nodejs/node/issues/62693 --- lib/internal/test_runner/runner.js | 18 ++++++++++++++++++ test/parallel/test-runner-v8-deserializer.mjs | 11 +++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js index bb3a8868309a94..67b285f2a887e1 100644 --- a/lib/internal/test_runner/runner.js +++ b/lib/internal/test_runner/runner.js @@ -364,7 +364,25 @@ class FileTest extends Test { } #drainRawBuffer() { while (this.#rawBuffer.length > 0) { + const sizeBefore = this.#rawBufferSize; + const lengthBefore = this.#rawBuffer.length; this.#processRawBuffer(); + if (this.#rawBufferSize === sizeBefore && this.#rawBuffer.length === lengthBefore) { + // The head matches the v8 serializer header but its declared payload + // size exceeds the buffered data and no more data will arrive, so + // flush the leftover bytes as stdout instead of looping forever. + const remaining = this.#rawBuffer.length === 1 ? + this.#rawBuffer[0] : + Buffer.concat(this.#rawBuffer, this.#rawBufferSize); + this.addToReport({ + __proto__: null, + type: 'test:stdout', + data: { __proto__: null, file: this.name, message: remaining.toString('utf-8') }, + }); + this.#rawBuffer = []; + this.#rawBufferSize = 0; + break; + } } } #processRawBuffer() { diff --git a/test/parallel/test-runner-v8-deserializer.mjs b/test/parallel/test-runner-v8-deserializer.mjs index 0f6fea1e64b58d..b0ba1ecb26e1e9 100644 --- a/test/parallel/test-runner-v8-deserializer.mjs +++ b/test/parallel/test-runner-v8-deserializer.mjs @@ -77,6 +77,17 @@ describe('v8 deserializer', common.mustCall(() => { ]); }); + it('should not loop forever on v8 header followed by an oversized length', async () => { + // Refs: https://github.com/nodejs/node/issues/62693 + // FF 0F is the v8 serializer header; the following four bytes encode a + // big-endian uint32 payload size that exceeds the buffer. + const malformed = Buffer.from([0xff, 0x0f, 0x7f, 0xff, 0xff, 0xff]); + const reported = await collectReported([malformed]); + assert.deepStrictEqual(reported, [ + { data: { __proto__: null, file: 'filetest', message: malformed.toString('utf-8') }, type: 'test:stdout' }, + ]); + }); + const headerPosition = headerLength * 2 + 4; for (let i = 0; i < headerPosition + 5; i++) { const message = `should deserialize a serialized message split into two chunks {...${i},${i + 1}...}`;