Skip to content

BUG: QuickSurfaceMesh preflight crashes when the grid geometry has no Cell Attribute Matrix #1843

Open

Description

@joeykleingers

Problem

QuickSurfaceMesh crashes during preflight when the selected grid geometry has no Cell Attribute Matrix. Preflight reads the cell count from the geometry's Cell Attribute Matrix without checking that one exists, so it dereferences a null pointer and the process ends with a segmentation fault.

  • Expected: preflight returns a result. With Attempt to Fix Problem Voxels off, Feature Ids that live outside the geometry are allowed, so a consistent input (one Feature Id per cell) should preflight cleanly.
  • Actual: the application or test process crashes (SIGSEGV).
  • Commit: develop at 2eb6b87.

Steps to Reproduce

  1. Create an Image Geometry with dimensions 4 x 1 x 1 and no Cell Attribute Matrix.
  2. Create an int32 DataArray with 4 tuples and 1 component in a DataGroup outside the geometry, for example Elsewhere/Feature Ids.
  3. Preflight Quick Surface Mesh with:
    • Grid Geometry: the Image Geometry from step 1
    • Cell Feature Ids: the array from step 2
    • Attempt to Fix Problem Voxels: off
    • every other parameter at its default
  4. Preflight crashes.

Cause

QuickSurfaceMeshFilter::preflightImpl() takes the number of cells from the Cell Attribute Matrix:

const auto& gridGeom = dataStructure.getDataRefAs<IGridGeometry>(pGridGeomDataPath);
const usize elementTupleCount = gridGeom.getCellData()->getNumberOfTuples();

  const auto& gridGeom = dataStructure.getDataRefAs<IGridGeometry>(pGridGeomDataPath);

  const usize elementTupleCount = gridGeom.getCellData()->getNumberOfTuples();

IGridGeometry::getCellData() returns nullptr when no Cell Attribute Matrix is assigned. Nothing before this line checks for that:

  • The Grid Geometry parameter only checks the geometry type (Image or Rectilinear Grid).
  • The Cell Feature Ids parameter only checks the data type and component shape. It does not require the array to be in the geometry.

The value is used only to check the tuple count of the Cell Attribute Arrays to Transfer (error -76531):

for(const auto& selectedDataPath : pSelectedDataArrayPaths)
{
// Check that the feature array has the correct tuple count to avoid crashing in execute.
const IDataArray* elementArray = dataStructure.getDataAs<IDataArray>(selectedDataPath);
if(elementArray->getNumberOfTuples() != elementTupleCount)
{
return {MakeErrorResult<OutputActions>(-76531, fmt::format("Cannot copy element data at path '{}'. DataArray does not have the correct tuple count.", selectedDataPath.toString()))};

Verification Unit Test

Add to src/Plugins/SimplnxCore/test/QuickSurfaceMeshFilterTest.cpp, with #include "simplnx/DataStructure/DataGroup.hpp" next to the other DataStructure includes:

TEST_CASE("SimplnxCore::QuickSurfaceMeshFilter: Geometry without a Cell Attribute Matrix", "[SimplnxCore][QuickSurfaceMeshFilter]")
{
  UnitTest::LoadPlugins();

  // The Image Geometry has 4 cells but no Cell Attribute Matrix. Feature Ids lives in a separate DataGroup
  // and has one tuple per cell, so the geometry and the Feature Ids array are consistent.
  DataStructure dataStructure;
  auto* imageGeomPtr = ImageGeom::Create(dataStructure, "Image Geometry");
  REQUIRE(imageGeomPtr != nullptr);
  imageGeomPtr->setDimensions({4, 1, 1});
  imageGeomPtr->setOrigin({0, 0, 0});
  imageGeomPtr->setSpacing({1, 1, 1});
  REQUIRE(imageGeomPtr->getCellData() == nullptr);
  auto* elsewherePtr = DataGroup::Create(dataStructure, "Elsewhere");
  REQUIRE(elsewherePtr != nullptr);
  auto* featureIdsPtr = Int32Array::CreateWithStore<DataStore<int32>>(dataStructure, "Feature Ids", {4}, {1}, elsewherePtr->getId());
  REQUIRE(featureIdsPtr != nullptr);
  (*featureIdsPtr)[0] = 1;
  (*featureIdsPtr)[1] = 1;
  (*featureIdsPtr)[2] = 2;
  (*featureIdsPtr)[3] = 2;

  QuickSurfaceMeshFilter filter;
  Arguments args;
  args.insertOrAssign(QuickSurfaceMeshFilter::k_GridGeometryDataPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry"})));
  args.insertOrAssign(QuickSurfaceMeshFilter::k_CellFeatureIdsArrayPath_Key, std::make_any<DataPath>(DataPath({"Elsewhere", "Feature Ids"})));
  args.insertOrAssign(QuickSurfaceMeshFilter::k_FixProblemVoxels_Key, std::make_any<bool>(false));

  // Preflight must return a result. It must not dereference the missing Cell Attribute Matrix.
  auto preflightResult = filter.preflight(dataStructure, args);
  SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions);
}

Run only this test:

ctest -R "QuickSurfaceMeshFilter: Geometry without a Cell Attribute Matrix" --output-on-failure

Output at 2eb6b87 (simplnx-Rel, macOS arm64):

QuickSurfaceMeshFilterTest.cpp:108: FAILED:
  {Unknown expression after the reported line}
due to a fatal error condition:
  SIGSEGV - Segmentation violation signal

test cases: 1 | 1 failed
assertions: 6 | 5 passed | 1 failed

	842 - SimplnxCore::QuickSurfaceMeshFilter: Geometry without a Cell Attribute Matrix (SEGFAULT)

Proposed Fix

In QuickSurfaceMeshFilter::preflightImpl(), take the cell count from the geometry instead of its Cell Attribute Matrix:

-  const usize elementTupleCount = gridGeom.getCellData()->getNumberOfTuples();
+  const usize elementTupleCount = gridGeom.getNumberOfCells();

getNumberOfCells() is the count the Cell Attribute Matrix must match, so the -76531 check on Cell Attribute Arrays to Transfer behaves the same for valid geometries. A geometry without a Cell Attribute Matrix then has no arrays to transfer, and preflight continues.

Add a Rectilinear Grid case to the test as well, because the filter accepts both geometry types.

Notes

  • Found while reviewing BUG: Require Feature Ids in the geometry's Cell Attribute Matrix for filters that modify it #1829. That PR requires Feature Ids to be in the geometry's Cell Attribute Matrix only when Attempt to Fix Problem Voxels is on, and it adds a check that Feature Ids has one tuple per cell (-76533). Neither check stops this crash when the option is off: the same line is still reached at the PR head. This was found by reading the code; the test was not run on the PR branch.
  • The QuickSurfaceMesh algorithm does not read the Cell Attribute Matrix during execute (checked by a search of Algorithms/QuickSurfaceMesh*.cpp). The only dereference found is this one in preflight.

Activity

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

    FilterAdds a new filterbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions