Bug 481045 - Crash (sometimes) in QQmlMetaType::propertyCache when going to a different KCM in Energy Saving > Advanced Power Settings
Summary: Crash (sometimes) in QQmlMetaType::propertyCache when going to a different KC...
Status: RESOLVED FIXED
Alias: None
Product: systemsettings
Classification: Applications
Component: kcm_powerdevil (other bugs)
Version First Reported In: master
Platform: Other Linux
: NOR crash
Target Milestone: ---
Assignee: Plasma Bugs List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-02-08 10:10 UTC by Bharadwaj Raju
Modified: 2024-02-26 16:01 UTC (History)
5 users (show)

See Also:
Latest Commit:
Version Fixed/Implemented In:
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Bharadwaj Raju 2024-02-08 10:10:07 UTC
SUMMARY
System Settings sometimes crashes when going to a different KCM from Energy Saving > Advanced Power Settings > Other Settings > Related Pages.


STEPS TO REPRODUCE
1. Open the Energy Saving KCM
2. Go to Advanced Power Settings
3. Click, for example, Notifications: Power Management
4. Repeat if necessary

BACKTRACE

#0  0x00007ffff51a41bf in QQmlMetaType::propertyCache (obj=obj@entry=0x26867a0, version=version@entry=...)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/qml/qqmlmetatype.cpp:1328
#1  0x00007ffff5145582 in QQmlData::createPropertyCache (object=object@entry=0x26867a0) at /usr/include/qt6/QtCore/qversionnumber.h:374
#2  0x00007ffff5059afc in QQmlData::ensurePropertyCache (object=0x26867a0)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/redhat-linux-build/include/QtQml/6.6.0/QtQml/private/../../../../../../src/qml/qml/qqmldata_p.h:257
#3  QV4::QObjectWrapper::create (engine=<optimized out>, engine@entry=0x87e180, object=object@entry=0x26867a0)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4qobjectwrapper.cpp:796
#4  0x00007ffff5061dd3 in QV4::QObjectWrapper::wrap_slowPath (engine=0x87e180, object=0x26867a0)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4qobjectwrapper.cpp:699
#5  0x00007ffff4fe48fc in QV4::ExecutionEngine::fromData (this=0x87e180, metaType=..., ptr=0x7fffffffa5d0, container=0x7fffbcf2a6e0, property=74, flags=3)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4engine.cpp:1827
#6  0x00007ffff4fe5711 in QV4::ExecutionEngine::fromVariant
    (this=<optimized out>, variant=<optimized out>, parent=<optimized out>, property=<optimized out>, flags=<optimized out>)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4engine.cpp:1968
#7  0x00007ffff5063035 in QV4::loadProperty (v4=0x87e180, wrapper=0x7fffbcf2a6e0, object=<optimized out>, property=<optimized out>)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4qobjectwrapper.cpp:217
#8  0x00007ffff5056942 in QV4::QObjectWrapper::lookupPropertyGetterImpl<QV4::QQmlContextWrapper::lookupScopeObjectProperty(QV4::Lookup*, QV4::ExecutionEngine*, QV4::Value*)::<lambda(const QV4::Value&)>::<lambda()> > (revertLookup=..., flags=..., object=<optimized out>, engine=0x87e180, lookup=0x1044610)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/redhat-linux-build/include/QtQml/6.6.0/QtQml/private/../../../../../../src/qml/jsruntime/qv4qobjectwrapper_p.h:299
#9  operator() (obj=<optimized out>, __closure=<synthetic pointer>) at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4qmlcontext.cpp:641
#10 callWithScopeObject<QV4::QQmlContextWrapper::lookupScopeObjectProperty(QV4::Lookup*, QV4::ExecutionEngine*, QV4::Value*)::<lambda(const QV4::Value&)> >
    (c=..., base=0x0, engine=0x87e180) at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4qmlcontext.cpp:632
#11 QV4::QQmlContextWrapper::lookupScopeObjectProperty (l=0x1044610, engine=0x87e180, base=0x0)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4qmlcontext.cpp:637
#12 0x00007fff895d8701 in ??? ()
#13 0x0000000000000000 in ??? ()
Comment 1 Bharadwaj Raju 2024-02-08 10:27:34 UTC
Interestingly the base is 0x0 in lookupScopeObjectProperty, is that normal?

Anyway, from some poking in GDB it appears that the file it happens in is "kcm/kcm_powerdevilprofilesconfig/ComboBoxWithIcon.qml", line 28, which is:

const modelIndex = model.index(currentIndex, 0);

I guess the model is being freed while the page gets unloaded, and sometimes that results in a call to freed memory?

---

(gdb) frame 11
#11 QV4::QQmlContextWrapper::lookupScopeObjectProperty (l=0x1044610, engine=0x87e180, base=0x0)
    at /usr/src/debug/qt6-qtdeclarative-6.6.0-1.fc39.x86_64/src/qml/jsruntime/qv4qmlcontext.cpp:637
