Repository navigation
BUG: C10: Parameter DataType wider than the execute cast in 9 filters #1870
Description
Summary
Some filters accept more DataTypes or values in their parameters than their execute code can handle. Preflight passes, and then execute casts a value or a DataArray to a type that is too narrow, or dispatches to a function that rejects the type. The result is a crash, undefined behavior that gives wrong output, or an unclear execute error.
As of develop 419f9db (2026-10-21), there are 9 open findings in 9 filters: 2 CRASH, 4 WRONG_DATA, 3 MINOR. Each finding is a candidate. It was found and re-checked by reading code, not by running it.
This class overlaps the open issues #1833 ("Enforce array-kind contracts in ArraySelectionParameter") and #1821. The parameter-level part of the fix must follow what #1833 decides.
Mechanism
The class has four sub-patterns:
- Numeric parameter cast to the array type without a range check (
ThresholdImageFilter,ThresholdMaximumConnectedComponentsImageFilter). Afloat64parameter is cast withstatic_cast<T>to the pixel type. A value outside the range ofT(for example 1000 foruint8, or -1.0, or NaN) is undefined behavior in C++. The threshold then uses an unexpected value. - Random distribution type narrower than the array type (
InitializeDataFilter,InitializeImageGeomCellDataFilter). The code usesstd::uniform_int_distribution<int64>orstd::uniform_int_distribution<>(int) for every integer type. For UInt32, Int64 or UInt64 the limits narrow, the lower bound becomes larger than the upper bound, and the values are garbage. - Allowed DataTypes include a type that execute does not handle (
FlyingEdges3DFilter,WriteVtkStructuredPointsFilter,MapPointCloudToRegularGridFilter,UncertainRegularGridSampleSurfaceMeshFilter). The parameter allows boolean or uint8 or a small integer type. Execute then calls a dispatch function that throws for bool, casts the DataArray toBoolArrayand dereferences null, or stores face indices in the label type so that they wrap. - The parameter does not enforce its own allowed DataTypes (
CombineTransformationMatricesFilter).MultiArraySelectionParameter::validatePathschecks existence, array type and component shape, but notm_AllowedDataTypes. A float64 DataArray passes preflight, and execute throwsbad_cast.
Example of sub-pattern 1: ThresholdImageFilter casts the three float64 parameters to the pixel type with no check:
ThresholdImageFilter.cpp#L27-L34
template <class T, class U>
auto makeMapOp() const
{
const T lowerT = static_cast<T>(lower);
const T upperT = static_cast<T>(upper);
const U outsideValueU = static_cast<U>(outsideValue);
return [lowerT, upperT, outsideValueU](T value) -> U { return (value < lowerT || value > upperT) ? outsideValueU : static_cast<U>(value); };
}With uint8 input and Upper = 1000 (meant as "no upper limit"), static_cast<uint8>(1000.0) is undefined behavior. On common platforms the result is 232, so pixels above 232 are set to Outside Value (not confirmed by running it).
A fix of sub-pattern 3 is in open PR #1766 for ComputeNumFeaturesFilter. The PR narrows the parameter to the type that execute uses:
params.insert(std::make_unique<ArraySelectionParameter>(k_FeaturePhasesArrayPath_Key, "Feature Phases", "Array specifying which Ensemble each Feature belongs",
DataPath({"DataContainer", "Feature Data", "Phases"}), ArraySelectionParameter::AllowedTypes{DataType::int32},
ArraySelectionParameter::AllowedComponentShapes{{1}}));Rule
Every DataType and every value that a parameter accepts must work in execute, or preflight must reject it with a clear error.
Decision needed for the parameter-level part (sub-patterns 3 and 4). Follow what #1833 decides. The options are:
- Narrow the parameter. Remove the DataTypes that execute does not support (for example use
GetAllNumericTypes()instead ofGetAllDataTypes()). This is the smallest change. - Support the type in execute. For example write bool as 0/1 in
WriteVtkStructuredPointsFilter, or useMaskCompareUtilities::InstantiateMaskCompareinMapPointCloudToRegularGridFilter. - Enforce the declared contract in the parameter. Make
MultiArraySelectionParameter::validatePathscheckm_AllowedDataTypes, asArraySelectionParameterdoes forIDataArrayselections. This is the shared fix that BUG: Enforce array-kind contracts in ArraySelectionParameter #1833 tracks.
For sub-patterns 1 and 2 no shared decision is needed. Values must be range-checked in preflight (the DataType of the selected DataArray is known in preflight), and execute must use a distribution or comparison type that holds the full range of the array type.
How to fix
- Sub-pattern 1: in preflight, read the DataType of the selected DataArray. Reject NaN and values outside
[std::numeric_limits<T>::lowest(), std::numeric_limits<T>::max()]with a clear error, or clamp them infloat64before the cast. Compare infloat64where the semantics allow it. - Sub-pattern 2: use
std::uniform_int_distribution<T>for integralT. The C++ standard does not allow 8-bit types inuniform_int_distribution, so useint16/uint16forint8/uint8. Range-check the user range in preflight. - Sub-pattern 3: choose an option from the Rule after BUG: Enforce array-kind contracts in ArraySelectionParameter #1833 decides. For
UncertainRegularGridSampleSurfaceMeshFilter, store face indices asusize(orMeshIndexType) whatever the label type; this does not depend on BUG: Enforce array-kind contracts in ArraySelectionParameter #1833. The code is insrc/simplnx/Utilities/SampleSurfaceMesh.cpp. At 419f9db onlyUncertainRegularGridSampleSurfaceMeshderives fromSampleSurfaceMesh. - Sub-pattern 4: fix
MultiArraySelectionParameter::validatePathsas part of the BUG: Enforce array-kind contracts in ArraySelectionParameter #1833 work, or narrow the dispatch inCombineTransformationMatricesFilteruntil then. - 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. - Remove any execute-time check that the new preflight check makes unreachable. Do not keep duplicate checks.
How to test
- Write the test first. It must fail on develop.
- Sub-pattern 1: preflight test with
uint8input and an out-of-range or NaN parameter value. Assert the new error code, or, if the value is clamped, an execute test whose expected output is computed by hand. - Sub-pattern 2: execute test with Random fill on UInt32, Int64 and UInt64 DataArrays. Assert that every value is inside the requested range. Use a fixed seed.
- Sub-pattern 3: run the filter with the DataType that fails today (bool, uint8, int8). Assert either a clear preflight error or correct output.
- Sub-pattern 4: preflight test with float64 matrices. Assert a preflight error, not execute error -2.
- Output values need an independent expected result (oracle), not the code's own output.
Conventions
- One PR for the whole class. One commit per filter.
- Commit prefix
BUG:. Add aSigned-off-byline. 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 · MapPointCloudToRegularGridFilter · MapPointCloudToRegularGridFilter.cpp:74 — The Mask allows uint8, but Manual mode casts it to
BoolArrayand dereferences null before the check.MapPointCloudToRegularGridFilter#1 - CRASH · UncertainRegularGridSampleSurfaceMeshFilter · SampleSurfaceMesh.cpp:253 — Face indices are stored as the label type; int8/uint8 labels wrap above 127/255 faces and index face bounds out of bounds.
UncertainRegularGridSampleSurfaceMeshFilter#2 - WRONG_DATA · InitializeDataFilter · InitializeData.cpp:275 — Random fill uses
uniform_int_distribution<int64>; for UInt64 the maximum becomes -1, so values are garbage.InitializeDataFilter#4 - WRONG_DATA · InitializeImageGeomCellDataFilter · InitializeImageGeomCellData.cpp:83 — Random fill uses
uniform_int_distribution<>(int); UInt32, Int64 and UInt64 limits narrow, so values are garbage.InitializeImageGeomCellDataFilter#3 - WRONG_DATA · ThresholdImageFilter · ThresholdImageFilter.cpp:30 — Lower, Upper and Outside Value are cast to the pixel type unchecked; Upper = 1000 on uint8 is undefined and shifts the threshold.
ThresholdImageFilter#1 - WRONG_DATA · ThresholdMaximumConnectedComponentsImageFilter · ImageProcessingFilterUtilities.hpp:4549 — Upper Boundary is clamped only from above; -1.0 or NaN on uint8 is undefined, and 64-bit types cast before
std::min.ThresholdMaximumConnectedComponentsImageFilter#1 - MINOR · CombineTransformationMatricesFilter · MultiArraySelectionParameter.cpp:124 —
MultiArraySelectionParameterignores allowed DataTypes; float64 matrices pass preflight, then execute fails with error -2.CombineTransformationMatricesFilter#1 - MINOR · FlyingEdges3DFilter · FlyingEdges3D.cpp:100 — The parameter allows bool, but
ExecuteNeighborFunctionthrows for bool; the run fails with error -2.FlyingEdges3DFilter#4 - MINOR · WriteVtkStructuredPointsFilter · WriteVtkStructuredPoints.cpp:427 — The parameter allows bool, but
ExecuteDataFunctionNoBoolthrows for a BoolArray; the run fails with an unclear error.WriteVtkStructuredPointsFilter#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 in open PR BUG: Enforce component shapes and types that filters read #1766:
- ComputeNumFeaturesFilter
ComputeNumFeaturesFilter#1
- ComputeNumFeaturesFilter
Related issues
- BUG: Enforce array-kind contracts in ArraySelectionParameter #1833: Enforce array-kind contracts in ArraySelectionParameter. The parameter-level fix in this issue must follow what BUG: Enforce array-kind contracts in ArraySelectionParameter #1833 decides.
- BUG: Numeric filters can throw after accepting nonnumeric arrays #1821: Numeric filters can throw after accepting nonnumeric arrays. This is the original defect report for BUG: Enforce array-kind contracts in ArraySelectionParameter #1833.
- BUG: Enforce component shapes and types that filters read #1766 (open PR): enforces component shapes and DataTypes that filters read; it fixes
ComputeNumFeaturesFilter#1of this class.
Out of scope
- Findings in filters that have a V&V report are excluded: 1 finding in ReplaceElementAttributesWithNeighborValuesFilter. It is handled on a separate V&V branch.
- StringArray and NeighborList selections in
ArraySelectionParameter. BUG: Enforce array-kind contracts in ArraySelectionParameter #1833 and BUG: Numeric filters can throw after accepting nonnumeric arrays #1821 track them.
Activity
[-]BUG: Parameter DataType wider than the execute cast in 9 filters[/-][+]BUG: C10: Parameter DataType wider than the execute cast in 9 filters[/+]