Compare commits

...

2 commits

Author SHA1 Message Date
13b6eeaa22
core/reloader: null generation ref in reloadables on destruction
On the post-reload reloadable initialzation path, a timer is used to
delay reload(). This change fixes a UAF when switching generations
while that timer is running.
2024-08-30 16:29:59 -07:00
3edb3f4efa
core/reloader: disconnect old generation before reloading
Previously the old generation was not disconnected, causing the root
wrapper's generation pointer to be nulled when pointing to the new
generation, leaving multiple shell versions running simultaneously.
2024-08-30 16:29:48 -07:00
3 changed files with 20 additions and 6 deletions

View file

@ -16,6 +16,15 @@ void Reloadable::componentComplete() {
if (this->engineGeneration->reloadComplete) {
// Delayed due to Component.onCompleted running after QQmlParserStatus::componentComplete.
QTimer::singleShot(0, this, &Reloadable::onReloadFinished);
// This only matters for preventing the above timer from UAFing the generation,
// so it isn't connected anywhere else.
QObject::connect(
this->engineGeneration,
&QObject::destroyed,
this,
&Reloadable::onGenerationDestroyed
);
} else {
QObject::connect(
this->engineGeneration,
@ -43,6 +52,7 @@ void Reloadable::reload(QObject* oldInstance) {
}
void Reloadable::onReloadFinished() { this->reload(nullptr); }
void Reloadable::onGenerationDestroyed() { this->engineGeneration = nullptr; }
void ReloadPropagator::onReload(QObject* oldInstance) {
auto* old = qobject_cast<ReloadPropagator*>(oldInstance);

View file

@ -71,6 +71,7 @@ public:
private slots:
void onReloadFinished();
void onGenerationDestroyed();
protected:
// Called unconditionally in the reload phase, with nullptr if no source could be determined.

View file

@ -17,10 +17,10 @@
#include "shell.hpp"
RootWrapper::RootWrapper(QString rootPath, QString shellId)
: QObject(nullptr)
, rootPath(std::move(rootPath))
, shellId(std::move(shellId))
, originalWorkingDirectory(QDir::current().absolutePath()) {
: QObject(nullptr)
, rootPath(std::move(rootPath))
, shellId(std::move(shellId))
, originalWorkingDirectory(QDir::current().absolutePath()) {
// clang-format off
QObject::connect(QuickshellSettings::instance(), &QuickshellSettings::watchFilesChanged, this, &RootWrapper::onWatchFilesChanged);
// clang-format on
@ -92,11 +92,14 @@ void RootWrapper::reloadGraph(bool hard) {
component.completeCreate();
if (this->generation) {
QObject::disconnect(this->generation, nullptr, this, nullptr);
}
auto isReload = this->generation != nullptr;
generation->onReload(hard ? nullptr : this->generation);
if (hard && this->generation != nullptr) {
QObject::disconnect(this->generation, nullptr, this, nullptr);
if (hard && this->generation) {
this->generation->destroy();
}