637         return callWithScopeObject(engine, base, [l, engine, base](const Value &obj) {                                                                               
(gdb) print engine
$3 = (QV4::ExecutionEngine *) 0x87e180

(gdb) print engine->stackTrace(-1)
$4 = {<QListSpecialMethods<QV4::StackFrame>> = {<QListSpecialMethodsBase<QV4::StackFrame>> = {<No data fields>}, <No data fields>}, d = {d = 0x1a5c620, 
    ptr = 0x1a5c630, size = 1}}

(gdb) print ((QV4::StackFrame *)(0x1a5c630))->source
$5 = {d = {d = 0x0, ptr = 0x7fffa4032c34 u"qrc:/kcm/kcm_powerdevilprofilesconfig/ComboBoxWithIcon.qml", size = 58}, static _empty = 0 u'\000'}

(gdb) print ((QV4::StackFrame *)(0x1a5c630))->function
$6 = {d = {d = 0x0, ptr = 0x0, size = 0}, static _empty = 0 u'\000'}

(gdb) print ((QV4::StackFrame *)(0x1a5c630))->line
$7 = 28
Comment 2 Doug 2024-02-09 06:13:00 UTC
Cannot reproduce.

Operating System: KDE neon Testing Edition
KDE Plasma Version: 6.0.0
KDE Frameworks Version: 6.0.0
Qt Version: 6.6.1
Kernel Version: 6.5.0-17-generic (64-bit)
Graphics Platform: Wayland
Graphics Processor: AMD Radeon Pro WX 3200 Series
Comment 3 Jakob Petsovits 2024-02-19 16:29:13 UTC
Can reliably reproduce. It didn't use to crash when I first introduced the new QML-based UI, although it always had QML warnings about underlying stuff being null. Let's see if there's perhaps a signal somewhere that lets me unload the UI before it tries to access unloaded objects.
Comment 4 Jakob Petsovits 2024-02-19 17:49:32 UTC
It looks like this kind of stuff should be prevented by the KQuickConfigModule destructor, which seems to unload the QML rootObject: https://invent.kde.org/frameworks/kcmutils/-/blob/master/src/qml/kquickconfigmodule.cpp?ref_type=heads#L71

If my C++ isn't totally off, the destructor order should go like:
1. ~PowerKCM() - no-op
2. ~KQuickManagedConfigModule() - deletes KQuickManagedConfigModulePrivate, which only holds non-owning pointers to KCoreConfigSkeleton objects
3. ~KQuickConfigModule() - unloading QML, see above
4. ~KAbstractConfigModule() - deletes KAbstractConfigModulePrivate, which holds plugin metadata and other only tangentially related stuff
5. ~QObject() - deletes children of PowerKCM, i.e. the various settings and model objects

I don't yet see what exactly is going wrong there. What I'm wondering is where the QV4::QQmlContextWrapper::lookupScopeObjectProperty call is coming from in that backtrace, we see a few "???" but I'd be super curious if this happens inside this whole destructor inheritance chain or is called externally somewhere.
Comment 5 Jakob Petsovits 2024-02-19 20:34:14 UTC
"Partial" success: If we forcibly delete each subPage of KQuickConfigModule (e.g. by deleting any object returned by takeLast() in ~KQuickConfigModule(), or by parenting a subPage to the already slated-for-deletion d->engine->rootObject()), then page transitions stop crashing while System Settings operates. However, it will crash when trying to close it because apparently QQmlEngine keeps other references to those objects around and they will derefence a freed object on QQmlEngine destruction.

Hopefully there's a way to remove these subPage items from the QQmlEngine in some other way that leaves no dangling pointers.
Comment 6 Jakob Petsovits 2024-02-19 22:07:07 UTC
(In reply to Jakob Petsovits from comment #5)
> "Partial" success: (...). However, it will crash when trying
> to close it because apparently QQmlEngine keeps other references to those
> objects around and they will derefence a freed object on QQmlEngine
> destruction.

Looks like that crash (featuring QQuickPopup) is independent from the sub-page crash of this issue, it also occurs when no sub-pages were opened in the first place, or when opening a different KCM than PowerDevil, before and after my reparenting/deletion changes. I'll submit an MR with the "partial" fix and maybe we'll figure out the other crash in a different bug report.
Comment 7 Bug Janitor Service 2024-02-19 22:48:06 UTC
A possibly relevant merge request was started @ https://invent.kde.org/frameworks/kcmutils/-/merge_requests/201
Comment 8 Jakob Petsovits 2024-02-26 16:01:04 UTC
Git commit 80db4e37396298c5712aa48a943f0a89637f83b7 by Jakob Petsovits.
Committed on 26/02/2024 at 15:57.
Pushed by jpetso into branch 'master'.

SharedQmlEngine: Don't crash from dangling child item objects

In System Settings, navigating away from pushed sub-pages would
often crash. The KQuickConfigModule destructor explicitly deletes
SharedQmlEngine's rootObject() and implicitly its QQmlContext,
but the subPages would stick around.

Before this commit, createObjectFromComponent() assigned rootObject()
as QObject parent for newly instantiated objects, but not for
Qt Quick items. Those would instead have the "parent" property set,
i.e. setParentItem(), which does not change the QObject hierarchy.

This commit changes the logic to always call QObject::setParent(),
and call the optional "parent" property setter in addition to it.
This ensures that deleting rootObject() will also get rid of any
otherwise dangling sub-pages.

M  +6    -10   src/qml/sharedqmlengine.cpp

https://invent.kde.org/frameworks/kcmutils/-/commit/80db4e37396298c5712aa48a943f0a89637f83b7