Repository navigation
BUG: C02: Component shape not declared in 6 filters #1862
Description
Summary
Some filters read an input DataArray with a fixed number of components (usually 1, sometimes a fixed element count), but the parameter that selects the DataArray does not declare that component shape. Preflight then accepts a DataArray with a different number of components. The algorithm reads the values with the wrong stride, or uses the total number of values as the number of tuples. This gives an out-of-bounds read or write, or wrong output with no error.
Open findings as of develop 419f9db (2026-10-21): 3 CRASH, 3 WRONG_DATA, 0 MINOR, in 6 filters. Each finding is a candidate: it was found and re-checked by reading code, not by running it.
Mechanism
The algorithm assumes 1 component per tuple, but the ArraySelectionParameter accepts any component shape. Example from FlyingEdges3DFilter: the parameter declares only the data types.
FlyingEdges3DFilter.cpp#L65-L66
params.insert(std::make_unique<ArraySelectionParameter>(k_SelectedDataArrayPath_Key, "Data Array to Contour", "This is the data that will be checked for the contouring iso value", DataPath{},
GetAllDataTypes()));A uint8 RGB DataArray with 3 components and one tuple per Image Geometry cell passes preflight. The contour algorithm reads it as 3 times more scalar values, so the surface is wrong. A tuple-count check (for example in PR #1840) does not find this, because the number of tuples is correct.
A related form: the algorithm uses getSize() (tuples x components) as the number of tuples. In AddBadData, a 3-component DataArray with one tuple per cell drives writes up to 3 times the cell count.
Fixed form (PR #1763, commit 8bed04c): WriteStlFileFilter now declares the component shape that its algorithm reads. ArraySelectionParameter::validate then rejects any other shape before preflight runs.
WriteStlFileFilter.cpp#L75-L76
params.insert(std::make_unique<ArraySelectionParameter>(k_FeatureIdsPath_Key, "Face labels", "The triangle feature ids array to order/index files by", DataPath{},
ArraySelectionParameter::AllowedTypes{DataType::int32}, ArraySelectionParameter::AllowedComponentShapes{{2}}));Two findings do not select the array through a parameter, so the parameter cannot hold the rule:
ImportFijiMontageFilterreads tiles from files. The tile component count is known from the image metadata in preflight, but nothing checks it before grayscale conversion.ApplyTransformationToGeometryFilterchecks that the pre-computed matrix has 16 elements only inside the Image Geometry branch. For node geometries, execute reads 16 values from a DataArray of any size.
Rule
- If an algorithm assumes a component count or stride for an input DataArray, the
ArraySelectionParameterorMultiArraySelectionParameterthat selects it MUST declare that shape withAllowedComponentShapes{{N}}. The parameter is the check. Parameter validation runs before the filter's preflight, so do not add a second preflight or execute check for the same component shape. - Layering: structure (component shape, DataType) goes in the parameter. Relationships between inputs (sibling tuple counts, tuple count vs geometry element count) go in preflight. Checks that need array values go in execute.
- Algorithms use
getNumberOfTuples()when they mean tuples, notgetSize(). - When the input does not come from a parameter (a file, or a mode-dependent input), the check goes in preflight.
- Decision needed for
ComputeArrayHistogramFilter: the component limit applies only when Use Mask Array is on, so a parameter-level shape cannot express it. Options: (a) restrict Input Data Arrays to{{1}}for all modes; (b) return a preflight error for multi-component inputs only when Use Mask Array is on; (c) apply each mask value to all components of its tuple in the algorithm.
How to fix
- Add
AllowedComponentShapes{{1}}(or the shape the algorithm reads) to the parameter. BothArraySelectionParameterandMultiArraySelectionParameterhave this argument at 419f9db, and both validate it. - In the algorithm, replace
getSize()withgetNumberOfTuples()where the code means tuples (AddBadData.cpplines 135 and 232). - For
NormalizeImageFilter, passrequireScalar = truetoImageProcessing::PreflightFullOverwriteImageFilter(the argument exists at 419f9db), or addAllowedComponentShapes{{1}}to the parameter. Use one of the two, not both. - For
ImportFijiMontageFilter, checkImageMetadata::numComponentsinpreflightImplwhen Convert To GrayScale is on. Decide if a tile with fewer than 3 components is an error or a warning plus skip. Also add a component check toPreflightColorToGrayScale(src/simplnx/Utilities/ImageProcessing/ColorToGrayScale.cpp);ReadImageStackFilterandConvertColorToGrayScaleFilteralso call it. - For
ApplyTransformationToGeometryFilter, move the 16-element check (error -82019) out of theif(imageGeomPtr != nullptr)branch so that it runs for every geometry type. - Remove any execute-time component check that the new parameter or preflight check makes unreachable.
- New filter error codes must be more negative than -10000 and stay in the filter's existing range. Check that each new code is unique with
git grep -nE -- "-NNNN([^0-9]|$)", and also in open PR branches.
How to test
- Write the test first. It must fail on develop.
- Parameter rule: build a DataStructure with a DataArray that has the correct number of tuples but the wrong number of components (for example 3 components on an Image Geometry with 24 cells and 24 tuples). Run preflight. Require a preflight error. For a parameter-level shape the error is
k_Validate_TupleShapeValuefromArraySelectionParameter::validate. ApplyTransformationToGeometryFilter: a Triangle Geometry with Pre-Computed Transformation Matrix set to a Float32 DataArray of 3 tuples x 1 component. Require preflight error -82019.ImportFijiMontageFilter: a TileConfiguration with 8-bit grayscale tiles (1 component, uint8) and Convert To GrayScale on. Require the decided preflight error or warning. Run the test with both in-core and out-of-core stores, because the out-of-core path is the one that over-reads.- If a finding cannot be reproduced, write that in the PR and on this issue, with the reason. Do not force a fix.
- If a fix changes output values, compare them with an independent expected result (oracle), not with the code's own output.
Conventions
- One PR for the whole class. One commit per filter.
- Commit prefix
BUG:. Add aSigned-off-byline. No AI attribution. - Every fix starts with a unit test that fails on develop.
- Value checks need an independent expected result (oracle), not the code's own output.
- Run
ctestserially. Never use-j(tests depend on each other). - Format with clang-format 19.1.1 and the repository
.clang-format. - Do NOT add per-filter documentation sentences about input requirements. The generated parameter table documents them.
- If a fix touches a filter that has a V&V report (
src/Plugins/<Plugin>/vv/<Filter>.md), update every file of that filter's V&V package (report, deviations, provenance, test tables). V&V documents reference DREAM3D 6.5.171 only. - Rebase on develop. Never merge develop into the branch.
Checklist
- CRASH · AddBadDataFilter · AddBadDataFilter.cpp:76 — Boundary Euclidean Distances has no component shape; a 3-component int32 DataArray with one tuple per cell passes, and the algorithm uses
getSize()as the point count, so it writes up to 3 times the cell count.AddBadDataFilter#3 - CRASH · ApplyTransformationToGeometryFilter · GeometryTransformation.cpp:84 — the 16-element check for the pre-computed matrix runs only for Image Geometry; with a Triangle (or other node) Geometry and a 3-element Float32 DataArray, execute reads 16 values out of bounds.
ApplyTransformationToGeometryFilter#6 - CRASH · ImportFijiMontageFilter · ImportFijiMontageFilter.cpp:338 — with Convert To GrayScale on and ordinary 8-bit grayscale tiles (1 component), only the data type is checked; the out-of-core luminosity path reads 3 values per tuple past the chunk buffer (in-core it throws).
ImportFijiMontageFilter#1 - WRONG_DATA · ComputeArrayHistogramFilter · ComputeArrayHistogram.cpp:653 — with Use Mask Array on and a 3-component input, the count is tuples but the reader reads flat values, so mask value i gates value i, not tuple i; the histogram is wrong. Decision needed (see Rule).
ComputeArrayHistogramFilter#6 - WRONG_DATA · FlyingEdges3DFilter · FlyingEdges3DFilter.cpp:65 — Data Array to Contour has no component shape; a 3-component Cell Data DataArray (for example IPF Colors) is read as interleaved scalars, so the surface is wrong.
FlyingEdges3DFilter#3 - WRONG_DATA · NormalizeImageFilter · NormalizeImageFilter.cpp:91 — preflight does not pass
requireScalarand Input Cell Data has no component shape; a 3-component RGB DataArray is normalized with one mean and sigma for all channels.NormalizeImageFilter#1
Already fixed or in progress
- PR BUG: Enforce component shapes and types that filters read #1766 (open, "BUG: Enforce component shapes and types that filters read"):
ComputeFeaturePhasesBinaryFilter#2ComputeGBCDPoleFigureFilter#1ComputeKMeansFilter#5ComputeKMedoidsFilter#2ComputeNumFeaturesFilter#3ComputeShapesFilter#4ComputeShapesTriangleGeomFilter#2ComputeVertexToTriangleDistancesFilter#1WriteGBCDGMTFileFilter#3
- PR BUG: Fix wrong output and invalid input handling in 19 filters #1765 (merged, commit 1986127):
ComputeFeatureBoundsFilter#4SilhouetteFilter#3
- PR BUG: Fix defects in seven SimplnxCore filters #1763 (merged, commit 8bed04c):
WriteStlFileFilter#1
Related issues
- BUG: Enforce component shapes and types that filters read #1766: open PR that applies the same rule to other filters. Coordinate so that the two changes do not touch the same filter.
- BUG: Check that grid-geometry cell inputs have one tuple per cell #1840: open PR that adds tuple-count checks. It does not check component counts, so it does not fix this class.
Out of scope
- No findings of this class are in filters with a V&V report (count 0).
- Tuple-count checks (number of tuples vs number of geometry elements) are a different class.
- Findings already fixed on develop or in an open PR (listed above).
Activity
[-]BUG: Component shape not declared in 6 filters[/-][+]BUG: C02: Component shape not declared in 6 filters[/+]