From cc15831d0a2c0c9128a3e972273317fe32bf8b52 Mon Sep 17 00:00:00 2001 From: Jeff <138259519+jdotc1@users.noreply.github.com> Date: Thu, 22 Oct 2026 19:13:54 -0400 Subject: [PATCH] BUG: Reject transform parameter counts that exceed the metric limit ObjectToObjectMetric::GetNumberOfParameters returned the moving transform's 64-bit IdentifierType count through the metric's unsigned int NumberOfParametersType, so a dense displacement field with more than 2^32 - 1 parameters was silently truncated. Callers then sized the derivative from the truncated count and wrote past its end. Throw instead when the count does not fit. This makes the overflow a clear error; widening the metric API remains open. Addresses #6949. --- .../include/itkObjectToObjectMetric.hxx | 8 +- .../Numerics/Optimizersv4/test/CMakeLists.txt | 1 + .../test/itkObjectToObjectMetricGTest.cxx | 130 ++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 Modules/Numerics/Optimizersv4/test/itkObjectToObjectMetricGTest.cxx diff --git a/Modules/Numerics/Optimizersv4/include/itkObjectToObjectMetric.hxx b/Modules/Numerics/Optimizersv4/include/itkObjectToObjectMetric.hxx index b4bca4d43da..39c98c4a7d6 100644 --- a/Modules/Numerics/Optimizersv4/include/itkObjectToObjectMetric.hxx +++ b/Modules/Numerics/Optimizersv4/include/itkObjectToObjectMetric.hxx @@ -121,7 +121,13 @@ typename ObjectToObjectMetric::GetNumberOfParameters() const { - return this->m_MovingTransform->GetNumberOfParameters(); + const auto numberOfParameters = this->m_MovingTransform->GetNumberOfParameters(); + if (numberOfParameters > NumericTraits::max()) + { + itkExceptionMacro("Moving transform has " << numberOfParameters << " parameters, exceeding the metric limit of " + << NumericTraits::max()); + } + return static_cast(numberOfParameters); } template + +namespace +{ +class CountTransform : public itk::IdentityTransform +{ +public: + ITK_DISALLOW_COPY_AND_MOVE(CountTransform); + using Self = CountTransform; + using Superclass = itk::IdentityTransform; + using Pointer = itk::SmartPointer; + using NumberOfParametersType = Superclass::NumberOfParametersType; + itkNewMacro(Self); + itkOverrideGetNameOfClassMacro(CountTransform); + + void + SetCount(NumberOfParametersType count) + { + m_Count = count; + } + + NumberOfParametersType + GetNumberOfParameters() const override + { + return m_Count; + } + +protected: + CountTransform() = default; + ~CountTransform() override = default; + +private: + NumberOfParametersType m_Count{}; +}; + +class CountMetric : public itk::ObjectToObjectMetric<3, 3> +{ +public: + ITK_DISALLOW_COPY_AND_MOVE(CountMetric); + using Self = CountMetric; + using Superclass = itk::ObjectToObjectMetric<3, 3>; + using Pointer = itk::SmartPointer; + itkNewMacro(Self); + itkOverrideGetNameOfClassMacro(CountMetric); + + MeasureType + GetValue() const override + { + return 0.0; + } + + void + GetDerivative(DerivativeType & derivative) const override + { + derivative.SetSize(0); + } + + void + GetValueAndDerivative(MeasureType & value, DerivativeType & derivative) const override + { + value = this->GetValue(); + this->GetDerivative(derivative); + } + + bool + SupportsArbitraryVirtualDomainSamples() const override + { + return false; + } + +protected: + CountMetric() = default; + ~CountMetric() override = default; +}; +} // namespace + +TEST(ObjectToObjectMetric, PreservesRepresentableParameterCounts) +{ + const auto transform = CountTransform::New(); + const auto metric = CountMetric::New(); + metric->SetMovingTransform(transform); + using CountType = CountMetric::NumberOfParametersType; + for (const CountType count : { CountType{ 0 }, CountType{ 17 }, std::numeric_limits::max() }) + { + transform->SetCount(count); + EXPECT_EQ(metric->GetNumberOfParameters(), count); + } +} + +TEST(ObjectToObjectMetric, RejectsParameterCountOverflow) +{ + using TransformCountType = CountTransform::NumberOfParametersType; + using MetricCountType = CountMetric::NumberOfParametersType; + if constexpr (std::numeric_limits::digits <= std::numeric_limits::digits) + { + GTEST_SKIP() << "The transform count type is not wider than the metric count type"; + } + else + { + const auto transform = CountTransform::New(); + const auto metric = CountMetric::New(); + metric->SetMovingTransform(transform); + const auto maximum = static_cast(std::numeric_limits::max()); + transform->SetCount(maximum + TransformCountType{ 1 }); + EXPECT_THROW(metric->GetNumberOfParameters(), itk::ExceptionObject); + } +}