From 3b2895dfa3110d1b3a75c52c8ab1ea3d20a8562e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 20 Jan 2025 10:46:44 -0800 Subject: [PATCH] util: inspect: avoid a crash on a DataView with a detached buffer --- lib/internal/util/inspect.js | 19 +++++++++++++++++-- test/parallel/test-util-inspect.js | 19 +++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 60bee498e5ea8b..994b84a4d9a33f 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -987,6 +987,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { noIterator = true; } } + let isDV; if (noIterator) { keys = getKeys(value, ctx.showHidden); braces = ['{', '}']; @@ -1046,6 +1047,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { braces[0] = `${prefix}{`; ArrayPrototypeUnshift(keys, 'byteLength'); } else if (isDataView(value)) { + isDV = true; braces[0] = `${getPrefix(constructor, tag, 'DataView')}{`; // .buffer goes last, it's not a primitive like the others. ArrayPrototypeUnshift(keys, 'byteLength', 'byteOffset', 'buffer'); @@ -1099,6 +1101,20 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { try { output = formatter(ctx, value, recurseTimes); for (i = 0; i < keys.length; i++) { + isDV ??= isDataView(value); + if ( + isDV && (keys[i] === 'byteLength' || keys[i] === 'byteOffset') + ) { + try { + new Uint8Array(value.buffer); + } catch { + ArrayPrototypePush( + output, + formatValue(ctx, value.buffer.byteLength), + ); + continue; + } + } ArrayPrototypePush( output, formatProperty(ctx, value, recurseTimes, keys[i], extrasType), @@ -1994,8 +2010,7 @@ function formatProperty(ctx, value, recurseTimes, key, type, desc, original = value) { let name, str; let extra = ' '; - desc ||= ObjectGetOwnPropertyDescriptor(value, key) || - { value: value[key], enumerable: true }; + desc ||= ObjectGetOwnPropertyDescriptor(value, key) || { value: value[key], enumerable: true }; if (desc.value !== undefined) { const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3; ctx.indentationLvl += diff; diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 0b04e3b8dc7179..5be239b0891c82 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -206,11 +206,20 @@ assert.doesNotMatch( { const ab = new ArrayBuffer(42); + const dv = new DataView(ab); + assert.strictEqual(ab.byteLength, 42); new MessageChannel().port1.postMessage(ab, [ ab ]); assert.strictEqual(ab.byteLength, 0); - assert.strictEqual(util.inspect(ab), - 'ArrayBuffer { (detached), byteLength: 0 }'); + assert.strictEqual( + util.inspect(ab), + 'ArrayBuffer { (detached), byteLength: 0 }', + ); + + assert.strictEqual( + util.inspect(dv), + 'DataView { 0, 0, buffer: ArrayBuffer { (detached), byteLength: 0 } }', + ); } // Truncate output for ArrayBuffers using plural or singular bytes @@ -294,7 +303,8 @@ assert.doesNotMatch( }); // Now check that declaring a TypedArray in a different context works the same. -[ Float32Array, +[ + Float32Array, Float64Array, Int16Array, Int32Array, @@ -302,7 +312,8 @@ assert.doesNotMatch( Uint16Array, Uint32Array, Uint8Array, - Uint8ClampedArray ].forEach((constructor) => { + Uint8ClampedArray, +].forEach((constructor) => { const length = 2; const byteLength = length * constructor.BYTES_PER_ELEMENT; const array = vm.runInNewContext(