Skip to content

BUG: C03: Element DataArray tuple count not checked in 21 filters #1863

Open

Description

@imikejackson

Summary

Many filters read an element DataArray (one value per vertex, edge, face or cell) but do not compare its number of tuples with the number of elements in the selected geometry. Preflight accepts a DataArray from any Attribute Matrix, so the tuple count can be different from the geometry's element count. Execute then loops over the geometry elements and reads the DataArray past its end, or it pairs values with the wrong elements.

As of develop 419f9db (2026-10-21), there are 23 open findings in 21 filters: 18 CRASH, 5 WRONG_DATA, 0 MINOR. Each finding is a candidate. It was found and re-checked by reading code, not by running it.

Mechanism

The selection parameters check the DataType and the component shape of a DataArray. They do not check that the DataArray has one tuple for each element of the geometry. Many triangle-mesh filters let the user select Face Labels, Face Normals, Node Types or a Mask from any Attribute Matrix. The algorithm then takes its loop count from the geometry (or from a different input DataArray) and indexes the DataArray with that count.

Example: SharedFeatureFace loops over the Triangle Geometry faces and reads Face Labels with no tuple-count check. Preflight sizes its output from the Face Labels tuple shape and never relates it to the face count.

SharedFeatureFace.cpp#L140-L161

  const auto& triangleGeom = m_DataStructure.getDataRefAs<TriangleGeom>(m_InputValues->TriangleGeometryPath);
  usize totalPoints = triangleGeom.getNumberOfFaces();

  const Int32Array& surfaceMeshFaceLabels = m_DataStructure.getDataRefAs<Int32Array>(m_InputValues->FaceLabelsArrayPath);
  // ...
  for(usize t = 0; t < totalPoints; ++t)
  {
    // ...
    int32 fl0 = surfaceMeshFaceLabels[t * 2];
    int32 fl1 = surfaceMeshFaceLabels[t * 2 + 1];

If the Triangle Geometry has 1000 faces and the selected Face Labels DataArray has 500 tuples, the loop reads 500 tuples past the end of the DataArray.

The correct form already exists on develop. ExtractTripleLinesFilter (added in #1729) compares the tuple counts in preflight and accepts the DataArray from any Attribute Matrix:

ExtractTripleLinesFilter.cpp#L147-L151

  if(faceLabels.getNumberOfTuples() != triangleGeom.getNumberOfFaces())
  {
    return {MakeErrorResult<OutputActions>(-57402, fmt::format("Face Labels array '{}' has {} tuples but Triangle Geometry '{}' has {} faces. They must match.", pFaceLabelsArrayPath.toString(),
                                                               faceLabels.getNumberOfTuples(), pTriangleGeometryPath.toString(), triangleGeom.getNumberOfFaces()))};
  }

For Image Geometry cells, ComputeSurfaceAreaToVolumeFilter (fixed in #1817) uses the shared helper ValidateCellTupleCount (ComputeSurfaceAreaToVolumeFilter.cpp#L120-L124). PR #1840 uses the same helper in more than 30 other filters.

Note: RegularGridSampleSurfaceMeshFilter was fixed in #1767 with a stricter check. It requires Face Labels to be a child of the geometry's Face Data Attribute Matrix (IsChildOfAttributeMatrix). The decided rule below is a tuple-count check that accepts any Attribute Matrix. Do not copy the parent check into new fixes.

Two findings are variants of the same gap:

  • ApplyTransformationToGeometryFilter: the selected Cell Attribute Matrix supplies the array names, but the paths are built under the Image Geometry's own Cell Data. The two are never required to match.
  • ComputeFeatureRectFilter: the algorithm reads the Feature Ids tuple shape as {z, y, x} with no rank check. A Feature Ids DataArray from an Attribute Matrix of rank 1 or 2 reads past the end of the dimension vector.

Rule

A filter that only READS an element DataArray needs a matching tuple count, checked in preflight. The DataArray may be in any Attribute Matrix.

  • The tuple count must equal the number of elements the algorithm indexes: cells for an Image or RectGrid Geometry, faces for a Triangle or Quad Geometry, edges for an Edge Geometry, vertices for Node Types, vertex masks and Vertex Data.
  • Inputs that the algorithm indexes together (for example Face Labels, Face Normals and Face Areas) must also have the same tuple count as each other.
  • A spatial filter that MODIFIES Feature Ids, its sibling arrays or the Feature Attribute Matrix is different. It requires Feature Ids in the geometry's Cell Attribute Matrix (see BUG: Require Feature Ids in the geometry's Cell Attribute Matrix for filters that modify it #1829).

How to fix

  1. In preflightImpl, get the geometry and each element DataArray that the algorithm indexes by element.
  2. For Image and RectGrid Geometry cells, call ValidateCellTupleCount from src/simplnx/Utilities/DataArrayUtilities.hpp. On develop it takes (const IGridGeometry&, geometryPath, tupleCount, dataPath, errorCode). PR BUG: Check that grid-geometry cell inputs have one tuple per cell #1840 adds an overload that takes (dataStructure, geometryPath, std::vector<DataPath>, errorCode). Use the overload if BUG: Check that grid-geometry cell inputs have one tuple per cell #1840 is merged first.
  3. For Triangle, Quad, Edge and Vertex geometries there is no shared helper on develop. Compare getNumberOfTuples() with getNumberOfFaces(), getNumberOfEdges() or getNumberOfVertices(), as ExtractTripleLinesFilter does. PR BUG: Check that grid-geometry cell inputs have one tuple per cell #1840 has a file-local validateVertexTupleCount in PartitionGeometryFilter.cpp. If more than two filters need the same check, add a shared helper next to ValidateCellTupleCount in one first commit.
  4. For sibling inputs, also call CheckArraysHaveSameTupleCount (DataArrayUtilities.hpp).
  5. For "Copy All" or "Copy Selected" inputs (RemoveFlaggedEdgesFilter, RemoveFlaggedTrianglesFilter), check every selected DataArray, and every child of a selected Attribute Matrix, against the matching element count.
  6. Write an error message that names both paths and both counts. Example: "The selected DataArray 'X' has 12 tuples, but the Triangle Geometry 'Y' has 24 faces."
  7. Error codes: use a new code in the filter's existing range. Filter error codes are more negative than -10000, and each filter uses one range (first code -47500 means all codes -47500..-47599). Check that the code is unique with git grep -nE -- "-NNNN([^0-9]|$)" (do not use \b) and also in the open PR refs.
  8. Remove any execute-time check that the new preflight check makes unreachable. Do not keep duplicate checks.
  9. Two findings need more than a preflight count check:
    • ComputeMomentInvariants2DFilter#3: Feature Rect corners are values. Check each corner against the Image Geometry dimensions at the start of execute.
    • ComputeFeatureRectFilter#1: require a Feature Ids tuple shape of rank 3 in preflight, or use the Image Geometry dimensions instead of the tuple shape.

How to test

  • Write the test first. It must fail on develop.
  • Preflight test: build a small geometry (for example a Triangle Geometry with 4 faces). Put the input DataArray in a different Attribute Matrix with fewer tuples (for example 2). Assert that preflight fails with the new error code. Add a second case with more tuples for the WRONG_DATA findings.
  • Positive test: the same input with a matching tuple count in a different Attribute Matrix must pass preflight. This proves that the check does not require a parent Attribute Matrix.
  • If the filter has more than one algorithm (for example Direct and Scanline), run the test for each one through the existing algorithm-selection test scope.
  • 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 a Signed-off-by line. Do not add AI attribution.
  • Every fix starts with a unit test that fails on develop. If you cannot reproduce a finding, write that with the reason in the PR and on this issue. Do not force a fix.
  • Value checks need an independent expected result (oracle), not the code's own output.
  • Run ctest serially. Never use -j; the tests depend on each other.
  • Format with clang-format 19.1.1 and the repository .clang-format.
  • Do NOT add documentation sentences about input requirements to the filter pages. 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 · ApplyTransformationToGeometryFilter · GeometryTransformation.cpp:172 — Names come from the selected Cell Attribute Matrix but paths use the geometry's Cell Data; a missing name throws, a subset gives a null DataArray. ApplyTransformationToGeometryFilter#4
  • CRASH · ComputeFeatureRectFilter · ComputeFeatureRect.cpp:62 — Feature Ids tuple shape is read as 3 dimensions; a shape of {N} or {ny,nx} reads past the end of the dimension vector. ComputeFeatureRectFilter#1
  • CRASH · ComputeGBCDMetricBasedFilter · ComputeGBCDMetricBased.cpp:498 — Face count comes from Face Areas; a larger Face Areas (or a short Node Types) makes the other face arrays read out of bounds. ComputeGBCDMetricBasedFilter#1
  • CRASH · ComputeGBPDMetricBasedFilter · ComputeGBPDMetricBased.cpp:586 — Face count comes from Face Areas; a larger Face Areas (or a short Node Types) makes the other face arrays read out of bounds. ComputeGBPDMetricBasedFilter#2
  • CRASH · ComputeMomentInvariants2DFilter · ComputeMomentInvariants2D.cpp:302 — Feature Ids is not compared with the cells (PR BUG: Check that grid-geometry cell inputs have one tuple per cell #1840 adds it); Feature Rect corners from a larger geometry still read Feature Ids out of bounds. ComputeMomentInvariants2DFilter#3
  • CRASH · ComputeShapesTriangleGeomFilter · ComputeShapesTriangleGeom.cpp:59 — Face Labels with a tuple count different from the face count makes execute read the triangle list out of bounds. ComputeShapesTriangleGeomFilter#4
  • CRASH · ComputeTriangleGeomVolumesFilter · ComputeTriangleGeomVolumesFilter.cpp:84 — Face Labels with fewer tuples than faces is read out of bounds for every triangle. ComputeTriangleGeomVolumesFilter#2
  • CRASH · ComputeVertexToTriangleDistancesFilter · ComputeVertexToTriangleDistancesFilter.cpp:90 — Triangle Normals with fewer tuples than faces is read out of bounds (PR BUG: Enforce component shapes and types that filters read #1766 adds only the component shape). ComputeVertexToTriangleDistancesFilter#2
  • CRASH · CreateAMScanPathsFilter · CreateAMScanPaths.cpp:378 — Slice Ids or Region Ids with fewer tuples than edges (for example Vertex Data) is read out of bounds. CreateAMScanPathsFilter#2
  • CRASH · FeatureFaceCurvatureFilter · FeatureFaceCurvatureFilter.cpp:144 — The preflight array list is never used; any of the four face inputs with fewer tuples than faces is read out of bounds. FeatureFaceCurvatureFilter#1
  • CRASH · HierarchicalSmoothFilter · HierarchicalSmoothFilter.cpp:85 — Face Labels with fewer tuples than faces, or Node Type with fewer tuples than vertices, is read out of bounds. HierarchicalSmoothFilter#3
  • CRASH · LaplacianSmoothingFilter · LaplacianSmoothing.cpp:114 — Node Type with fewer tuples than vertices makes lambdas[i] read out of bounds (PR BUG: Fix OOC Review Findings #1764 checks only its new out-of-core path). LaplacianSmoothingFilter#1
  • CRASH · PointSampleTriangleGeometryFilter · PointSampleTriangleGeometryFilter.cpp:153 — Face Areas, Mask or a transfer array with fewer tuples than faces is read out of bounds. PointSampleTriangleGeometryFilter#3
  • CRASH · RemoveFlaggedEdgesFilter · RemoveFlaggedEdgesFilter.cpp:161 — Copied arrays come from the selected Attribute Matrix but outputs use the geometry size; a shorter source is read out of bounds. RemoveFlaggedEdgesFilter#2
  • CRASH · RemoveFlaggedTrianglesFilter · RemoveFlaggedTrianglesFilter.cpp:169 — Copied arrays come from the selected Attribute Matrix but outputs use the geometry size; a shorter source is read out of bounds. RemoveFlaggedTrianglesFilter#2
  • CRASH · SharedFeatureFaceFilter · SharedFeatureFace.cpp:160 — Face Labels with fewer tuples than faces is read and written out of bounds. SharedFeatureFaceFilter#2
  • CRASH · UncertainRegularGridSampleSurfaceMeshFilter · UncertainRegularGridSampleSurfaceMeshFilter.cpp:121 — Face Labels with more tuples than faces reads face bounding boxes out of bounds (the RegularGrid sibling is fixed). UncertainRegularGridSampleSurfaceMeshFilter#3
  • CRASH · VerifyTriangleWindingFilter · VerifyTriangleWindingFilter.cpp:117 — Labels with fewer tuples than faces is read out of bounds in the in-core repair (PR BUG: Fix OOC Review Findings #1764 checks only its new out-of-core path). VerifyTriangleWindingFilter#1
  • WRONG_DATA · ComputeGBCDFilter · ComputeGBCDFilter.cpp:102 — Face Normals or Face Areas with more tuples than Face Labels pass preflight and are paired with the wrong faces. ComputeGBCDFilter#3
  • WRONG_DATA · PointSampleEdgeGeometryFilter · PointSampleEdgeGeometryFilter.cpp:130 — A transfer array with more tuples than edges (for example Vertex Data) is silently read by edge index. PointSampleEdgeGeometryFilter#2
  • WRONG_DATA · RemoveFlaggedEdgesFilter · RemoveFlaggedEdges.cpp:109 — Mask is never checked; a longer Mask (for example Vertex Data) removes the wrong edges, a shorter one throws out_of_range. RemoveFlaggedEdgesFilter#1
  • WRONG_DATA · RemoveFlaggedTrianglesFilter · RemoveFlaggedTriangles.cpp:108 — Mask is never checked; a longer Mask removes the wrong triangles, a shorter one throws out_of_range. RemoveFlaggedTrianglesFilter#1
  • WRONG_DATA · RemoveFlaggedVerticesFilter · RemoveFlaggedVerticesFilter.cpp:102 — The validateNumberOfTuples list is empty; a longer Mask gives the wrong vertex count, a shorter one throws out_of_range. RemoveFlaggedVerticesFilter#1

Already fixed or in progress

These findings of this class are fixed on develop or in an open PR. They are not in the checklist.

  • Fixed on develop by BUG: Fix defects in seven SimplnxCore filters #1763 (8bed04c):
    • SliceTriangleGeometryFilter SliceTriangleGeometryFilter#2
    • WriteStlFileFilter WriteStlFileFilter#2
  • Fixed on develop by BUG: Fix wrong output and invalid input handling in 19 filters #1765 (1986127):
    • ComputeLargestCrossSectionsFilter ComputeLargestCrossSectionsFilter#1
    • SurfaceNetsFilter SurfaceNetsFilter#2
  • Fixed on develop by BUG: Fix remaining input checks in 6 filters and correct 3 docs #1767 (53c678d):
    • RegularGridSampleSurfaceMeshFilter RegularGridSampleSurfaceMeshFilter#4
  • Fixed on develop by BUG: Correct surface-area and sphericity calculations #1817 (01809c2):
    • ComputeSurfaceAreaToVolumeFilter ComputeSurfaceAreaToVolumeFilter#2
  • Fixed on develop by BUG: Require Feature Ids in the geometry's Cell Attribute Matrix for filters that modify it #1829 (419f9db):
    • ErodeDilateBadDataFilter ErodeDilateBadDataFilter#2
    • ErodeDilateCoordinationNumberFilter ErodeDilateCoordinationNumberFilter#2
    • FillBadDataFilter FillBadDataFilter#1
    • KeepRemoveRankedFeaturesFilter KeepRemoveRankedFeaturesFilter#2
    • QuickSurfaceMeshFilter QuickSurfaceMeshFilter#2
    • RemoveFlaggedFeaturesFilter RemoveFlaggedFeaturesFilter#2
    • RequireMinNumNeighborsFilter RequireMinNumNeighborsFilter#1
    • RequireMinimumSizeFeaturesFilter RequireMinimumSizeFeaturesFilter#2
  • Fixed in open PR BUG: Check that grid-geometry cell inputs have one tuple per cell #1840:
    • AddBadDataFilter AddBadDataFilter#2
    • AlignSectionsFeatureCentroidFilter AlignSectionsFeatureCentroidFilter#2
    • AlignSectionsMisorientationFilter AlignSectionsMisorientationFilter#1
    • AlignSectionsMutualInformationFilter AlignSectionsMutualInformationFilter#2
    • BadDataNeighborOrientationCheckFilter BadDataNeighborOrientationCheckFilter#1
    • ComputeBoundingBoxStatsFilter ComputeBoundingBoxStatsFilter#4
    • ComputeEuclideanDistMapFilter ComputeEuclideanDistMapFilter#2
    • ComputeFeatureCentroidsFilter ComputeFeatureCentroidsFilter#1
    • ComputeFeatureNeighborsFilter ComputeFeatureNeighborsFilter#1
    • ComputeFeatureReferenceCAxisMisorientationsFilter ComputeFeatureReferenceCAxisMisorientationsFilter#3
    • ComputeFeatureSizesFilter ComputeFeatureSizesFilter#1
    • ComputeKernelAvgMisorientationsFilter ComputeKernelAvgMisorientationsFilter#1
    • ComputeShapesFilter ComputeShapesFilter#5
    • ComputeSurfaceFeaturesFilter ComputeSurfaceFeaturesFilter#1
    • ErodeDilateMaskFilter ErodeDilateMaskFilter#1
    • ExtractFeatureBoundaries2DFilter ExtractFeatureBoundaries2DFilter#1
    • FlyingEdges3DFilter FlyingEdges3DFilter#2
    • IdentifySampleFilter IdentifySampleFilter#2
    • InitializeImageGeomCellDataFilter InitializeImageGeomCellDataFilter#1
    • M3CSurfaceMeshingFilter M3CSurfaceMeshingFilter#1
    • NearestPointFuseRegularGridsFilter NearestPointFuseRegularGridsFilter#1
    • NeighborOrientationCorrelationFilter NeighborOrientationCorrelationFilter#2
    • PartitionGeometryFilter PartitionGeometryFilter#1
    • ScalarSegmentFeaturesFilter ScalarSegmentFeaturesFilter#2
    • WriteAbaqusHexahedronFilter WriteAbaqusHexahedronFilter#1
    • WriteAvizoRectilinearCoordinateFilter WriteAvizoRectilinearCoordinateFilter#1
    • WriteAvizoUniformCoordinateFilter WriteAvizoUniformCoordinateFilter#1
    • WriteINLFileFilter WriteINLFileFilter#2
    • WriteLosAlamosFFTFilter WriteLosAlamosFFTFilter#1
    • WriteSPParksSitesFilter WriteSPParksSitesFilter#1
  • Fixed on a local branch that is not yet a pull request. Confirm that these fixes reach develop:
    • CropImageGeometryFilter CropImageGeometryFilter#2
    • ResampleImageGeomFilter ResampleImageGeomFilter#4

Related issues

Out of scope

  • Findings in filters that have a V&V report are excluded: 2 findings in ComputeTriangleGeomCentroidsFilter and ReplaceElementAttributesWithNeighborValuesFilter. They are handled on a separate V&V branch.
  • Filters that MODIFY Feature Ids or the Feature Attribute Matrix. Their rule (Feature Ids in the geometry's Cell Attribute Matrix) was merged in BUG: Require Feature Ids in the geometry's Cell Attribute Matrix for filters that modify it #1829.
  • Execute-time checks of Feature Id values against the Feature Attribute Matrix. That is a different class.

Activity

  1. changed the title [-]BUG: Element DataArray tuple count not checked in 21 filters[/-] [+]BUG: C03: Element DataArray tuple count not checked in 21 filters[/+] on Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions