https://bugs.kde.org/show_bug.cgi?id=466524

            Bug ID: 466524
           Summary: Use-after-free crashing, when refreshing diff view or
                    entering merge view
    Classification: Applications
           Product: kdiff3
           Version: 1.10.0
          Platform: Other
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: application
          Assignee: reeves...@gmail.com
          Reporter: nyanpas...@tuta.io
  Target Milestone: ---

Created attachment 156787
  --> https://bugs.kde.org/attachment.cgi?id=156787&action=edit
Terminal log including app logs and stack traces, from triggering
use-after-free errors in Valgrind

SUMMARY
KDiff3 sometimes crashes when opening the diff view between two files,
reloading and cancelling, entering merge view of two files, etc.

STEPS TO REPRODUCE
1. Build KDiff3 with debug symbols. (The Arch packages lack debug symbols *and*
lack a kdiff3-debug package for some reason.)
2. Run `valgrind path/to/kdiff3-dbg file1 file2`. (I can't share the files I
was diffing locally, but they were rather long. I initially encountered the
crash outside of Valgrind, but it generates better stack traces. Alternatively
you could build with asan too.)
3. Enable word wrap (affects DiffTextWindowData::draw() near the crash site,
you *may* be able to reproduce the crash without it but I haven't tested).
4. Try playing with the diff view, refreshing with F5, clicking the merge
toolbar button, etc. The bug doesn't trigger reliably.

OBSERVED RESULT

==437501== Invalid read of size 1
==437501==    at 0x1BB767: isEqualAB (diff.h:287)
==437501==    by 0x1BB767: Diff3Line::getLineInfo(e_SrcSelector, bool,
LineRef&, std::shared_ptr<DiffList>&, std::shared_ptr<DiffList>&,
QFlags<ChangeFlag>&, QFlags<ChangeFlag>&) const (diff.cpp:1486)
==437501==    by 0x1B246D: DiffTextWindowData::draw(RLPainter&, QRect const&,
int, LineRef const&) (difftextwindow.cpp:1259)
==437501==    by 0x1B2A39: DiffTextWindow::paintEvent(QPaintEvent*)
(difftextwindow.cpp:1182)
==437501==    by 0x5090513: QWidget::event(QEvent*) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x5059B5B: QApplicationPrivate::notify_helper(QObject*,
QEvent*) (in /usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x5E93F47: QCoreApplication::notifyInternal2(QObject*,
QEvent*) (in /usr/lib/libQt5Core.so.5.15.8)
==437501==    by 0x50843EA: QWidgetPrivate::sendPaintEvent(QRegion const&) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x50857B5: QWidgetPrivate::drawWidget(QPaintDevice*, QRegion
const&, QPoint const&, QFlags<QWidgetPrivate::DrawWidgetFlag>, QPainter*,
QWidgetRepaintManager*) (in /usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x506588F: ??? (in /usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x50901E3: QWidget::event(QEvent*) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x4B0E41D: KXmlGuiWindow::event(QEvent*) (in
/usr/lib/libKF5XmlGui.so.5.103.0)
==437501==    by 0x5059B5B: QApplicationPrivate::notify_helper(QObject*,
QEvent*) (in /usr/lib/libQt5Widgets.so.5.15.8)

==437501==  Address 0x29b0a89e is 30 bytes inside a block of size 96 free'd
==437501==    at 0x4844B5F: operator delete(void*, unsigned long) (in
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==437501==    by 0x199969: deallocate (new_allocator.h:158)
==437501==    by 0x199969: deallocate (alloc_traits.h:496)
==437501==    by 0x199969: _M_put_node (stl_list.h:522)
==437501==    by 0x199969: _M_clear (list.tcc:81)
==437501==    by 0x199969: clear (stl_list.h:1594)
==437501==    by 0x199969: KDiff3App::mainInit(TotalDiffStatus*,
QFlags<InitFlag>) (pdiff.cpp:118)
==437501==    by 0x5EC4A70: ??? (in /usr/lib/libQt5Core.so.5.15.8)
==437501==    by 0x504CEC6: QAction::triggered(bool) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x50528C6: QAction::activate(QAction::ActionEvent) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x505298D: QAction::event(QEvent*) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x5059B5B: QApplicationPrivate::notify_helper(QObject*,
QEvent*) (in /usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x5E93F47: QCoreApplication::notifyInternal2(QObject*,
QEvent*) (in /usr/lib/libQt5Core.so.5.15.8)
==437501==    by 0x5710FAC: QShortcutMap::dispatchEvent(QKeyEvent*) (in
/usr/lib/libQt5Gui.so.5.15.8)
==437501==    by 0x5707816: QShortcutMap::tryShortcut(QKeyEvent*) (in
/usr/lib/libQt5Gui.so.5.15.8)
==437501==    by 0x56C7037:
QWindowSystemInterface::handleShortcutEvent(QWindow*, unsigned long, int,
QFlags<Qt::KeyboardModifier>, unsigned int, unsigned int, unsigned int, QString
const&, bool, unsigned short) (in /usr/lib/libQt5Gui.so.5.15.8)
==437501==    by 0x56DC971:
QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyEvent*)
(in /usr/lib/libQt5Gui.so.5.15.8)

==437501==  Block was alloc'd at
==437501==    at 0x4842003: operator new(unsigned long) (in
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==437501==    by 0x1BDC92: allocate (new_allocator.h:137)
==437501==    by 0x1BDC92: allocate (alloc_traits.h:464)
==437501==    by 0x1BDC92: _M_get_node (stl_list.h:518)
==437501==    by 0x1BDC92: _M_create_node<const Diff3Line&> (stl_list.h:710)
==437501==    by 0x1BDC92: _M_insert<const Diff3Line&> (stl_list.h:2005)
==437501==    by 0x1BDC92: push_back (stl_list.h:1306)
==437501==    by 0x1BDC92: Diff3LineList::calcDiff3LineListUsingAB(DiffList
const*) (diff.cpp:123)
==437501==    by 0x19B25D: KDiff3App::mainInit(TotalDiffStatus*,
QFlags<InitFlag>) (pdiff.cpp:181)
==437501==    by 0x5EC4A70: ??? (in /usr/lib/libQt5Core.so.5.15.8)
==437501==    by 0x504CEC6: QAction::triggered(bool) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x50528C6: QAction::activate(QAction::ActionEvent) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x505298D: QAction::event(QEvent*) (in
/usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x5059B5B: QApplicationPrivate::notify_helper(QObject*,
QEvent*) (in /usr/lib/libQt5Widgets.so.5.15.8)
==437501==    by 0x5E93F47: QCoreApplication::notifyInternal2(QObject*,
QEvent*) (in /usr/lib/libQt5Core.so.5.15.8)
==437501==    by 0x5710FAC: QShortcutMap::dispatchEvent(QKeyEvent*) (in
/usr/lib/libQt5Gui.so.5.15.8)
==437501==    by 0x5707816: QShortcutMap::tryShortcut(QKeyEvent*) (in
/usr/lib/libQt5Gui.so.5.15.8)
==437501==    by 0x56C7037:
QWindowSystemInterface::handleShortcutEvent(QWindow*, unsigned long, int,
QFlags<Qt::KeyboardModifier>, unsigned int, unsigned int, unsigned int, QString
const&, bool, unsigned short) (in /usr/lib/libQt5Gui.so.5.15.8)

And other messages (see attachment). Unfortunately these stack traces are
incomplete and don't extend down to main(), so I don't know if any of these
call stacks originate from reentrant event loops (eg. in KDiff3App::mainInit()
after freeing).

For line numbers, reference https://invent.kde.org/sdk/kdiff3/-/tree/1.10.0.

Looks like DiffTextWindowData::m_diff3WrapLineVector is holding onto stale
Diff3WrapLine::pD3L (Diff3Line*), pointing into KDiff3App::m_diff3LineList even
after it gets cleared.

DiffTextWindow::recalcWordWrapHelper() assigns Diff3WrapLine& d3wl =
d->m_diff3WrapLineVector[wrapLineIdx], and d3wl.pD3L =
(*d->getDiff3LineVector())[i] (DiffTextWindowData::mDiff3LineVector).

`const Diff3LineVector* mDiff3LineVector` is initialized in
`DiffTextWindow::init` (called in KDiff3App::mainInit and passing in
&mDiff3LineVector), and IDK if mutated later on.

I *think* mDiff3LineVector is initialized by
m_diff3LineList.calcDiff3LineVector(mDiff3LineVector), finally completing the
chain from free (KDiff3App::m_diff3LineList) to use (m_diff3WrapLineVector).

I think `KDiff3App::mainInit` runs and returns while the progress dialog
remains visible.

----

Running under gdb, I found that when a use-after-free error appears, it's from
a reentrant event loop called from within KDiff3App::mainInit
(pp.setInformation(i18n("Loading A")); -> setInformationSig ->
signals2::signal), after clearing the vectors but before calling
DiffTextWindow::init() with the new contents. The call stack is long, should I
post it as an attachment?

EXPECTED RESULT
No crash.

SOFTWARE/OS VERSIONS
Operating System: Arch Linux 
KDE Plasma Version: 5.27.1
KDE Frameworks Version: 5.103.0
Qt Version: 5.15.8
Kernel Version: 6.1.12-zen1-1-zen (64-bit)
Graphics Platform: X11
Processors: 12 × AMD Ryzen 5 5600X 6-Core Processor
Memory: 15.5 GiB of RAM
Graphics Processor: AMD Radeon RX 570 Series
Manufacturer: Gigabyte Technology Co., Ltd.
Product Name: B550M DS3H

ADDITIONAL INFORMATION
Unsure if related to Bug 466374.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to