Summary: | Discover crashes on start unless .cache/discover/ deleted | ||
---|---|---|---|
Product: | [Applications] Discover | Reporter: | Fabian Vogt <fabian> |
Component: | discover | Assignee: | Plasma Bugs List <plasma-bugs> |
Status: | RESOLVED FIXED | ||
Severity: | crash | CC: | aleixpol, kde.p-foerster, khaleeq416, nate, qydwhotmail |
Priority: | VHI | ||
Version: | 5.26.90 | ||
Target Milestone: | --- | ||
Platform: | openSUSE | ||
OS: | Linux | ||
Latest Commit: | https://invent.kde.org/plasma/plasma-desktop/commit/ec5cc12b4c4119672acd95680e897ac96e573ee8 | Version Fixed In: | 5.27.9 |
Sentry Crash Report: | |||
Attachments: | Terminal output when starting Discover |
Description
Fabian Vogt
2023-01-19 19:03:54 UTC
The cause is that the cache for https://api.kde-look.org/ocs/v1/content/categories contains only metadata, but not any actual data: .cache/discover/attica/data8/2: total 4 -rw------- 1 linux linux 718 Jan 21 07:56 9hw8n262.d This is how it should like with a full GET request: /tmp/qtnamcache/data8/2: insgesamt 28 -rw------- 1 fabian users 26575 21. Jan 14:13 9hw8n262.d The crash is not directly related, appears to be some use-after-free: ==4859== Invalid read of size 8 ==4859== at 0x68E1BF0: QObjectPrivate::maybeSignalConnected(unsigned int) const (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x68F004D: ??? (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x49363F1: ResultsStream::resourcesFound(QVector<AbstractResource*> const&) (moc_AbstractResourcesBackend.cpp:159) ==4859== by 0x28443DE8: UnknownInlinedFun (PackageKitBackend.cpp:532) ==4859== by 0x28443DE8: UnknownInlinedFun (PackageKitBackend.cpp:521) ==4859== by 0x28443DE8: UnknownInlinedFun (PackageKitBackend.cpp:626) ==4859== by 0x28443DE8: UnknownInlinedFun (invoke.h:61) ==4859== by 0x28443DE8: UnknownInlinedFun (invoke.h:111) ==4859== by 0x28443DE8: std::_Function_handler<void (), PackageKitBackend::search(AbstractResourcesBackend::Filters const&)::{lambda()#9}>::_M_invoke(std::_Any_data const&) [clone .lto_priv.0] (std_function.h:290) ==4859== by 0x68E4C4F: QObject::event(QEvent*) (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x4B9344D: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/lib64/libQt5Widgets.so.5.15.8) ==4859== by 0x68B9137: QCoreApplication::notifyInternal2(QObject*, QEvent*) (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x68BC0D0: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x6911362: ??? (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x81A6A8F: g_main_context_dispatch (in /usr/lib64/libglib-2.0.so.0.7400.4) ==4859== by 0x81A6E47: ??? (in /usr/lib64/libglib-2.0.so.0.7400.4) ==4859== by 0x81A6EDB: g_main_context_iteration (in /usr/lib64/libglib-2.0.so.0.7400.4) ==4859== Address 0xd00b420 is 64 bytes inside a block of size 85 free'd ==4859== at 0x484717B: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==4859== by 0x8F93524: xmlFreeNodeList (in /usr/lib64/libxml2.so.2.10.3) ==4859== by 0x8F93634: xmlFreeNode (in /usr/lib64/libxml2.so.2.10.3) ==4859== by 0x82CE5BA: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82CEB07: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82F6EFB: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82CB1C4: as_cache_get_components_by_categories (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82EDBAF: as_pool_get_components_by_categories (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x7199E1B: AppStream::Pool::componentsByCategories(QStringList const&) const (in /usr/lib64/libAppStreamQt.so.0.15.6) ==4859== by 0x4974E13: AppStreamUtils::componentsByCategories(AppStream::Pool*, Category*, AppStream::Bundle::Kind) (AppStreamUtils.cpp:259) ==4859== by 0x28443A7A: UnknownInlinedFun (PackageKitBackend.cpp:618) ==4859== by 0x28443A7A: UnknownInlinedFun (invoke.h:61) ==4859== by 0x28443A7A: UnknownInlinedFun (invoke.h:111) ==4859== by 0x28443A7A: std::_Function_handler<void (), PackageKitBackend::search(AbstractResourcesBackend::Filters const&)::{lambda()#9}>::_M_invoke(std::_Any_data const&) [clone .lto_priv.0] (std_function.h:290) ==4859== by 0x68E4C4F: QObject::event(QEvent*) (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== Block was alloc'd at ==4859== at 0x48447B4: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==4859== by 0x8FE3552: xmlStrndup (in /usr/lib64/libxml2.so.2.10.3) ==4859== by 0x8F8CC5B: xmlNewTextLen (in /usr/lib64/libxml2.so.2.10.3) ==4859== by 0x8F8CCCD: xmlNewDocTextLen (in /usr/lib64/libxml2.so.2.10.3) ==4859== by 0x8F98A7F: xmlNodeAddContentLen (in /usr/lib64/libxml2.so.2.10.3) ==4859== by 0x82C8C52: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82C8D58: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82C8D58: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82C8D58: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82CE58F: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82CEB07: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== by 0x82F6EFB: ??? (in /usr/lib64/libappstream.so.0.15.6) ==4859== ==4859== Invalid read of size 8 ==4859== at 0x68E1C00: QObjectPrivate::maybeSignalConnected(unsigned int) const (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x68F004D: ??? (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x49363F1: ResultsStream::resourcesFound(QVector<AbstractResource*> const&) (moc_AbstractResourcesBackend.cpp:159) ==4859== by 0x28443DE8: UnknownInlinedFun (PackageKitBackend.cpp:532) ==4859== by 0x28443DE8: UnknownInlinedFun (PackageKitBackend.cpp:521) ==4859== by 0x28443DE8: UnknownInlinedFun (PackageKitBackend.cpp:626) ==4859== by 0x28443DE8: UnknownInlinedFun (invoke.h:61) ==4859== by 0x28443DE8: UnknownInlinedFun (invoke.h:111) ==4859== by 0x28443DE8: std::_Function_handler<void (), PackageKitBackend::search(AbstractResourcesBackend::Filters const&)::{lambda()#9}>::_M_invoke(std::_Any_data const&) [clone .lto_priv.0] (std_function.h:290) ==4859== by 0x68E4C4F: QObject::event(QEvent*) (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x4B9344D: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/lib64/libQt5Widgets.so.5.15.8) ==4859== by 0x68B9137: QCoreApplication::notifyInternal2(QObject*, QEvent*) (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x68BC0D0: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x6911362: ??? (in /usr/lib64/libQt5Core.so.5.15.8) ==4859== by 0x81A6A8F: g_main_context_dispatch (in /usr/lib64/libglib-2.0.so.0.7400.4) ==4859== by 0x81A6E47: ??? (in /usr/lib64/libglib-2.0.so.0.7400.4) ==4859== by 0x81A6EDB: g_main_context_iteration (in /usr/lib64/libglib-2.0.so.0.7400.4) ==4859== Address 0x6e69687469772075 is not stack'd, malloc'd or (recently) free'd Created attachment 155478 [details]
Terminal output when starting Discover
I can reproduce this on a fresh install of KDE neon Testing.
SYSTEM INFORMATION
Operating System: KDE neon Testing Edition
KDE Plasma Version: 5.26.90
KDE Frameworks Version: 5.102.0
Qt Version: 5.15.8
Kernel Version: 5.15.0-58-generic (64-bit)
Graphics Platform: X11
Processors: 16 × AMD Ryzen 7 5700U with Radeon Graphics
Memory: 15.0 GiB of RAM
Graphics Processor: AMD Radeon Graphics
Manufacturer: LENOVO
Product Name: 82LM
System Version: IdeaPad 5 14ALC05
I can also confirm that after deleting .cache/discover Discover starts as expected again. > The crash is not directly related, appears to be some use-after-free:
Indeed. The bug with categories caching causes ResourcesProxyModel::invalidateFilter() to be called for all resources/streams which calls "delete" on them. This happens while the backends are still busy fetching stuff for those streams, so they end up calling ResultsStream::resourcesFound on dangling pointers.
It's a bug in plasma-desktop's attica-kde plugin triggering a bug in QNetworkDiskCache: const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/attica"); QNetworkDiskCache *cache = new QNetworkDiskCache(m_accessManager); QStorageInfo storageInfo(cacheDir); cache->setCacheDirectory(cacheDir); cache->setMaximumCacheSize(storageInfo.bytesTotal() / 1000); QStorageInfo returns -1 there if the cache directory didn't exist yet. QNetworkDiskCache happily accepts 0 as max size and eagerly deletes everything it can on every occasion. This happens in the worst possible spot, QNetworkDiskCachePrivate::storeItem: void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem) { ... currentCacheSize = q->expire(); if (!cacheItem->file) { QString templateName = tmpCacheFileName(); cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data); if (cacheItem->file->open()) { cacheItem->writeHeader(cacheItem->file); cacheItem->writeCompressedData(cacheItem->file); } } if (cacheItem->file && cacheItem->file->isOpen() && cacheItem->file->error() == QFile::NoError) { cacheItem->file->setAutoRemove(false); // ### use atomic rename rather then remove & rename if (cacheItem->file->rename(fileName)) currentCacheSize += cacheItem->file->size(); else cacheItem->file->setAutoRemove(true); } cacheItem->file is the temporary file with the cached contents. q->expire() however notices that the cache is too big and deletes it, setting cacheItem->file to nullptr. This unfortunately triggers the if block below, which is only meant for compressed caches, which are backed by cacheItem->data instead of cacheItem->file. The categories cache will not be compressed, which means that cacheItem->data is empty and it writes no actual data into the new file. The in hindsight ridiculously simple fix for the attica-kde bug: https://invent.kde.org/plasma/plasma-desktop/-/merge_requests/1343 Git commit 63788e6520a64a0846e4ee5ff0f2693730604986 by Fushan Wen, on behalf of Fabian Vogt. Committed on 22/01/2023 at 09:22. Pushed by fusionfuture into branch 'master'. attica-kde: Use the correct location for determining the max cache size QStorageInfo returns -1 there if the cache directory didn't exist yet. QNetworkDiskCache happily accepts 0 as max size and eagerly deletes everything it can on every occasion, triggering cache corruption. M +1 -1 attica-kde/kdeplugin/kdeplatformdependent.cpp https://invent.kde.org/plasma/plasma-desktop/commit/63788e6520a64a0846e4ee5ff0f2693730604986 Git commit 884e1988a064ec132b351d4f9b1a272d3627829c by Fushan Wen, on behalf of Fabian Vogt. Committed on 22/01/2023 at 09:28. Pushed by fusionfuture into branch 'cherry-pick-63788e65'. attica-kde: Use the correct location for determining the max cache size QStorageInfo returns -1 there if the cache directory didn't exist yet. QNetworkDiskCache happily accepts 0 as max size and eagerly deletes everything it can on every occasion, triggering cache corruption. (cherry picked from commit 63788e6520a64a0846e4ee5ff0f2693730604986) M +1 -1 attica-kde/kdeplugin/kdeplatformdependent.cpp https://invent.kde.org/plasma/plasma-desktop/commit/884e1988a064ec132b351d4f9b1a272d3627829c Git commit ec5cc12b4c4119672acd95680e897ac96e573ee8 by Fushan Wen, on behalf of Fabian Vogt. Committed on 22/01/2023 at 09:34. Pushed by fusionfuture into branch 'cherry-pick-63788e65-2'. attica-kde: Use the correct location for determining the max cache size QStorageInfo returns -1 there if the cache directory didn't exist yet. QNetworkDiskCache happily accepts 0 as max size and eagerly deletes everything it can on every occasion, triggering cache corruption. (cherry picked from commit 63788e6520a64a0846e4ee5ff0f2693730604986) M +1 -1 attica-kde/kdeplugin/kdeplatformdependent.cpp https://invent.kde.org/plasma/plasma-desktop/commit/ec5cc12b4c4119672acd95680e897ac96e573ee8 I'll report the QNetworkDiskCache bug to Qt soon. https://bugs.kde.org/show_bug.cgi?id=463864 has the same backtrace as the triggered UAF, so I think that can particular bug can continue there. A possibly relevant merge request was started @ https://invent.kde.org/plasma/discover/-/merge_requests/671 Git commit 90e271b96974adb009e27570855d7040d1b3cea7 by Aleix Pol Gonzalez, on behalf of David Edmundson. Committed on 24/10/2023 at 02:29. Pushed by apol into branch 'master'. Fix network cache size for first run QNetworkDiskCache will take care of creating a directory if it doesn't exist. QStorageInfo on a directory that doesn't exist will return that it has 0 bytesTotal. As well as being potentially inefficient for the second run, it has been reported that a QNetworkDiskCache of 0 causes out of bounds memory writes. M +1 -1 libdiscover/CachedNetworkAccessManager.cpp https://invent.kde.org/plasma/discover/-/commit/90e271b96974adb009e27570855d7040d1b3cea7 A possibly relevant merge request was started @ https://invent.kde.org/plasma/discover/-/merge_requests/675 Git commit b98e9209b84473fcaa158d6a2d0710a51e3e43c3 by David Edmundson. Committed on 24/10/2023 at 10:06. Pushed by davidedmundson into branch 'Plasma/5.27'. Fix network cache size for first run QNetworkDiskCache will take care of creating a directory if it doesn't exist. QStorageInfo on a directory that doesn't exist will return that it has 0 bytesTotal. As well as being potentially inefficient for the second run, it has been reported that a QNetworkDiskCache of 0 causes out of bounds memory writes. (cherry picked from commit 90e271b96974adb009e27570855d7040d1b3cea7) M +1 -1 libdiscover/CachedNetworkAccessManager.cpp https://invent.kde.org/plasma/discover/-/commit/b98e9209b84473fcaa158d6a2d0710a51e3e43c3 |