diff --git a/src/histogram.cc b/src/histogram.cc index 6efe2d6985c..cf0258c2af9 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -3211,6 +3211,15 @@ void HistogramImpl::DoImport(const FunctionCallbackInfo& args) { static_cast(backing->Data()) + input->ByteOffset(); size_t len = input->ByteLength(); + // Another thread can write to a SharedArrayBuffer while it is parsed, so + // parse a private copy instead. + std::unique_ptr data_copy; + if (input->Buffer()->IsSharedArrayBuffer() && len > 0) { + data_copy = std::make_unique_for_overwrite(len); + memcpy(data_copy.get(), data, len); + data = data_copy.get(); + } + auto histogram = Histogram::Import(data, len); if (!histogram) { THROW_ERR_INVALID_ARG_VALUE(env, "Invalid histogram export data"); diff --git a/test/parallel/test-perf-hooks-histogram-import-shared.js b/test/parallel/test-perf-hooks-histogram-import-shared.js new file mode 100644 index 00000000000..e491ff9b9f9 --- /dev/null +++ b/test/parallel/test-perf-hooks-histogram-import-shared.js @@ -0,0 +1,33 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); +const { createHistogram, importHistogram } = require('node:perf_hooks'); +const kInvalid = { code: 'ERR_INVALID_ARG_VALUE' }; + +{ + // Data in a SharedArrayBuffer, including a sliced view, imports the same way + // as data in an ArrayBuffer. + const h = createHistogram(); + h.record(1); + h.record(42); + h.record(1000); + const exported = h.export(); + + for (const offset of [0, 3]) { + const sab = new SharedArrayBuffer(exported.length + offset + 5); + new Uint8Array(sab).fill(0xff); + const view = new Uint8Array(sab, offset, exported.length); + view.set(exported); + const imported = importHistogram(view); + assert.deepStrictEqual(imported.export(), exported); + + // Later writes do not change the imported histogram. + view.fill(0); + assert.deepStrictEqual(imported.export(), exported); + assert.throws(() => importHistogram(view), kInvalid); + } + + assert.throws( + () => importHistogram(new Uint8Array(new SharedArrayBuffer(0))), kInvalid); +}