vtkDataArray.getValue reads past array end on non-first-component indices
Summary
vtkDataArray.getValue(valueIdx) computes its tuple index as
valueIdx / model.numberOfComponents without flooring. Any valueIdx
that isn't a multiple of numberOfComponents reads out of bounds and
returns undefined (or the wrong value if adjacent bytes happen to
contain a live entry).
File / line
Sources/Common/Core/DataArray/index.js:152-156
publicAPI.getValue = (valueIdx) => {
const idx = valueIdx / model.numberOfComponents; // ← not floored
const comp = valueIdx % model.numberOfComponents;
return publicAPI.getComponent(idx, comp);
};
Downstream getComponent(idx, comp) at
Common/Core/DataArray.js:145-147 computes
values[idx * numberOfComponents + comp], so a non-integer idx
becomes a non-integer array subscript, resolving to undefined under
TypedArray[] access.
Minimal reproducer
<!doctype html>
<script type="module">
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
const a = vtkDataArray.newInstance({
numberOfComponents: 3,
values: Float32Array.from([
0, 1, 2, // tuple 0
3, 4, 5, // tuple 1
6, 7, 8, // tuple 2
9, 10, 11, // tuple 3
]),
});
// Expected: 11 (last live element)
// Actual : undefined (reads values[13], off the end)
console.log('getValue(11) =', a.getValue(11));
// Every valueIdx where valueIdx % 3 !== 0 is broken. For a 3-comp
// array of 4 tuples, valueIdx ∈ {1,2,4,5,7,8,10,11} — that's 8 of
// the 12 slots, i.e. 66% of the flat-view accesses.
for (let i = 0; i < 12; i++) {
console.log(i, '=>', a.getValue(i));
}
</script>
Fix
const idx = Math.floor(valueIdx / model.numberOfComponents);
One-line change, no API change, no behavior change on the correct
valueIdx % numberOfComponents === 0 path.
Impact
Every VTK.js consumer using .getValue(i) to iterate a flat view of
a multi-component array gets undefined for ~(components - 1) / components of accesses. Single-component arrays are unaffected.
Workaround
Use getComponent(tupleIdx, compIdx) — always correct.
Cross-substrate note
Filed by the vtk-wasm project after our tri-substrate parity harness
surfaced this defect while comparing wasi VTK to VTK.js on a
vtkFloatArray (tests/parity/data-array.parity.mjs). Native VTK's
equivalent method is vtkAOSDataArrayTemplate::GetValue(vtkIdType)
which correctly floors via integer division.
vtkDataArray.getValuereads past array end on non-first-component indicesSummary
vtkDataArray.getValue(valueIdx)computes its tuple index asvalueIdx / model.numberOfComponentswithout flooring. AnyvalueIdxthat isn't a multiple of
numberOfComponentsreads out of bounds andreturns
undefined(or the wrong value if adjacent bytes happen tocontain a live entry).
File / line
Sources/Common/Core/DataArray/index.js:152-156Downstream
getComponent(idx, comp)atCommon/Core/DataArray.js:145-147computesvalues[idx * numberOfComponents + comp], so a non-integeridxbecomes a non-integer array subscript, resolving to
undefinedunderTypedArray[]access.Minimal reproducer
Fix
One-line change, no API change, no behavior change on the correct
valueIdx % numberOfComponents === 0path.Impact
Every VTK.js consumer using
.getValue(i)to iterate a flat view ofa multi-component array gets
undefinedfor ~(components - 1) / componentsof accesses. Single-component arrays are unaffected.Workaround
Use
getComponent(tupleIdx, compIdx)— always correct.Cross-substrate note
Filed by the vtk-wasm project after our tri-substrate parity harness
surfaced this defect while comparing wasi VTK to VTK.js on a
vtkFloatArray(tests/parity/data-array.parity.mjs). Native VTK'sequivalent method is
vtkAOSDataArrayTemplate::GetValue(vtkIdType)which correctly floors via integer division.