diff --git a/lib/buffer.js b/lib/buffer.js index 93cf26387cc762..cf9e0ca50c3d7e 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -51,6 +51,7 @@ const { TypedArrayPrototypeGetLength, TypedArrayPrototypeSet, TypedArrayPrototypeSlice, + TypedArrayPrototypeSubarray, Uint8Array, } = primordials; @@ -614,25 +615,55 @@ Buffer.concat = function concat(list, length) { if (length === undefined) { length = 0; for (let i = 0; i < list.length; i++) { - if (list[i].length) { - length += list[i].length; + const buf = list[i]; + if (!isUint8Array(buf)) { + // TODO(BridgeAR): This should not be of type ERR_INVALID_ARG_TYPE. + // Instead, find the proper error code for this. + throw new ERR_INVALID_ARG_TYPE( + `list[${i}]`, ['Buffer', 'Uint8Array'], buf); } + length += TypedArrayPrototypeGetByteLength(buf); } - } else { - validateOffset(length, 'length'); + + const buffer = allocate(length); + let pos = 0; + for (let i = 0; i < list.length; i++) { + const buf = list[i]; + const bufLength = TypedArrayPrototypeGetByteLength(buf); + TypedArrayPrototypeSet(buffer, buf, pos); + pos += bufLength; + } + + if (pos < length) { + TypedArrayPrototypeFill(buffer, 0, pos, length); + } + return buffer; } - const buffer = Buffer.allocUnsafe(length); - let pos = 0; + validateOffset(length, 'length'); for (let i = 0; i < list.length; i++) { - const buf = list[i]; - if (!isUint8Array(buf)) { + if (!isUint8Array(list[i])) { // TODO(BridgeAR): This should not be of type ERR_INVALID_ARG_TYPE. // Instead, find the proper error code for this. throw new ERR_INVALID_ARG_TYPE( `list[${i}]`, ['Buffer', 'Uint8Array'], list[i]); } - pos += _copyActual(buf, buffer, pos, 0, buf.length, true); + } + + const buffer = allocate(length); + let pos = 0; + for (let i = 0; i < list.length; i++) { + const buf = list[i]; + const bufLength = TypedArrayPrototypeGetByteLength(buf); + if (pos + bufLength > length) { + TypedArrayPrototypeSet(buffer, + TypedArrayPrototypeSubarray(buf, 0, length - pos), + pos); + pos = length; + break; + } + TypedArrayPrototypeSet(buffer, buf, pos); + pos += bufLength; } // Note: `length` is always equal to `buffer.length` at this point diff --git a/test/parallel/test-buffer-concat.js b/test/parallel/test-buffer-concat.js index 9f0eadd2f10163..bf96302445edf3 100644 --- a/test/parallel/test-buffer-concat.js +++ b/test/parallel/test-buffer-concat.js @@ -122,3 +122,13 @@ assert.deepStrictEqual( assert.deepStrictEqual(Buffer.concat([new Uint8Array([0x41, 0x42]), new Uint8Array([0x43, 0x44])]), Buffer.from('ABCD')); + +// Spoofed length getter should not cause uninitialized memory exposure +{ + const u8_1 = new Uint8Array([1, 2, 3, 4]); + const u8_2 = new Uint8Array([5, 6, 7, 8]); + Object.defineProperty(u8_1, 'length', { get() { return 100; } }); + const buf = Buffer.concat([u8_1, u8_2]); + assert.strictEqual(buf.length, 8); + assert.deepStrictEqual(buf, Buffer.from([1, 2, 3, 4, 5, 6, 7, 8])); +}