OpenOrienteering / mapper

OpenOrienteering Mapper is a software for creating maps for the orienteering sport.
https://www.openorienteering.org/apps/mapper/
GNU General Public License v3.0
403 stars 107 forks source link

Mapper crashes when rotating large template #2143

Open dl3sdo opened 1 year ago

dl3sdo commented 1 year ago

Steps to reproduce

  1. Open the attached map
  2. Select in template window Edit->Positioning
  3. Enter a value in the 'Rotation' field

Actual behaviour

Mapper crashes.

Expected behaviour

Mapper should at least issue a warning that the rotation of the template cannot be performed (even if the size of the template does not represent a realistic usecase).

Configuration

Mapper Version: 0.95/Current Master (Debug) Operating System: Win7/Ubuntu LargeTemplateCrash.zip

lpechacek commented 1 year ago

Confirmed. The process crashes deep in Qt code which suggests inconsistent QImage data rather than a straightforward programmatic error. I'm attaching a partial backtrace for illustration. The bug is easy to replicate.

#0  fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2(unsigned int*, unsigned int*, QTextureData const&, int&, int&, int, int)
    (b=0x7fffffff9cb0, end=0x7fffffffab38, image=..., fx=@0x7fffffff7b30: 526803398, fy=@0x7fffffff7b34: 458696704, fdx=654798, fdy=11429)
    at painting/qdrawhelper_avx2.cpp:941
#1  0x00007ffff7401294 in fetchTransformedBilinearARGB32PM<(TextureBlendType)4>(uint*, Operator const*, QSpanData const*, int, int, int)
    (buffer=0x7fffffff9cb0, data=<optimized out>, y=<optimized out>, x=<optimized out>, length=930) at painting/qdrawhelper.cpp:3107
#2  0x00007ffff741b32f in BlendSrcGeneric::fetch(int, int, int) (len=<optimized out>, y=0, x=0, this=0x7fffffff7c10)
    at painting/qdrawhelper.cpp:4764
#3  handleSpans<BlendSrcGeneric>(int, QT_FT_Span_ const*, QSpanData const*, BlendSrcGeneric&)
    (count=256, spans=0x7fffffffbeb0, data=<optimized out>, handler=...) at painting/qdrawhelper.cpp:4708
#4  0x00007ffff7418dbd in blend_src_generic(int, QSpan const*, void*) (count=<optimized out>, spans=<optimized out>, userData=<optimized out>)
    at painting/qdrawhelper.cpp:4816
#5  0x00007ffff74abcc3 in QSpanBuffer::flushSpans() (this=0x7fffffffbeb0) at painting/qrasterizer.cpp:113
#6  QSpanBuffer::addSpan(int, unsigned int, int, unsigned char)
    (x=<optimized out>, len=<optimized out>, y=255, coverage=255 '\377', this=0x7fffffffbeb0) at painting/qrasterizer.cpp:107
#7  QSpanBuffer::addSpan(int, unsigned int, int, unsigned char)
    (coverage=255 '\377', y=255, len=<optimized out>, x=<optimized out>, this=0x7fffffffbeb0) at painting/qrasterizer.cpp:91
#8  QRasterizer::rasterizeLine(QPointF const&, QPointF const&, double, bool) (this=<optimized out>, a=..., b=..., width=<optimized out>, 
    width@entry=0.52554545454545454, squareCap=squareCap@entry=false) at painting/qrasterizer.cpp:1185
#9  0x00007ffff74549b9 in QRasterPaintEngine::drawImage(QRectF const&, QImage const&, QRectF const&, QFlags<Qt::ImageConversionFlag>)
    (this=<optimized out>, r=<optimized out>, img=<optimized out>, sr=...) at ../../include/QtCore/../../src/corelib/tools/qscopedpointer.h:116
#10 0x00007ffff745619c in QRasterPaintEngine::drawImage(QRectF const&, QImage const&, QRectF const&, QFlags<Qt::ImageConversionFlag>)
    (sr=..., img=..., r=..., this=0x3078ad0) at ../../include/QtCore/../../src/corelib/tools/qrect.h:674
#11 QRasterPaintEngine::drawImage(QPointF const&, QImage const&) (this=0x3078ad0, p=..., img=...) at painting/qpaintengine_raster.cpp:2189
#12 0x000000000100352e in OpenOrienteering::TemplateImage::drawTemplate(QPainter*, QRectF const&, double, bool, double) const
    (this=0x2cf22b0, painter=0x7fffffffcb88, opacity=1) at /data/src/oomapper/src/templates/template_image.cpp:363
#13 0x0000000000587fed in OpenOrienteering::Map::drawTemplates(QPainter*, QRectF const&, int, int, OpenOrienteering::MapView const*, bool) const
    (this=0x2bda510, painter=0x7fffffffcb88, bounding_box=..., first_template=0, last_template=0, view=0x2cdebe0, on_screen=true)
    at /data/src/oomapper/src/core/map.cpp:876
#14 0x0000000000c78a67 in OpenOrienteering::MapWidget::updateTemplateCache(QImage&, QRect&, int, int, bool)
    (this=0x2cf2550, cache=..., dirty_rect=..., first_template=0, last_template=0, use_background=true)
    at /data/src/oomapper/src/gui/map/map_widget.cpp:1343
#15 0x0000000000c7b911 in OpenOrienteering::MapWidget::updateAllDirtyCaches() (this=0x2cf2550)
    at /data/src/oomapper/src/gui/map/map_widget.cpp:1418
#16 0x0000000000c6212e in OpenOrienteering::MapWidget::paintEvent(QPaintEvent*) (this=0x2cf2550, event=0x7fffffffd010)
    at /data/src/oomapper/src/gui/map/map_widget.cpp:870
dl3sdo commented 1 year ago

@lpechacek: thank you for the analysis. My first impression was also that this might be an issue of QT. I found this known (and already solved) issue: QTransform rotate big image will crash

lpechacek commented 1 year ago

I've tested with Qt 6.4.3 (https://github.com/lpechacek/mapper/tree/master-qt6) and the process crashes as well. I'm not going to dig deeper into this issue despite its attractiveness.

My replication procedure with Mapper (slightly different than Matthias'):

  1. open map
  2. open the positioning dialog
  3. enter 1 in the rotation field
  4. move the map using middle mouse button (an extra step that triggers the crash on my system)

Backtrace with Qt 6.4.3:

#0  0x00007ffff7424364 in fetchTransformedBilinearARGB32PM_fast_rotate_helper<(TextureBlendType)4>(uint*, uint*, QTextureData const&, int&, int&, int, int)
    (b=0x7fff6982ae70, end=0x7fff6982bcf8, image=..., fx=@0x7fff69828db4: 593894098, fy=@0x7fff69828db0: 555169819, fdx=<optimized out>, fdy=11429) at /usr/lib64/gcc/x86_64-suse-linux/13/include/emmintrin.h:607
#1  0x00007ffff743054b in fetchTransformedBilinearARGB32PM<(TextureBlendType)4>(uint*, Operator const*, QSpanData const*, int, int, int)
    (buffer=0x7fff6982ae70, data=0x3020cc8, y=<optimized out>, x=<optimized out>, length=<optimized out>)
    at /usr/src/debug/qtbase-everywhere-src-6.4.3/src/gui/painting/qdrawhelper.cpp:1954
#2  0x00007ffff7436ab7 in BlendSrcGeneric::fetch(int, int, int, bool)
    (fetchDest=false, len=<optimized out>, y=<optimized out>, x=0, this=0x7fff69828e50)
    at /usr/src/debug/qtbase-everywhere-src-6.4.3/src/gui/painting/qdrawhelper.cpp:4042
#3  handleSpans<BlendSrcGeneric>(int, QT_FT_Span_ const*, QSpanData const*, Operator const&)::{lambda(int, int)#1}::operator()(int, int) const
    (__closure=0x7fffffffb0e0, cStart=<optimized out>, cEnd=<optimized out>)
    at /usr/src/debug/qtbase-everywhere-src-6.4.3/src/gui/painting/qdrawhelper.cpp:3994
#4  0x00007ffff743dd34 in handleSpans<BlendSrcGeneric>(int, QT_FT_Span_ const*, QSpanData const*, Operator const&)::{lambda()#1}::operator()() const (__closure=0x28d4260) at /usr/src/debug/qtbase-everywhere-src-6.4.3/src/gui/painting/qdrawhelper.cpp:4016
#5  std::__invoke_impl<void, handleSpans<BlendSrcGeneric>(int, QT_FT_Span_ const*, QSpanData const*, Operator const&)::{lambda()#1}&>(std::__invoke_other, handleSpans<BlendSrcGeneric>(int, QT_FT_Span_ const*, QSpanData const*, Operator const&)::{lambda()#1}&) (__f=...)
    at /usr/include/c++/13/bits/invoke.h:61
#6  std::__invoke_r<void, handleSpans<BlendSrcGeneric>(int, QT_FT_Span_ const*, QSpanData const*, Operator const&)::{lambda()#1}&>(handleSpans<BlendSrcGeneric>(int, QT_FT_Span_ const*, QSpanData const*, Operator const&)::{lambda()#1}&) (__fn=...) at /usr/include/c++/13/bits/invoke.h:111
#7  std::_Function_handler<void (), handleSpans<BlendSrcGeneric>(int, QT_FT_Span_ const*, QSpanData const*, Operator const&)::{lambda()#1}>::_M_invoke(std::_Any_data const&) (__functor=<optimized out>) at /usr/include/c++/13/bits/std_function.h:290
#8  0x00007ffff4ebad02 in  () at /lib64/libQt6Core.so.6
#9  0x00007ffff4eba95f in  () at /lib64/libQt6Core.so.6
#10 0x00007ffff3e92f24 in start_thread () at /lib64/libc.so.6
#11 0x00007ffff3f19f50 in clone3 () at /lib64/libc.so.6