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

            Bug ID: 528090
           Summary: Crash (SIGSEGV / use-after-free in
                    WhisperDownload::processHashCheck) when closing
                    Whisper model download dialog during model integrity
                    check
    Classification: Applications
           Product: kdenlive
      Version First 26.08.2
       Reported In:
          Platform: RedHat Enterprise Linux
                OS: Linux
            Status: REPORTED
          Severity: crash
          Priority: NOR
         Component: Title Clips & Subtitles
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ---

SUMMARY
    Kdenlive crashes with SIGSEGV (use-after-free on dangling `this` pointer)
in a background QThreadPool worker running
`WhisperDownload::processHashCheck()` if the `WhisperDownload`
  modal dialog is closed while the SHA-256 hash verification of installed
Whisper `.pt` models is still in progress.

    WHAT I WAS DOING WHEN THE CRASH HAPPENED / STEPS TO REPRODUCE
    1. Have one or more large OpenAI Whisper models already downloaded in
`~/.var/app/org.kde.kdenlive/data/kdenlive/whispermodels/` (in this case:
`large-v3-turbo.pt` [1.51 GB], `medium.
  pt` [1.42 GB], `small.pt` [461 MB], `base.pt` [139 MB], `tiny.pt` [72 MB]).
    2. Open Settings -> Configure Kdenlive -> Plugins -> Speech To Text
(Whisper) (`PluginsSettings`), and open the Whisper model management / download
dialog (`WhisperDownload`).
       - Opening `WhisperDownload` immediately calls
`WhisperDownload::checkHashes()`, which launches
`QtConcurrent::run(&WhisperDownload::processHashCheck, this, hashesToCheck)` on
a
  background thread and locks `m_closeMutex`.
    3. Close the `WhisperDownload` dialog (`10:59:04`) before
`QCryptographicHash::addData(&info)` finishes hashing the large `.pt` file(s),
for example via the window close button (X) or
  Escape key (`QDialog::reject()`).
       - At this point, systemd journal logs: `QMutex: destroying locked mutex`
because stack-allocated `WhisperDownload` (`0x7fff2efa1df0`) is destroyed while
`processHashCheck` still
  holds `m_closeMutex` (`0x7fff2efa1e70`).
    4. In the still-open `PluginsSettings` dialog
(`src/dialogs/settings/pluginssettings.cpp:92`), click to check/install missing
dependencies
  (`AbstractPythonInterface::installMissingDependencies` at
`src/pythoninterfaces/abstractpythoninterface.cpp:621`), which opens a modal
`KMessageBox::warningContinueCancel`
  (`QDialog::exec()`), reusing the main thread stack memory around
`0x7fff2efa1df0`.
    5. Approximately 5 seconds after `WhisperDownload` was closed (`10:59:09`),
`QCryptographicHash::addData(&info)` finishes hashing on the worker thread
(Thread 1 / LWP 296) and
  executes `Q_EMIT itemMatch(i.value(), hashMatch)` on the destroyed
`WhisperDownload` instance (`this = 0x7fff2efa1df0`), causing an immediate
segmentation fault.

    OBSERVED RESULT
    1. When `WhisperDownload` closes at `10:59:04`:
       `kdenlive[31070]: QMutex: destroying locked mutex`
    2. When `QCryptographicHash::addData()` completes on the worker thread at
`10:59:09`:
       `SIGSEGV` in `QQmlData::isSignalConnected` / `QMetaObject::activate` /
`WhisperDownload::itemMatch` because `sender = 0x7fff2efa1df0` is a dangling
stack pointer whose `d_ptr` was
  overwritten by subsequent stack frames on the main thread.

    EXPECTED RESULT
    Closing `WhisperDownload` by any means (Close button, window close 'X',
Escape key / `reject()`, or destruction) must set `m_requestClose = true` and
wait for `m_closeMutex` before
  the `WhisperDownload` object is destroyed, or avoid passing raw `this` to
`QtConcurrent::run`.

    ROOT CAUSE ANALYSIS
    In `src/pythoninterfaces/dialogs/whisperdownload.h` and
`src/pythoninterfaces/dialogs/whisperdownload.cpp`:
    - `WhisperDownload` is stack-allocated and executed via `QDialog::exec()`.
    - `WhisperDownload::checkHashes()` spawns
`QtConcurrent::run(&WhisperDownload::processHashCheck, this, hashesToCheck)`.
    - `WhisperDownload::processHashCheck()` acquires `QMutexLocker
lock(&m_closeMutex)` and iterates over `hashesToCheck`, calling
`QCryptographicHash::addData(&info)` on each `.pt` model
  file and emitting `Q_EMIT itemMatch(i.value(), hashMatch)`.
    - Only `WhisperDownload::queryClose()` (connected to
`QDialogButtonBox::Close`) sets `m_requestClose = true` and locks
`m_closeMutex` before calling `accept()`.
    - `WhisperDownload` does not define a destructor (`~WhisperDownload()`),
nor does it override `reject()` or `closeEvent(QCloseEvent*)` to set
`m_requestClose = true` and wait on
  `m_closeMutex`. Furthermore, `QCryptographicHash::addData(&info)` blocks
synchronously for the entire 1.5 GB file read before checking `m_requestClose`.

    SOFTWARE/OS VERSIONS (from `kinfo` and `flatpak info`)
    Operating System: Fedora Linux 44
    KDE Plasma Version: 6.7.5
    KDE Frameworks Version: 6.31.0
    Qt Version: 6.11.3 (Flatpak runtime) / 6.11.2 (Host)
    Kernel Version: 7.2.13-200.fc44.x86_64 (64-bit)
    Graphics Platform: Wayland
    Processors: 8 × 11th Gen Intel® Core™ i5-1145G7 @ 2.60GHz
    Memory: 32 GiB of RAM (30.7 GiB usable)
    Graphics Processor: Intel® Iris® Xe Graphics

    Flatpak Package Details:
    - Application: org.kde.kdenlive 26.08.2 (flathub stable, arch x86_64)
    - App Commit:
b1fc34accc861c296d08863b01c65d9c32816bdf1d08414ee157de565e56199d
    - App Debug Commit (org.kde.kdenlive.Debug):
efff7f5c35f4e69146ec808e9f25bf51b30ac0cff80426843726036118f6cd8a
    - Runtime: org.kde.Platform/x86_64/6.11
    - Runtime Commit:
fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415
    - SDK Debug Commit (org.kde.Sdk.Debug//6.11):
ca2e2fd709050b8496f75ab4d315b2b9731d6d13e0331c3a7361c65af8

    BACKTRACE (GDB with DWARF debug symbols for Kdenlive and Qt6)

    Core was generated by `/app/bin/kdenlive'.
    Program terminated with signal SIGSEGV, Segmentation fault.
    [Current thread is 1 (LWP 296)]

    Thread 1 (LWP 296 - Crashed QThreadPool worker thread):
    #0  0x00007fcc5fd890a2 in std::__atomic_base<unsigned long long>::load
(this=0x61, __m=std::memory_order_relaxed) at
/usr/include/c++/15.2.0/bits/atomic_base.h:501
            __b = std::memory_order_relaxed
            __PRETTY_FUNCTION__ = <optimized out>
    #1  QAtomicOps<unsigned long long>::loadRelaxed<unsigned long long>
(_q_value=...) at /usr/include/QtCore/qatomic_cxx11.h:203
    No locals.
    #2  QBasicAtomicInteger<unsigned long long>::loadRelaxed (this=0x61) at
/usr/include/QtCore/qbasicatomic.h:48
    No locals.
    #3  QQmlData::signalHasEndpoint (this=0x560e8bb50780, index=10) at
/usr/lib/debug/source/org.kde.Sdk/qtdeclarative.bst/src/qml/qml/qqmldata_p.h:375
            list = 0x61
    #4  QQmlData::isSignalConnected (d=0x560e8bb50780, index=10) at
/usr/lib/debug/source/org.kde.Sdk/qtdeclarative.bst/src/qml/qml/qqmlengine.cpp:359
            ddata = 0x560e8bb50780
    #5  0x00007fcc5f02450e in QObjectPrivate::isDeclarativeSignalConnected
(this=<optimized out>, signal_index=10) at
/usr/lib/debug/source/org.kde.Sdk/qtbase.
  bst/src/corelib/kernel/qobject_p.h:232
    No locals.
    #6  doActivate<false> (sender=0x7fff2efa1df0, signal_index=10,
argv=argv@entry=0x7fcc36dfdef0) at
/usr/lib/debug/source/org.kde.Sdk/qtbase.bst/src/corelib/kernel/qobject.cpp:4278
            sp = <optimized out>
            signal_spy_set = <optimized out>
            empty_argv = {0x7fcbdc000f90}
            senderDeleted = <optimized out>
    #7  0x00007fcc5f01bb69 in QMetaObject::activate
(sender=sender@entry=0x7fff2efa1df0, m=m@entry=0x560e4a6f6000,
local_signal_index=local_signal_index@entry=0,
  argv=argv@entry=0x7fcc36dfdef0) at
/usr/lib/debug/source/org.kde.Sdk/qtbase.bst/src/corelib/kernel/qobject.cpp:4453
            signal_index = <optimized out>
    #8  0x0000560e49803b7a in QMetaObject::activate<void, QString, bool>
(sender=0x7fff2efa1df0, mo=0x560e4a6f6000, local_signal_index=0, ret=0x0) at
/usr/include/QtCore/qobjectdefs.h:320
            _a = {0x0, 0x7fcc36dfdfb0, 0x7fcc36dfdeec}
    #9  WhisperDownload::itemMatch (this=this@entry=0x7fff2efa1df0, _t1=...,
_t2=<optimized out>) at
 
/run/build/kdenlive/_flatpak_build/src/kdenliveLib_autogen/N4EE2GT4VS/moc_whisperdownload.cpp:165
    No locals.
    #10 0x0000560e49e47dcd in WhisperDownload::processHashCheck
(this=0x7fff2efa1df0, hashesToCheck=...) at
/run/build/kdenlive/src/pythoninterfaces/dialogs/whisperdownload.cpp:259
            info = {<QFileDevice> = {<QIODevice> = {<QObject> = {_vptr.QObject
= 0x7fcc5f4e2ec8 <vtable for QFile+16>, d_ptr = {d = 0x7fcbdc003c00}}, <No data
fields>}, <No data fields>},
  <No data fields>}
            hashMatch = <optimized out>
            lock = {m_mutex = 0x7fff2efa1e70, m_isLocked = true}
            i = {c = {d = {d = {ptr = 0x560e8c539d50}}}, i = {i = {_M_node =
0x560e8c539d60}}, n = {i = {_M_node = <optimized out>}}}
    #11 0x0000560e49e4b2ad in std::__invoke_impl<void, void
(WhisperDownload::*&)(QMap<QString, QString>), WhisperDownload*&, QMap<QString,
QString>&> (__f=<synthetic pointer>: <optimized
  out>, __t=<synthetic pointer>: <optimized out>) at
/usr/include/c++/15.2.0/bits/invoke.h:76
    No locals.
    #12 std::__invoke<void (WhisperDownload::*&)(QMap<QString, QString>),
WhisperDownload*&, QMap<QString, QString>&> (__fn=<synthetic pointer>:
<optimized out>) at /usr/include/c++/15.2.
  0/bits/invoke.h:98
    No locals.
    #13 std::invoke<void (WhisperDownload::*&)(QMap<QString, QString>),
WhisperDownload*&, QMap<QString, QString>&> (__fn=<synthetic pointer>:
<optimized out>) at /usr/include/c++/15.2.
  0/functional:122
    No locals.
    #14 QtConcurrent::StoredFunctionCall<void
(WhisperDownload::*)(QMap<QString, QString>), WhisperDownload*, QMap<QString,
QString> >::runFunctor()::{lambda(void
  (WhisperDownload::*)(QMap<QString, QString>), WhisperDownload*, QMap<QString,
QString>)#1}::operator()(void (WhisperDownload::*)(QMap<QString, QString>),
WhisperDownload*, QMap<QString,
  QString>) const (__closure=<optimized out>, function=<optimized out>,
args#0=<optimized out>, args#1=...) at
/usr/include/QtConcurrent/qtconcurrentstoredfunctioncall.h:117
    No locals.
    #19 QtConcurrent::StoredFunctionCall<void
(WhisperDownload::*)(QMap<QString, QString>), WhisperDownload*, QMap<QString,
QString> >::runFunctor (this=<optimized out>) at
  /usr/include/QtConcurrent/qtconcurrentstoredfunctioncall.h:121
            invoke = <optimized out>
    #20 0x0000560e49508ab5 in QtConcurrent::RunFunctionTaskBase<void>::run
(this=0x560e8734f9a0) at /usr/include/QtConcurrent/qtconcurrentrunbase.h:84
    No locals.
    #21 0x00007fcc5f0ea154 in QThreadPoolThread::run (this=0x560e8bb91930) at
/usr/lib/debug/source/org.kde.Sdk/qtbase.bst/src/corelib/thread/qthreadpool.cpp:72
            del = true
            page = <optimized out>
            r = 0x560e8734f9a0
            locker = {m_mutex = 0x560e866d3af8, m_isLocked = false}
    #22 0x00007fcc5f1a4b07 in QThreadPrivate::start (arg=0x560e8bb91930) at
/usr/lib/debug/source/org.kde.Sdk/qtbase.bst/src/corelib/thread/qthread_unix.cpp:466
            thr = 0x560e8bb91930
            data = 0x560e8bb91960
    #23 0x00007fcc5e89b4ca in start_thread () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-
  linux-gnu/libc.so.6
    #24 0x00007fcc5e91f014 in clone () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-
  gnu/libc.so.6

    Thread 24 (LWP 2 - Main GUI thread at time of crash):
    #0  0x00007fcc5e8a3b42 in __syscall_cancel_arch () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libc.so.6
    #1  0x00007fcc5e897c9c in __internal_syscall_cancel () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libc.so.6
    #2  0x00007fcc5e897ce4 in __syscall_cancel () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libc.so.6
    #3  0x00007fcc5e912396 in ppoll () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-
  gnu/libc.so.6
    #4  0x00007fcc5ed0f232 in g_main_context_iterate_unlocked.isra () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.4
    #5  0x00007fcc5ed0f323 in g_main_context_iteration () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.4
    #6  0x00007fcc5f2fb93d in QEventDispatcherGlib::processEvents
(this=0x560e85d99ce0, flags=...) at /usr/lib/debug/source/org.kde.Sdk/qtbase.
  bst/src/corelib/kernel/qeventdispatcher_glib.cpp:402
    #7  0x00007fcc5efc219b in QEventLoop::exec (this=0x7fff2efa18d0, flags=...)
at /usr/lib/debug/source/org.kde.Sdk/qtbase.bst/src/corelib/global/qflags.h:78
    #8  0x00007fcc63c5ff27 in QDialog::exec() () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libQt6Widgets.so.6.11.3
    #9  0x00007fcc659157e9 in KMessageBox::createKMessageBox(QDialog*,
QDialogButtonBox*, QIcon const&, QString const&, QList<QString> const&, QString
const&, bool*,
  QFlags<KMessageBox::Option>, QString const&, QMessageBox::Icon) () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libKF6WidgetsAddons.so.6.31.0
    #10 0x00007fcc65916443 in KMessageBox::createKMessageBox(QDialog*,
QDialogButtonBox*, QMessageBox::Icon, QString const&, QList<QString> const&,
QString const&, bool*,
  QFlags<KMessageBox::Option>, QString const&) () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libKF6WidgetsAddons.so.6.31.0
    #11 0x00007fcc65917781 in
KMessageBox::warningContinueCancelListInternal(QDialog*, QString const&,
QList<QString> const&, QString const&, KGuiItem const&, KGuiItem const&,
QString
  const&, QFlags<KMessageBox::Option>, QString const&) () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libKF6WidgetsAddons.so.6.31.0
    #12 0x00007fcc6591796e in KMessageBox::warningContinueCancelList(QWidget*,
QString const&, QList<QString> const&, QString const&, KGuiItem const&,
KGuiItem const&, QString const&,
  QFlags<KMessageBox::Option>) () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-
  gnu/libKF6WidgetsAddons.so.6.31.0
    #13 0x00007fcc65917a1b in KMessageBox::warningContinueCancel(QWidget*,
QString const&, QString const&, KGuiItem const&, KGuiItem const&, QString
const&, QFlags<KMessageBox::Option>)
  () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libKF6WidgetsAddons.so.6.31.0
    #14 0x0000560e49e5eeab in
AbstractPythonInterface::installMissingDependencies (this=0x560e8bb3f410) at
/run/build/kdenlive/src/pythoninterfaces/abstractpythoninterface.cpp:621
    #15 0x0000560e49c40b15 in operator() (__closure=0x560e8bb50790) at
/run/build/kdenlive/src/dialogs/settings/pluginssettings.cpp:92
    #20
QtPrivate::QCallableObject<PluginsSettings::PluginsSettings(QWidget*)::<lambda()>,
QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *,
void **, bool *)
  (which=<optimized out>, this_=0x560e8bb50780, r=<optimized out>, a=<optimized
out>, ret=<optimized out>) at /usr/include/QtCore/qobjectdefs_impl.h:548
    #21 0x00007fcc5f024896 in QtPrivate::QSlotObjectBase::call
(this=0x560e8bb50780, r=0x560e86128c50, a=0x7fff2efa2010) at
/usr/lib/debug/source/org.kde.Sdk/qtbase.
  bst/src/corelib/kernel/qobjectdefs_impl.h:462
    #22 doActivate<false> (sender=0x560e8652c7f0, signal_index=<optimized out>,
argv=<optimized out>) at
/usr/lib/debug/source/org.kde.Sdk/qtbase.bst/src/corelib/kernel/qobject.cpp:4393
    #23 0x00007fcc63af541a in QAbstractButton::clicked(bool) () from
/var/lib/flatpak/runtime/org.kde.Platform/x86_64/6.
 
11/fb23e178d082b117df7924f9a84710992ec233ebc007385feb85d349c4c81415/files/lib/x86_64-linux-gnu/libc.so.6
    #45 0x0000560e494c3506 in main (argc=<optimized out>, argv=0x7fff2efa32d8)
at /run/build/kdenlive/src/main.cpp:567

    REGISTERS & DISASSEMBLY AT CRASH (Thread 1)
    rax            0xa                 10
    rbx            0x7fcc36dfdef0      140515070697200
    rcx            0x61                97
    rdx            0xa                 10
    rsi            0x560e8c52db40      94620483771200
    rdi            0x560e8bb50780      94620473427840
    rbp            0x7fcc36dfded0      0x7fcc36dfded0
    rsp            0x7fcc36dfde18      0x7fcc36dfde18
    r12            0x560e8c539d60      94620483820896
    r13            0x7fff2efa1df0      140733981531632  <-- dangling `this`
(stack-allocated WhisperDownload)
    r14            0x7fff2efa1e20      140733981531680
    r15            0xa                 10
    rip            0x7fcc5fd890a2      0x7fcc5fd890a2
<QQmlData::isSignalConnected(QAbstractDeclarativeData*, QObject const*,
int)+18>

    => 0x7fcc5fd890a2
<_ZN8QQmlData17isSignalConnectedEP24QAbstractDeclarativeDataPK7QObjecti+18>:
mov (%rcx),%rcx

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

Reply via email to