summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/painting/qicc.cpp142
-rw-r--r--tests/libfuzzer/gui/iccparser/iccparser.pro3
-rw-r--r--tests/libfuzzer/gui/iccparser/main.cpp37
3 files changed, 130 insertions, 52 deletions
diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp
index 2abb42993b..53055b426c 100644
--- a/src/gui/painting/qicc.cpp
+++ b/src/gui/painting/qicc.cpp
@@ -190,23 +190,17 @@ static float fromFixedS1516(int x)
return x * (1.0f / 65536.0f);
}
-QColorVector fromXyzData(const XYZTagData *xyz)
-{
- const float x = fromFixedS1516(xyz->fixedX);
- const float y = fromFixedS1516(xyz->fixedY);
- const float z = fromFixedS1516(xyz->fixedZ);
- qCDebug(lcIcc) << "XYZ_ " << x << y << z;
-
- return QColorVector(x, y, z);
-}
-
static bool isValidIccProfile(const ICCProfileHeader &header)
{
if (header.signature != uint(Tag::acsp)) {
qCWarning(lcIcc, "Failed ICC signature test");
return false;
}
- if (header.profileSize < (sizeof(ICCProfileHeader) + header.tagCount * sizeof(TagTableEntry))) {
+
+ // Don't overflow 32bit integers:
+ if (header.tagCount >= INT32_MAX / sizeof(TagTableEntry))
+ return false;
+ if (header.profileSize - sizeof(ICCProfileHeader) < header.tagCount * sizeof(TagTableEntry)) {
qCWarning(lcIcc, "Failed basic size sanity");
return false;
}
@@ -413,17 +407,44 @@ QByteArray toIccProfile(const QColorSpace &space)
return iccProfile;
}
-bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma)
+struct TagEntry {
+ quint32 offset;
+ quint32 size;
+};
+
+bool parseXyzData(const QByteArray &data, const TagEntry &tagEntry, QColorVector &colorVector)
+{
+ if (tagEntry.size < sizeof(XYZTagData)) {
+ qCWarning(lcIcc) << "Undersized XYZ tag";
+ return false;
+ }
+ const XYZTagData *xyz = reinterpret_cast<const XYZTagData *>(data.constData() + tagEntry.offset);
+ if (xyz->type != quint32(Tag::XYZ_)) {
+ qCWarning(lcIcc) << "Bad XYZ content type";
+ return false;
+ }
+ const float x = fromFixedS1516(xyz->fixedX);
+ const float y = fromFixedS1516(xyz->fixedY);
+ const float z = fromFixedS1516(xyz->fixedZ);
+
+ colorVector = QColorVector(x, y, z);
+ return true;
+}
+
+bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma)
{
+ const GenericTagData *trcData = reinterpret_cast<const GenericTagData *>(data.constData() + tagEntry.offset);
if (trcData->type == quint32(Tag::curv)) {
- const CurvTagData *curv = reinterpret_cast<const CurvTagData *>(trcData);
- qCDebug(lcIcc) << "curv" << uint(curv->valueCount);
+ const CurvTagData *curv = static_cast<const CurvTagData *>(trcData);
+ if (curv->valueCount > (1 << 16))
+ return false;
+ if (tagEntry.size - 12 < 2 * curv->valueCount)
+ return false;
if (curv->valueCount == 0) {
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(); // Linear
} else if (curv->valueCount == 1) {
float g = curv->value[0] * (1.0f / 256.0f);
- qCDebug(lcIcc) << g;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction::fromGamma(g);
} else {
@@ -445,49 +466,54 @@ bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma)
return true;
}
if (trcData->type == quint32(Tag::para)) {
- const ParaTagData *para = reinterpret_cast<const ParaTagData *>(trcData);
- qCDebug(lcIcc) << "para" << uint(para->curveType);
+ if (tagEntry.size < sizeof(ParaTagData))
+ return false;
+ const ParaTagData *para = static_cast<const ParaTagData *>(trcData);
switch (para->curveType) {
case 0: {
float g = fromFixedS1516(para->parameter[0]);
- qCDebug(lcIcc) << g;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction::fromGamma(g);
break;
}
case 1: {
+ if (tagEntry.size < sizeof(ParaTagData) + 2 * 4)
+ return false;
float g = fromFixedS1516(para->parameter[0]);
float a = fromFixedS1516(para->parameter[1]);
float b = fromFixedS1516(para->parameter[2]);
float d = -b / a;
- qCDebug(lcIcc) << g << a << b;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, 0.0f, 0.0f, g);
break;
}
case 2: {
+ if (tagEntry.size < sizeof(ParaTagData) + 3 * 4)
+ return false;
float g = fromFixedS1516(para->parameter[0]);
float a = fromFixedS1516(para->parameter[1]);
float b = fromFixedS1516(para->parameter[2]);
float c = fromFixedS1516(para->parameter[3]);
float d = -b / a;
- qCDebug(lcIcc) << g << a << b << c;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, c, c, g);
break;
}
case 3: {
+ if (tagEntry.size < sizeof(ParaTagData) + 4 * 4)
+ return false;
float g = fromFixedS1516(para->parameter[0]);
float a = fromFixedS1516(para->parameter[1]);
float b = fromFixedS1516(para->parameter[2]);
float c = fromFixedS1516(para->parameter[3]);
float d = fromFixedS1516(para->parameter[4]);
- qCDebug(lcIcc) << g << a << b << c << d;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, c, d, 0.0f, 0.0f, g);
break;
}
case 4: {
+ if (tagEntry.size < sizeof(ParaTagData) + 6 * 4)
+ return false;
float g = fromFixedS1516(para->parameter[0]);
float a = fromFixedS1516(para->parameter[1]);
float b = fromFixedS1516(para->parameter[2]);
@@ -495,7 +521,6 @@ bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma)
float d = fromFixedS1516(para->parameter[4]);
float e = fromFixedS1516(para->parameter[5]);
float f = fromFixedS1516(para->parameter[6]);
- qCDebug(lcIcc) << g << a << b << c << d << e << f;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, c, d, e, f, g);
break;
@@ -529,8 +554,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
// Read tag index
const TagTableEntry *tagTable = (const TagTableEntry *)(data.constData() + sizeof(ICCProfileHeader));
const qsizetype offsetToData = sizeof(ICCProfileHeader) + header->tagCount * sizeof(TagTableEntry);
+ if (offsetToData > data.size()) {
+ qCWarning(lcIcc) << "fromIccProfile: failed index size sanity";
+ return false;
+ }
- QHash<Tag, quint32> tagIndex;
+ QHash<Tag, TagEntry> tagIndex;
for (uint i = 0; i < header->tagCount; ++i) {
// Sanity check tag sizes and offsets:
if (qsizetype(tagTable[i].offset) < offsetToData) {
@@ -542,15 +571,24 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 2";
return false;
}
- if ((tagTable[i].offset + tagTable[i].size) > header->profileSize) {
+ if (tagTable[i].size < 12) {
+ qCWarning(lcIcc) << "fromIccProfile: failed minimal tag size sanity";
+ return false;
+ }
+ if (tagTable[i].size > header->profileSize - tagTable[i].offset) {
qCWarning(lcIcc) << "fromIccProfile: failed tag offset + size sanity";
return false;
}
+ if (tagTable[i].offset & 0x03) {
+ qCWarning(lcIcc) << "fromIccProfile: invalid tag offset alignment";
+ return false;
+ }
// printf("'%4s' %d %d\n", (const char *)&tagTable[i].signature,
// quint32(tagTable[i].offset),
// quint32(tagTable[i].size));
- tagIndex.insert(Tag(quint32(tagTable[i].signature)), tagTable[i].offset);
+ tagIndex.insert(Tag(quint32(tagTable[i].signature)), { tagTable[i].offset, tagTable[i].size });
}
+
// Check the profile is three-component matrix based (what we currently support):
if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) ||
!tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) ||
@@ -559,23 +597,17 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
return false;
}
- // Parse XYZ tags
- const XYZTagData *rXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::rXYZ]);
- const XYZTagData *gXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::gXYZ]);
- const XYZTagData *bXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::bXYZ]);
- const XYZTagData *wXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::wtpt]);
- if (rXyz->type != quint32(Tag::XYZ_) || gXyz->type != quint32(Tag::XYZ_) ||
- wXyz->type != quint32(Tag::XYZ_) || wXyz->type != quint32(Tag::XYZ_)) {
- qCWarning(lcIcc) << "fromIccProfile: Bad XYZ data type";
- return false;
- }
QColorSpacePrivate *colorspaceDPtr = QColorSpacePrivate::getWritable(*colorSpace);
- colorspaceDPtr->toXyz.r = fromXyzData(rXyz);
- colorspaceDPtr->toXyz.g = fromXyzData(gXyz);
- colorspaceDPtr->toXyz.b = fromXyzData(bXyz);
- QColorVector whitePoint = fromXyzData(wXyz);
- colorspaceDPtr->whitePoint = whitePoint;
+ // Parse XYZ tags
+ if (!parseXyzData(data, tagIndex[Tag::rXYZ], colorspaceDPtr->toXyz.r))
+ return false;
+ if (!parseXyzData(data, tagIndex[Tag::gXYZ], colorspaceDPtr->toXyz.g))
+ return false;
+ if (!parseXyzData(data, tagIndex[Tag::bXYZ], colorspaceDPtr->toXyz.b))
+ return false;
+ if (!parseXyzData(data, tagIndex[Tag::wtpt], colorspaceDPtr->whitePoint))
+ return false;
colorspaceDPtr->gamut = QColorSpace::Gamut::Custom;
if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) {
@@ -600,29 +632,35 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
colorspaceDPtr->setToXyzMatrix();
// Parse TRC tags
- const GenericTagData *rTrc;
- const GenericTagData *gTrc;
- const GenericTagData *bTrc;
+ TagEntry rTrc;
+ TagEntry gTrc;
+ TagEntry bTrc;
if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) {
// Apple extension for parametric version of TRCs in ICCv2:
- rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aarg]);
- gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aagg]);
- bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aabg]);
+ rTrc = tagIndex[Tag::aarg];
+ gTrc = tagIndex[Tag::aagg];
+ bTrc = tagIndex[Tag::aabg];
} else {
- rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::rTRC]);
- gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::gTRC]);
- bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::bTRC]);
+ rTrc = tagIndex[Tag::rTRC];
+ gTrc = tagIndex[Tag::gTRC];
+ bTrc = tagIndex[Tag::bTRC];
}
QColorTrc rCurve;
QColorTrc gCurve;
QColorTrc bCurve;
- if (!parseTRC(rTrc, rCurve))
+ if (!parseTRC(data, rTrc, rCurve)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid rTRC";
return false;
- if (!parseTRC(gTrc, gCurve))
+ }
+ if (!parseTRC(data, gTrc, gCurve)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid gTRC";
return false;
- if (!parseTRC(bTrc, bCurve))
+ }
+ if (!parseTRC(data, bTrc, bCurve)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid bTRC";
return false;
+ }
if (rCurve == gCurve && gCurve == bCurve && rCurve.m_type == QColorTrc::Type::Function) {
if (rCurve.m_fun.isLinear()) {
qCDebug(lcIcc) << "fromIccProfile: Linear gamma detected";
diff --git a/tests/libfuzzer/gui/iccparser/iccparser.pro b/tests/libfuzzer/gui/iccparser/iccparser.pro
new file mode 100644
index 0000000000..bf4037eae7
--- /dev/null
+++ b/tests/libfuzzer/gui/iccparser/iccparser.pro
@@ -0,0 +1,3 @@
+QT += gui
+SOURCES += main.cpp
+LIBS += -fsanitize=fuzzer
diff --git a/tests/libfuzzer/gui/iccparser/main.cpp b/tests/libfuzzer/gui/iccparser/main.cpp
new file mode 100644
index 0000000000..ba4f70ef3b
--- /dev/null
+++ b/tests/libfuzzer/gui/iccparser/main.cpp
@@ -0,0 +1,37 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QColorSpace>
+
+extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) {
+ static int c = 0;
+ static QGuiApplication a(c, nullptr);
+ QColorSpace cs = QColorSpace::fromIccProfile(QByteArray(data, size));
+ return 0;
+}