Itk: UBSan finds invalid cast in itkPeriodicBoundaryCondition.hxx

Created on 2 Mar 2021  路  5Comments  路  Source: InsightSoftwareConsortium/ITK

Building current itk master using clang UBSan (undefined behaviour sanitizer) I get this error from the itkPeriodicBoundaryConditionTest test:

/ITK/Modules/Core/Common/include/itkPeriodicBoundaryCondition.hxx:60:17: runtime error: member call on address 0x00016d7da420 which does not point to an object of type 'itk::ConstNeighborhoodIterator<itk::Image<int, 2>, itk::PeriodicBoundaryCondition<itk::Image<int, 2>, itk::Image<int, 2>>>'

It's from this code:

namespace itk
{
template <typename TInputImage, typename TOutputImage>
typename PeriodicBoundaryCondition<TInputImage, TOutputImage>::OutputPixelType
PeriodicBoundaryCondition<TInputImage, TOutputImage>::operator()(const OffsetType &       point_index,
                                                                 const OffsetType &       boundary_offset,
                                                                 const NeighborhoodType * data) const
{
  // This is guaranteed to be called with an object that is using
  // PeriodicBoundaryCondition
  const auto * iterator = reinterpret_cast<const ConstNeighborhoodIterator<TInputImage, Self> *>(data);

If you change that to dynamic_cast and assert the result, you'll see it's null. (That's a way to repro/debug without UBSan).

Alas, I don't know this code enough to debug further...

Bug

Most helpful comment

Of course!

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = Dynamic type mismatch
  * frame #0: 0x00000001093f2718 libclang_rt.asan_osx_dynamic.dylib`__ubsan_on_report
    frame #1: 0x00000001093f2684 libclang_rt.asan_osx_dynamic.dylib`__ubsan::UndefinedBehaviorReport::UndefinedBehaviorReport(char const*, __ubsan::Location&, __sanitizer::InternalScopedString&) + 124
    frame #2: 0x00000001093edf68 libclang_rt.asan_osx_dynamic.dylib`__ubsan::Diag::~Diag() + 224
    frame #3: 0x00000001093f2f10 libclang_rt.asan_osx_dynamic.dylib`HandleDynamicTypeCacheMiss(__ubsan::DynamicTypeCacheMissData*, unsigned long, unsigned long, __ubsan::ReportOptions) + 408
    frame #4: 0x00000001093f2d6c libclang_rt.asan_osx_dynamic.dylib`__ubsan_handle_dynamic_type_cache_miss + 56
    frame #5: 0x0000000100243ab8 ITKCommon2TestDriver`itk::PeriodicBoundaryCondition<itk::Image<int, 2u>, itk::Image<int, 2u> >::operator(this=<unavailable>, point_index=0x000000016fdfde00, boundary_offset=0x000000016fdfde20, data=0x000000016fdfe380)(itk::Offset<2u> const&, itk::Offset<2u> const&, itk::Neighborhood<int*, 2u, itk::NeighborhoodAllocator<int*> > const*) const at itkPeriodicBoundaryCondition.hxx:60:17 [opt]
    frame #6: 0x000000010024f4d4 ITKCommon2TestDriver`itk::ImageBoundaryCondition<itk::Image<int, 2u>, itk::Image<int, 2u> >::OutputPixelType itk::NeighborhoodAccessorFunctor<itk::Image<int, 2u> >::BoundaryCondition<itk::Image<int, 2u> >(this=<unavailable>, point_index=<unavailable>, boundary_offset=<unavailable>, data=<unavailable>, boundaryCondition=<unavailable>) const at itkNeighborhoodAccessorFunctor.h:88:31 [opt]
    frame #7: 0x000000010024db60 ITKCommon2TestDriver`itk::ConstNeighborhoodIterator<itk::Image<int, 2u>, itk::ZeroFluxNeumannBoundaryCondition<itk::Image<int, 2u>, itk::Image<int, 2u> > >::GetPixel(this=0x000000016fdfe380, i=<unavailable>) const at itkConstNeighborhoodIterator.h:206:46 [opt]
    frame #8: 0x000000010023e4e8 ITKCommon2TestDriver`TestPrintNeighborhood(p=<unavailable>, v=<unavailable>) at itkPeriodicBoundaryConditionTest.cxx:46:22 [opt]
    frame #9: 0x00000001002396dc ITKCommon2TestDriver`itkPeriodicBoundaryConditionTest((null)=<unavailable>, (null)=<unavailable>) at itkPeriodicBoundaryConditionTest.cxx:171:20 [opt]
    frame #10: 0x0000000100004000 ITKCommon2TestDriver`main(ac=<unavailable>, av=<unavailable>) at ITKCommon2TestDriver.cxx:515:14 [opt]
    frame #11: 0x000000018a2cdf34 libdyld.dylib`start + 4

All 5 comments

@thewtex @dzenanz @jhlegarreta maybe one of you could figure this out?

@seanm is it possible to get a stack trace ?

Of course!

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = Dynamic type mismatch
  * frame #0: 0x00000001093f2718 libclang_rt.asan_osx_dynamic.dylib`__ubsan_on_report
    frame #1: 0x00000001093f2684 libclang_rt.asan_osx_dynamic.dylib`__ubsan::UndefinedBehaviorReport::UndefinedBehaviorReport(char const*, __ubsan::Location&, __sanitizer::InternalScopedString&) + 124
    frame #2: 0x00000001093edf68 libclang_rt.asan_osx_dynamic.dylib`__ubsan::Diag::~Diag() + 224
    frame #3: 0x00000001093f2f10 libclang_rt.asan_osx_dynamic.dylib`HandleDynamicTypeCacheMiss(__ubsan::DynamicTypeCacheMissData*, unsigned long, unsigned long, __ubsan::ReportOptions) + 408
    frame #4: 0x00000001093f2d6c libclang_rt.asan_osx_dynamic.dylib`__ubsan_handle_dynamic_type_cache_miss + 56
    frame #5: 0x0000000100243ab8 ITKCommon2TestDriver`itk::PeriodicBoundaryCondition<itk::Image<int, 2u>, itk::Image<int, 2u> >::operator(this=<unavailable>, point_index=0x000000016fdfde00, boundary_offset=0x000000016fdfde20, data=0x000000016fdfe380)(itk::Offset<2u> const&, itk::Offset<2u> const&, itk::Neighborhood<int*, 2u, itk::NeighborhoodAllocator<int*> > const*) const at itkPeriodicBoundaryCondition.hxx:60:17 [opt]
    frame #6: 0x000000010024f4d4 ITKCommon2TestDriver`itk::ImageBoundaryCondition<itk::Image<int, 2u>, itk::Image<int, 2u> >::OutputPixelType itk::NeighborhoodAccessorFunctor<itk::Image<int, 2u> >::BoundaryCondition<itk::Image<int, 2u> >(this=<unavailable>, point_index=<unavailable>, boundary_offset=<unavailable>, data=<unavailable>, boundaryCondition=<unavailable>) const at itkNeighborhoodAccessorFunctor.h:88:31 [opt]
    frame #7: 0x000000010024db60 ITKCommon2TestDriver`itk::ConstNeighborhoodIterator<itk::Image<int, 2u>, itk::ZeroFluxNeumannBoundaryCondition<itk::Image<int, 2u>, itk::Image<int, 2u> > >::GetPixel(this=0x000000016fdfe380, i=<unavailable>) const at itkConstNeighborhoodIterator.h:206:46 [opt]
    frame #8: 0x000000010023e4e8 ITKCommon2TestDriver`TestPrintNeighborhood(p=<unavailable>, v=<unavailable>) at itkPeriodicBoundaryConditionTest.cxx:46:22 [opt]
    frame #9: 0x00000001002396dc ITKCommon2TestDriver`itkPeriodicBoundaryConditionTest((null)=<unavailable>, (null)=<unavailable>) at itkPeriodicBoundaryConditionTest.cxx:171:20 [opt]
    frame #10: 0x0000000100004000 ITKCommon2TestDriver`main(ac=<unavailable>, av=<unavailable>) at ITKCommon2TestDriver.cxx:515:14 [opt]
    frame #11: 0x000000018a2cdf34 libdyld.dylib`start + 4

If you add an assert as I described, you should probably be able to repro 100% without bothering with UBSan.

Upon reconsideration, I don't know what to do here. The spaghetti is:

  1. The TBoundaryCondition template parameter of ConstNeighborhoodIterator<TImage, TBoundaryCondition> is a subclass of ImageBoundaryCondition<TImage, TOutputImage>, such as ZeroFluxNeumannBoundaryCondition<TImage> or PeriodicBoundaryCondition<TInputImage, TOutputImage>.
  2. ConstNeighborhoodIterator<TImage, TBoundaryCondition> has a OverrideBoundaryCondition method that changes the treatment of boundary pixels to be any subclass of ImageBoundaryCondition. That means a ConstNeighborhoodIterator<TImage, ZeroFluxNeumannBoundaryCondition<TImage>> could now implement boundary conditions via an object of type PeriodicBoundaryCondition<TInputImage, TOutputImage>.
  3. When PeriodicBoundaryCondition<TInputImage, TOutputImage>::operator() is called it is given a pointer to a NeighborhoodType and wants to downcast it to ConstNeighborhoodIterator<TImage, PeriodicBoundaryCondition<TInputImage, TOutputImage>> which seems quite reasonable. However, with an ordinary dynamic_cast that can fail when the pointer is actually to a ConstNeighborhoodIterator<TImage, ZeroFluxNeumannBoundaryCondition<TImage>> that has been changed with the OverrideBoundaryCondition method.
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Bleach665 picture Bleach665  路  6Comments

issakomi picture issakomi  路  3Comments

gdevenyi picture gdevenyi  路  3Comments

Leengit picture Leengit  路  6Comments

fbudin69500 picture fbudin69500  路  5Comments