From 52f4628fe0940faa096d4e099d6cb43c72cb2ff5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 21:34:15 +0200 Subject: [PATCH] Bound avatar manifest sizes before numeric conversion --- tools/chatgui/src/avatars/avatarcache.cpp | 5 +++-- tools/chatgui/tests/test_avatars.cpp | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/chatgui/src/avatars/avatarcache.cpp b/tools/chatgui/src/avatars/avatarcache.cpp index 16d70121..78649dc8 100644 --- a/tools/chatgui/src/avatars/avatarcache.cpp +++ b/tools/chatgui/src/avatars/avatarcache.cpp @@ -51,13 +51,14 @@ AvatarCache::Result AvatarCache::readObject(const QString& path, const QString& if (name.endsWith(".json")) { const QJsonObject o = QJsonDocument::fromJson(bytes).object(); Record& r = result.record; r.original = o["original"].toString(); r.preview = o["preview"].toString(); - r.originalBytes = qint64(o["original_bytes"].toDouble()); r.previewBytes = qint64(o["preview_bytes"].toDouble()); + r.originalBytes = qint64(qBound(-1.0, o["original_bytes"].toDouble(-1), double(OriginalMaxBytes) + 1)); + r.previewBytes = qint64(qBound(-1.0, o["preview_bytes"].toDouble(-1), double(MaxBytes) + 1)); r.size = QSize(o["width"].toInt(), o["height"].toInt()); r.crop = QRectF(o["x"].toDouble(-1), o["y"].toDouble(-1), o["w"].toDouble(), o["h"].toDouble()); const double pixelW = r.crop.width() * r.size.width(), pixelH = r.crop.height() * r.size.height(); if (o["version"].toInt() != 1 || !mf_avatar_file_valid(r.original.toLatin1().constData()) || r.original.endsWith(".json") || !mf_avatar_file_valid(r.preview.toLatin1().constData()) || !r.preview.endsWith(".webp") || - !r.size.isValid() || qint64(r.size.width()) * r.size.height() > 16 * 1024 * 1024 || + r.size.width() <= 0 || r.size.height() <= 0 || qint64(r.size.width()) * r.size.height() > 16 * 1024 * 1024 || r.originalBytes < 1 || r.originalBytes > OriginalMaxBytes || r.previewBytes < 1 || r.previewBytes > MaxBytes || !r.crop.isValid() || !QRectF(0, 0, 1, 1).contains(r.crop) || qAbs(pixelW - pixelH) > 2) { result.error = "invalid avatar manifest"; r = {}; diff --git a/tools/chatgui/tests/test_avatars.cpp b/tools/chatgui/tests/test_avatars.cpp index 261bd8a7..e1b2a67c 100644 --- a/tools/chatgui/tests/test_avatars.cpp +++ b/tools/chatgui/tests/test_avatars.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include extern "C" { #include "utun_instance.h" #include "chat/chat_core.h" @@ -77,6 +79,13 @@ private slots: QFile webp(directory.path() + '/' + prepared.record.preview); QVERIFY(webp.open(QIODevice::ReadOnly)); const QByteArray encoded=webp.readAll(); QCOMPARE(encoded.left(4),QByteArray("RIFF")); QVERIFY(encoded.contains("VP8 ")); QVERIFY(!AvatarCache::load(path, prepared.hash).image.isNull()); + QFile description(path); QVERIFY(description.open(QIODevice::ReadOnly)); + QJsonObject record=QJsonDocument::fromJson(description.readAll()).object(); description.close(); + record["original_bytes"]=1e99; + const QByteArray oversized=QJsonDocument(record).toJson(QJsonDocument::Compact); + const QString oversizedName=QCryptographicHash::hash(oversized,QCryptographicHash::Sha256).toHex()+".json"; + QString writeError; QVERIFY(AvatarCache::storeObject(directory.path(),oversizedName,oversized,writeError)); + QVERIFY(!AvatarCache::readObject(directory.path()+'/'+oversizedName,oversizedName).error.isEmpty()); QVERIFY(AvatarCache::load(path, QString(64, '0')).image.isNull()); QFile corrupt(path); QVERIFY(corrupt.open(QIODevice::Append)); QCOMPARE(corrupt.write("x"), 1LL); corrupt.close(); QVERIFY(AvatarCache::load(path, prepared.hash).image.isNull());