core/colorquant: copy source to operations and cancel on destroy

Fixes UAFing the source url in ColorQuantizerOperation if the
ColorQuantizer is destroyed.
This commit is contained in:
outfoxxed committed 2026-08-15 20:53:18 -07:00
1 parent b1fc3e0740
commit 63eb2ba6ef
3 files changed
+15 -8

No files matched your search

+1
View File
@@ -15,3 +15,4 @@
- Fixed JsonAdapter crashing when deserializing new objects into an array.
- Fixed `Toplevel.unsetRectangle` crashing when calle.
- Fixed intermittent `FileView` crashes when updating watched files.
- Fixed `ColorQuantizer` deletion crashing if an operation was live.
+9 -6
View File
@@ -1,5 +1,6 @@
#include "colorquantizer.hpp"
#include <algorithm>
#include <utility>
#include <qatomic.h>
#include <qcolor.h>
@@ -26,12 +27,12 @@ QS_LOGGING_CATEGORY(logColorQuantizer, "quickshell.colorquantizer", QtWarningMsg
}
ColorQuantizerOperation::ColorQuantizerOperation(
QUrl* source,
QUrl source,
qreal depth,
QRect imageRect,
qreal rescaleSize
)
: source(source)
: source(std::move(source))
, maxDepth(depth)
, imageRect(imageRect)
, rescaleSize(rescaleSize) {
@@ -39,11 +40,11 @@ ColorQuantizerOperation::ColorQuantizerOperation(
}
void ColorQuantizerOperation::quantizeImage(const QAtomicInteger<bool>& shouldCancel) {
if (shouldCancel.loadAcquire() || this->source->isEmpty()) return;
if (shouldCancel.loadAcquire() || this->source.isEmpty()) return;
this->colors.clear();
auto image = QImage(this->source->toLocalFile());
auto image = QImage(this->source.toLocalFile());
if (this->imageRect.isValid()) {
image = image.copy(this->imageRect);
@@ -61,7 +62,7 @@ void ColorQuantizerOperation::quantizeImage(const QAtomicInteger<bool>& shouldCa
}
if (image.isNull()) {
qCWarning(logColorQuantizer) << "Failed to load image from" << this->source->toString();
qCWarning(logColorQuantizer) << "Failed to load image from" << this->source.toString();
return;
}
@@ -191,6 +192,8 @@ void ColorQuantizerOperation::run() {
void ColorQuantizerOperation::tryCancel() { this->shouldCancel.storeRelease(true); }
ColorQuantizer::~ColorQuantizer() { this->cancelAsync(); }
void ColorQuantizer::componentComplete() {
this->componentCompleted = true;
if (!this->mSource.isEmpty()) this->quantizeAsync();
@@ -246,7 +249,7 @@ void ColorQuantizer::quantizeAsync() {
qCDebug(logColorQuantizer) << "Starting color quantization asynchronously";
this->liveOperation = new ColorQuantizerOperation(
&this->mSource,
this->mSource,
this->mDepth,
this->mImageRect,
this->mRescaleSize
+5 -2
View File
@@ -7,6 +7,7 @@
#include <qqmlparserstatus.h>
#include <qrect.h>
#include <qrunnable.h>
#include <qtclasshelpermacros.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include <qurl.h>
@@ -17,7 +18,7 @@ class ColorQuantizerOperation
Q_OBJECT;
public:
explicit ColorQuantizerOperation(QUrl* source, qreal depth, QRect imageRect, qreal rescaleSize);
explicit ColorQuantizerOperation(QUrl source, qreal depth, QRect imageRect, qreal rescaleSize);
void run() override;
void tryCancel();
@@ -43,7 +44,7 @@ private:
QAtomicInteger<bool> shouldCancel = false;
QList<QColor> colors;
QUrl* source;
QUrl source;
qreal maxDepth;
QRect imageRect;
qreal rescaleSize;
@@ -94,6 +95,8 @@ class ColorQuantizer
public:
explicit ColorQuantizer(QObject* parent = nullptr): QObject(parent) {}
~ColorQuantizer() override;
Q_DISABLE_COPY_MOVE(ColorQuantizer);
void componentComplete() override;
void classBegin() override {}