From 1002b269ac2ffd445a67e6dae4279f996104b0d6 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Fri, 2 Oct 2026 16:17:58 +1300 Subject: [PATCH] Fix a symbol with a huge size aborting QElectroTech in its preview ElementPictureFactory::pixmap() made a pixmap of whatever width and height the symbol file declares. A symbol whose parts span 280 000 px (made by the GUI fuzzer in the symbol editor) asked for a ~315 GB pixmap. Under AddressSanitizer that aborts QElectroTech; the symbol then sat in the user collection and every later start died loading its icon. The preview is now drawn scaled down to fit 4096 px on its longer side. The largest symbols in the shipped collection are 3160 px, so none of them changes. The size is also bounded before it is rounded up to a multiple of 10, so a crafted value near INT_MAX cannot overflow. Checked with a user collection holding one symbol declared 280000 x 280000, on ASan builds: master aborts (out of memory, exit 1) 3/3 when the collection is expanded; with this change QElectroTech keeps running and lists the symbol, 3/3. Co-Authored-By: Claude Opus 5.5 (1M context) --- sources/factory/elementpicturefactory.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/sources/factory/elementpicturefactory.cpp b/sources/factory/elementpicturefactory.cpp index c3b0f0745..f0ad1e22d 100644 --- a/sources/factory/elementpicturefactory.cpp +++ b/sources/factory/elementpicturefactory.cpp @@ -135,14 +135,29 @@ QPixmap ElementPictureFactory::pixmap(const ElementsLocation &location) { auto doc = location.pugiXml(); //size - int w = doc.document_element().attribute("width").as_int(); - int h = doc.document_element().attribute("height").as_int(); + //Bounded first, so the rounding below cannot overflow on a + //crafted file. + int w = qBound(0, doc.document_element().attribute("width").as_int(), 1000000); + int h = qBound(0, doc.document_element().attribute("height").as_int(), 1000000); while (w % 10) ++ w; while (h % 10) ++ h; //hotspot int hsx = qMin(doc.document_element().attribute("hotspot_x").as_int(), w); int hsy = qMin(doc.document_element().attribute("hotspot_y").as_int(), h); + //The size comes straight from the file. A symbol whose parts + //span hundreds of thousands of pixels would ask for a pixmap of + //hundreds of gigabytes; draw it scaled down to fit instead. The + //largest symbols in the shipped collection are about 3200 px, + //so this only changes files nobody would draw on purpose. + const int max_side = 4096; + qreal scale = 1.0; + if (w > max_side || h > max_side) { + scale = qreal(max_side) / qMax(w, h); + w = qMax(1, qRound(w * scale)); + h = qMax(1, qRound(h * scale)); + } + QPixmap pix(w, h); //Element definitions almost always draw with a hardcoded black //stroke color, on the assumption of the white diagram sheet they @@ -157,6 +172,7 @@ QPixmap ElementPictureFactory::pixmap(const ElementsLocation &location) QPainter painter(&pix); painter.setRenderHint(QPainter::Antialiasing, true); painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.scale(scale, scale); painter.translate(hsx, hsy); painter.drawPicture(0, 0, m_pictures_H.value(uuid));