Fix image allocation with Qt 6

To make the plugins fail to allocate if the image size is greater than QImageReader::allocationLimit() it is necessary to allocate the image with QImageIOHandler::allocateImage().

Note that not all plugins have been changed and some others are not tested in the CI (maybe due to missing libraries).

PS: the following message is printed by QImageIOHandler::allocateImage() if the size is exceeded: "qt.gui.imageio: QImageIOHandler: Rejecting image as it exceeds the current allocation limit of XXX megabytes"
This commit is contained in:
Mirco Miranda
2022-09-07 14:03:33 +00:00
committed by Albert Astals Cid
parent dfbc6e0f8c
commit feb6d9b20f
13 changed files with 85 additions and 30 deletions

View File

@ -12,6 +12,8 @@
#include <QColorSpace> #include <QColorSpace>
#include "avif_p.h" #include "avif_p.h"
#include "util_p.h"
#include <cfloat> #include <cfloat>
QAVIFHandler::QAVIFHandler() QAVIFHandler::QAVIFHandler()
@ -235,8 +237,8 @@ bool QAVIFHandler::decode_one_frame()
resultformat = QImage::Format_RGB32; resultformat = QImage::Format_RGB32;
} }
} }
QImage result(m_decoder->image->width, m_decoder->image->height, resultformat);
QImage result = imageAlloc(m_decoder->image->width, m_decoder->image->height, resultformat);
if (result.isNull()) { if (result.isNull()) {
qWarning("Memory cannot be allocated"); qWarning("Memory cannot be allocated");
return false; return false;

View File

@ -8,6 +8,7 @@
*/ */
#include "exr_p.h" #include "exr_p.h"
#include "util_p.h"
#include <IexThrowErrnoExc.h> #include <IexThrowErrnoExc.h>
#include <ImathBox.h> #include <ImathBox.h>
@ -191,7 +192,7 @@ bool EXRHandler::read(QImage *outImage)
width = dw.max.x - dw.min.x + 1; width = dw.max.x - dw.min.x + 1;
height = dw.max.y - dw.min.y + 1; height = dw.max.y - dw.min.y + 1;
QImage image(width, height, QImage::Format_RGB32); QImage image = imageAlloc(width, height, QImage::Format_RGB32);
if (image.isNull()) { if (image.isNull()) {
qWarning() << "Failed to allocate image, invalid size?" << QSize(width, height); qWarning() << "Failed to allocate image, invalid size?" << QSize(width, height);
return false; return false;

View File

@ -7,6 +7,7 @@
*/ */
#include "hdr_p.h" #include "hdr_p.h"
#include "util_p.h"
#include <QDataStream> #include <QDataStream>
#include <QImage> #include <QImage>
@ -93,7 +94,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
uchar code; uchar code;
// Create dst image. // Create dst image.
img = QImage(width, height, QImage::Format_RGB32); img = imageAlloc(width, height, QImage::Format_RGB32);
if (img.isNull()) { if (img.isNull()) {
qCDebug(HDRPLUGIN) << "Couldn't create image with size" << width << height << "and format RGB32"; qCDebug(HDRPLUGIN) << "Couldn't create image with size" << width << height << "and format RGB32";
return false; return false;

View File

@ -9,6 +9,7 @@
#include "heif_p.h" #include "heif_p.h"
#include "libheif/heif_cxx.h" #include "libheif/heif_cxx.h"
#include "util_p.h"
#include <QColorSpace> #include <QColorSpace>
#include <QDebug> #include <QDebug>
@ -432,7 +433,7 @@ bool HEIFHandler::ensureDecoder()
return false; return false;
} }
m_current_image = QImage(imageSize, target_image_format); m_current_image = imageAlloc(imageSize, target_image_format);
if (m_current_image.isNull()) { if (m_current_image.isNull()) {
m_parseState = ParseHeicError; m_parseState = ParseHeicError;
qWarning() << "Unable to allocate memory!"; qWarning() << "Unable to allocate memory!";

View File

@ -10,6 +10,8 @@
#include <QtGlobal> #include <QtGlobal>
#include "jxl_p.h" #include "jxl_p.h"
#include "util_p.h"
#include <jxl/encode.h> #include <jxl/encode.h>
#include <jxl/thread_parallel_runner.h> #include <jxl/thread_parallel_runner.h>
#include <string.h> #include <string.h>
@ -359,7 +361,7 @@ bool QJpegXLHandler::decode_one_frame()
return false; return false;
} }
m_current_image = QImage(m_basicinfo.xsize, m_basicinfo.ysize, m_input_image_format); m_current_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, m_input_image_format);
if (m_current_image.isNull()) { if (m_current_image.isNull()) {
qWarning("Memory cannot be allocated"); qWarning("Memory cannot be allocated");
m_parseState = ParseJpegXLError; m_parseState = ParseJpegXLError;

View File

@ -6,6 +6,7 @@
*/ */
#include "pcx_p.h" #include "pcx_p.h"
#include "util_p.h"
#include <QColor> #include <QColor>
#include <QDataStream> #include <QDataStream>
@ -262,7 +263,7 @@ static void readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
{ {
QByteArray buf(header.BytesPerLine, 0); QByteArray buf(header.BytesPerLine, 0);
img = QImage(header.width(), header.height(), QImage::Format_Mono); img = imageAlloc(header.width(), header.height(), QImage::Format_Mono);
img.setColorCount(2); img.setColorCount(2);
if (img.isNull()) { if (img.isNull()) {
@ -294,7 +295,7 @@ static void readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
QByteArray buf(header.BytesPerLine * 4, 0); QByteArray buf(header.BytesPerLine * 4, 0);
QByteArray pixbuf(header.width(), 0); QByteArray pixbuf(header.width(), 0);
img = QImage(header.width(), header.height(), QImage::Format_Indexed8); img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
img.setColorCount(16); img.setColorCount(16);
if (img.isNull()) { if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height()); qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
@ -338,7 +339,7 @@ static void readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
{ {
QByteArray buf(header.BytesPerLine, 0); QByteArray buf(header.BytesPerLine, 0);
img = QImage(header.width(), header.height(), QImage::Format_Indexed8); img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
img.setColorCount(256); img.setColorCount(256);
if (img.isNull()) { if (img.isNull()) {
@ -388,7 +389,7 @@ static void readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
QByteArray g_buf(header.BytesPerLine, 0); QByteArray g_buf(header.BytesPerLine, 0);
QByteArray b_buf(header.BytesPerLine, 0); QByteArray b_buf(header.BytesPerLine, 0);
img = QImage(header.width(), header.height(), QImage::Format_RGB32); img = imageAlloc(header.width(), header.height(), QImage::Format_RGB32);
if (img.isNull()) { if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height()); qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());

View File

@ -14,8 +14,8 @@
*/ */
#include "pic_p.h" #include "pic_p.h"
#include "rle_p.h" #include "rle_p.h"
#include "util_p.h"
#include <QDataStream> #include <QDataStream>
#include <QDebug> #include <QDebug>
@ -238,7 +238,7 @@ bool SoftimagePICHandler::read(QImage *image)
} }
} }
QImage img(m_header.width, m_header.height, fmt); QImage img = imageAlloc(m_header.width, m_header.height, fmt);
if (img.isNull()) { if (img.isNull()) {
qDebug() << "Failed to allocate image, invalid dimensions?" << QSize(m_header.width, m_header.height) << fmt; qDebug() << "Failed to allocate image, invalid dimensions?" << QSize(m_header.width, m_header.height) << fmt;
return false; return false;

View File

@ -34,7 +34,6 @@
*/ */
#include "psd_p.h" #include "psd_p.h"
#include "util_p.h" #include "util_p.h"
#include <QDataStream> #include <QDataStream>
@ -1013,7 +1012,7 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
return false; return false;
} }
img = QImage(header.width, header.height, format); img = imageAlloc(header.width, header.height, format);
if (img.isNull()) { if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width, header.height); qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width, header.height);
return false; return false;

View File

@ -8,7 +8,6 @@
*/ */
#include "ras_p.h" #include "ras_p.h"
#include "util_p.h" #include "util_p.h"
#include <QDataStream> #include <QDataStream>
@ -152,8 +151,7 @@ static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
} }
// Allocate image // Allocate image
img = QImage(ras.Width, ras.Height, QImage::Format_ARGB32); img = imageAlloc(ras.Width, ras.Height, QImage::Format_ARGB32);
if (img.isNull()) { if (img.isNull()) {
return false; return false;
} }

View File

@ -20,6 +20,7 @@
*/ */
#include "rgb_p.h" #include "rgb_p.h"
#include "util_p.h"
#include <QMap> #include <QMap>
#include <QVector> #include <QVector>
@ -324,7 +325,7 @@ bool SGIImage::readImage(QImage &img)
return false; return false;
} }
img = QImage(_xsize, _ysize, QImage::Format_RGB32); img = imageAlloc(_xsize, _ysize, QImage::Format_RGB32);
if (img.isNull()) { if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(_xsize, _ysize); qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(_xsize, _ysize);
return false; return false;

View File

@ -17,6 +17,7 @@
*/ */
#include "tga_p.h" #include "tga_p.h"
#include "util_p.h"
#include <assert.h> #include <assert.h>
@ -172,7 +173,7 @@ struct TgaHeaderInfo {
static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img) static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img)
{ {
// Create image. // Create image.
img = QImage(tga.width, tga.height, QImage::Format_RGB32); img = imageAlloc(tga.width, tga.height, QImage::Format_RGB32);
if (img.isNull()) { if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height); qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height);
return false; return false;
@ -184,7 +185,7 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img)
const int numAlphaBits = tga.flags & 0xf; const int numAlphaBits = tga.flags & 0xf;
// However alpha exists only in the 32 bit format. // However alpha exists only in the 32 bit format.
if ((tga.pixel_size == 32) && (tga.flags & 0xf)) { if ((tga.pixel_size == 32) && (tga.flags & 0xf)) {
img = QImage(tga.width, tga.height, QImage::Format_ARGB32); img = imageAlloc(tga.width, tga.height, QImage::Format_ARGB32);
if (img.isNull()) { if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height); qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height);
return false; return false;

View File

@ -1,10 +1,42 @@
/* /*
SPDX-FileCopyrightText: 2022 Albert Astals Cid <aacid@kde.org> SPDX-FileCopyrightText: 2022 Albert Astals Cid <aacid@kde.org>
SPDX-FileCopyrightText: 2022 Mirco Miranda <mircomir@outlook.com>
SPDX-License-Identifier: LGPL-2.0-or-later SPDX-License-Identifier: LGPL-2.0-or-later
*/ */
#ifndef UTIL_P_H
#define UTIL_P_H
#include <limits> #include <limits>
#include <QImage>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QImageIOHandler>
#endif
// QVector uses some extra space for stuff, hence the 32 here suggested by Thiago Macieira // QVector uses some extra space for stuff, hence the 32 here suggested by Thiago Macieira
static constexpr int kMaxQVectorSize = std::numeric_limits<int>::max() - 32; static constexpr int kMaxQVectorSize = std::numeric_limits<int>::max() - 32;
// On Qt 6 to make the plugins fail to allocate if the image size is greater than QImageReader::allocationLimit()
// it is necessary to allocate the image with QImageIOHandler::allocateImage().
inline QImage imageAlloc(const QSize &size, const QImage::Format &format)
{
QImage img;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
img = QImage(size, format);
#else
if (!QImageIOHandler::allocateImage(size, format, &img)) {
img = QImage(); // paranoia
}
#endif
return img;
}
inline QImage imageAlloc(qint32 width, qint32 height, const QImage::Format &format)
{
return imageAlloc(QSize(width, height), format);
}
#endif // UTIL_P_H

View File

@ -6,6 +6,7 @@
SPDX-License-Identifier: LGPL-2.1-or-later SPDX-License-Identifier: LGPL-2.1-or-later
*/ */
#include "util_p.h"
#include "xcf_p.h" #include "xcf_p.h"
#include <QDebug> #include <QDebug>
@ -17,6 +18,9 @@
#include <QVector> #include <QVector>
#include <QtEndian> #include <QtEndian>
#include <QColorSpace> #include <QColorSpace>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QImageReader>
#endif
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -1099,6 +1103,18 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
return false; return false;
} }
// Qt 6 image allocation limit calculation: we have to check the limit here because the image is splitted in
// tiles of 64x64 pixels. The required memory to build the image is at least doubled because tiles are loaded
// and then the final image is created by copying the tiles inside it.
// NOTE: on Windows to open a 10GiB image the plugin uses 28GiB of RAM
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
if (qint64(layer.width) * qint64(layer.height) * channels * 2ll / 1024ll / 1024ll > QImageReader::allocationLimit()) {
qCDebug(XCFPLUGIN) << "Rejecting image as it exceeds the current allocation limit of" << QImageReader::allocationLimit() << "megabytes";
return false;
}
#endif
layer.image_tiles.resize(layer.nrows); layer.image_tiles.resize(layer.nrows);
if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) { if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
@ -1917,7 +1933,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
switch (layer.type) { switch (layer.type) {
case RGB_GIMAGE: case RGB_GIMAGE:
if (layer.opacity == OPAQUE_OPACITY) { if (layer.opacity == OPAQUE_OPACITY) {
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_RGB32); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_RGB32);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
} }
@ -1926,7 +1942,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
} // else, fall through to 32-bit representation } // else, fall through to 32-bit representation
Q_FALLTHROUGH(); Q_FALLTHROUGH();
case RGBA_GIMAGE: case RGBA_GIMAGE:
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
} }
@ -1935,7 +1951,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
case GRAY_GIMAGE: case GRAY_GIMAGE:
if (layer.opacity == OPAQUE_OPACITY) { if (layer.opacity == OPAQUE_OPACITY) {
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image.setColorCount(256); image.setColorCount(256);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
@ -1946,7 +1962,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
} // else, fall through to 32-bit representation } // else, fall through to 32-bit representation
Q_FALLTHROUGH(); Q_FALLTHROUGH();
case GRAYA_GIMAGE: case GRAYA_GIMAGE:
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
} }
@ -1967,7 +1983,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
// or two-color palette. Have to ask about this... // or two-color palette. Have to ask about this...
if (xcf_image.num_colors <= 2) { if (xcf_image.num_colors <= 2) {
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
image.setColorCount(xcf_image.num_colors); image.setColorCount(xcf_image.num_colors);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
@ -1975,7 +1991,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
image.fill(0); image.fill(0);
setPalette(xcf_image, image); setPalette(xcf_image, image);
} else if (xcf_image.num_colors <= 256) { } else if (xcf_image.num_colors <= 256) {
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image.setColorCount(xcf_image.num_colors); image.setColorCount(xcf_image.num_colors);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
@ -1993,7 +2009,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
xcf_image.palette[1] = xcf_image.palette[0]; xcf_image.palette[1] = xcf_image.palette[0];
xcf_image.palette[0] = qRgba(255, 255, 255, 0); xcf_image.palette[0] = qRgba(255, 255, 255, 0);
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
image.setColorCount(xcf_image.num_colors); image.setColorCount(xcf_image.num_colors);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
@ -2009,7 +2025,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
} }
xcf_image.palette[0] = qRgba(255, 255, 255, 0); xcf_image.palette[0] = qRgba(255, 255, 255, 0);
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image.setColorCount(xcf_image.num_colors); image.setColorCount(xcf_image.num_colors);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
@ -2020,7 +2036,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
// No room for a transparent color, so this has to be promoted to // No room for a transparent color, so this has to be promoted to
// true color. (There is no equivalent PNG representation output // true color. (There is no equivalent PNG representation output
// from The GIMP as of v1.2.) // from The GIMP as of v1.2.)
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32); image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
if (image.isNull()) { if (image.isNull()) {
return false; return false;
} }
@ -2031,11 +2047,11 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) { if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
const float dpmx = xcf_image.x_resolution * INCHESPERMETER; const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
if (dpmx > std::numeric_limits<int>::max()) { if (dpmx > float(std::numeric_limits<int>::max())) {
return false; return false;
} }
const float dpmy = xcf_image.y_resolution * INCHESPERMETER; const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
if (dpmy > std::numeric_limits<int>::max()) { if (dpmy > float(std::numeric_limits<int>::max())) {
return false; return false;
} }
image.setDotsPerMeterX((int)dpmx); image.setDotsPerMeterX((int)dpmx);