QImage: introduce a class invariant that format() is in-range

Coverity complains that image.format() is used to index into
qPixelLayouts[] when it may be NImageFormats.

Most code paths (e.g. QImageData::create()) defended against that, but
e.g. QImage::reinterpretAsFormat() did not (at least not overtly), as
didn't some of the backend functions.

Add checks, document the invariant on 'format' and assert it in
QImage::format().

If this doesn't convince Coverity, we'll need to turn qPixelLayouts[]
into a function, so that it can defend itself against out-of-bounds
access.

Pick-to: 6.8
Coverity-Id: 390711
Coverity-Id: 390720
Coverity-Id: 390758
Change-Id: I29431193face3cae8be56f01da8dced19c3abb38
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
(cherry picked from commit a2e60ebee3737548d1be14fdbb39b08c515ae602)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2025-03-12 10:16:07 +01:00 committed by Qt Cherry-pick Bot
parent cd395a7999
commit 52b20a1351
3 changed files with 19 additions and 1 deletions

View File

@ -2158,6 +2158,11 @@ void QImage::setColorCount(int colorCount)
*/
QImage::Format QImage::format() const
{
if (d) {
// Class Invariant Check
Q_ASSERT(d->format < NImageFormats);
Q_ASSERT(d->format > Format_Invalid);
}
return d ? d->format : Format_Invalid;
}
@ -2361,6 +2366,8 @@ QImage QImage::convertToFormat(Format format, const QList<QRgb> &colorTable, Qt:
bool QImage::reinterpretAsFormat(Format format)
{
if (format <= Format_Invalid || format >= NImageFormats)
return false;
if (!d)
return false;
if (d->format == format)

View File

@ -345,6 +345,7 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im
{
// Cannot be used with indexed formats or between formats with different pixel depths.
Q_ASSERT(dst_format > QImage::Format_Indexed8);
Q_ASSERT(dst_format < QImage::NImageFormats);
Q_ASSERT(data->format > QImage::Format_Indexed8);
const int destDepth = qt_depthForFormat(dst_format);
if (data->depth < destDepth)
@ -478,6 +479,7 @@ bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_for
{
Q_ASSERT(data->format > QImage::Format_Indexed8);
Q_ASSERT(dst_format > QImage::Format_Indexed8);
Q_ASSERT(dst_format < QImage::NImageFormats);
const int destDepth = qt_depthForFormat(dst_format);
if (data->depth < destDepth)
return false;
@ -572,6 +574,7 @@ bool convert_generic_inplace_over_rgba32f(QImageData *data, QImage::Format dst_f
{
Q_ASSERT(data->format >= QImage::Format_RGBX16FPx4);
Q_ASSERT(dst_format >= QImage::Format_RGBX16FPx4);
Q_ASSERT(dst_format < QImage::NImageFormats);
const int destDepth = qt_depthForFormat(dst_format);
if (data->depth < destDepth)
return false;
@ -682,6 +685,8 @@ static void convert_passthrough(QImageData *dest, const QImageData *src, Qt::Ima
template<QImage::Format Format>
static bool convert_passthrough_inplace(QImageData *data, Qt::ImageConversionFlags)
{
static_assert(Format > QImage::Format_Invalid);
static_assert(Format < QImage::NImageFormats);
data->format = Format;
return true;
}
@ -839,6 +844,8 @@ static void convert_ARGB_to_RGBA(QImageData *dest, const QImageData *src, Qt::Im
template<QImage::Format DestFormat>
static bool convert_ARGB_to_RGBA_inplace(QImageData *data, Qt::ImageConversionFlags)
{
static_assert(DestFormat > QImage::Format_Invalid);
static_assert(DestFormat < QImage::NImageFormats);
Q_ASSERT(data->format == QImage::Format_ARGB32 || data->format == QImage::Format_ARGB32_Premultiplied);
const int pad = (data->bytes_per_line >> 2) - data->width;
@ -885,6 +892,8 @@ static void convert_RGBA_to_ARGB(QImageData *dest, const QImageData *src, Qt::Im
template<QImage::Format DestFormat>
static bool convert_RGBA_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags)
{
static_assert(DestFormat > QImage::Format_Invalid);
static_assert(DestFormat < QImage::NImageFormats);
Q_ASSERT(data->format == QImage::Format_RGBX8888 || data->format == QImage::Format_RGBA8888 || data->format == QImage::Format_RGBA8888_Premultiplied);
const int pad = (data->bytes_per_line >> 2) - data->width;
@ -1233,6 +1242,8 @@ static void mask_alpha_converter(QImageData *dest, const QImageData *src, Qt::Im
template<QImage::Format DestFormat>
static bool mask_alpha_converter_inplace(QImageData *data, Qt::ImageConversionFlags)
{
static_assert(DestFormat > QImage::Format_Invalid);
static_assert(DestFormat < QImage::NImageFormats);
Q_ASSERT(data->format == QImage::Format_RGB32
|| DestFormat == QImage::Format_RGB32
|| DestFormat == QImage::Format_RGBX8888);

View File

@ -49,7 +49,7 @@ struct Q_GUI_EXPORT QImageData { // internal image data
qreal devicePixelRatio;
QList<QRgb> colortable;
uchar *data;
QImage::Format format;
QImage::Format format; // invariants: > Format_Invalid, < NImageFormats
qsizetype bytes_per_line;
int ser_no; // serial number
int detach_no;