aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/util/qquicksvgparser.cpp46
-rw-r--r--src/quick/util/qquicksvgparser_p.h2
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml43
-rw-r--r--tests/auto/quick/qquickpath/tst_qquickpath.cpp44
4 files changed, 113 insertions, 22 deletions
diff --git a/src/quick/util/qquicksvgparser.cpp b/src/quick/util/qquicksvgparser.cpp
index 59a976b65a..84efe061ec 100644
--- a/src/quick/util/qquicksvgparser.cpp
+++ b/src/quick/util/qquicksvgparser.cpp
@@ -19,7 +19,7 @@ static inline bool isDigit(ushort ch)
return ((ch >> 4) == 3) && (magic >> (ch & 15));
}
-static qreal toDouble(const QChar *&str)
+static qreal toDouble(const QChar *&str, const QChar *end)
{
const int maxLen = 255;//technically doubles can go til 308+ but whatever
char temp[maxLen+1];
@@ -31,28 +31,28 @@ static qreal toDouble(const QChar *&str)
} else if (*str == QLatin1Char('+')) {
++str;
}
- while (isDigit(str->unicode()) && pos < maxLen) {
+ while (str != end && isDigit(str->unicode()) && pos < maxLen) {
temp[pos++] = str->toLatin1();
++str;
}
- if (*str == QLatin1Char('.') && pos < maxLen) {
+ if (str != end && *str == QLatin1Char('.') && pos < maxLen) {
temp[pos++] = '.';
++str;
}
- while (isDigit(str->unicode()) && pos < maxLen) {
+ while (str != end && isDigit(str->unicode()) && pos < maxLen) {
temp[pos++] = str->toLatin1();
++str;
}
bool exponent = false;
- if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
+ if (str != end && (*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
exponent = true;
temp[pos++] = 'e';
++str;
- if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
+ if (str != end && (*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
temp[pos++] = str->toLatin1();
++str;
}
- while (isDigit(str->unicode()) && pos < maxLen) {
+ while (str != end && isDigit(str->unicode()) && pos < maxLen) {
temp[pos++] = str->toLatin1();
++str;
}
@@ -96,24 +96,28 @@ static qreal toDouble(const QChar *&str)
return val;
}
-static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
+static inline void parseNumbersArray(const QChar *&str, const QChar *end, QVarLengthArray<qreal, 8> &points)
{
- while (str->isSpace())
- ++str;
- while (isDigit(str->unicode()) ||
+ auto eatWhitespace = [&]{
+ while (str != end && str->isSpace())
+ ++str;
+ return str != end;
+ };
+ if (!eatWhitespace())
+ return;
+ while (str != end && (isDigit(str->unicode()) ||
*str == QLatin1Char('-') || *str == QLatin1Char('+') ||
- *str == QLatin1Char('.')) {
+ *str == QLatin1Char('.'))) {
- points.append(toDouble(str));
+ points.append(toDouble(str, end));
- while (str->isSpace())
- ++str;
+ if (!eatWhitespace())
+ return;
if (*str == QLatin1Char(','))
++str;
- //eat the rest of space
- while (str->isSpace())
- ++str;
+ if (!eatWhitespace())
+ return;
}
}
@@ -253,12 +257,12 @@ bool QQuickSvgParser::parsePathDataFast(const QString &dataStr, QPainterPath &pa
const QChar *end = str + dataStr.size();
while (str != end) {
- while (str->isSpace())
- ++str;
QChar pathElem = *str;
++str;
+ if (pathElem.isSpace())
+ continue;
QVarLengthArray<qreal, 8> arg;
- parseNumbersArray(str, arg);
+ parseNumbersArray(str, end, arg);
if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
arg.append(0);//dummy
const qreal *num = arg.constData();
diff --git a/src/quick/util/qquicksvgparser_p.h b/src/quick/util/qquicksvgparser_p.h
index 3af0d182ae..4e358d9bee 100644
--- a/src/quick/util/qquicksvgparser_p.h
+++ b/src/quick/util/qquicksvgparser_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
namespace QQuickSvgParser
{
- bool parsePathDataFast(const QString &dataStr, QPainterPath &path);
+ Q_QUICK_AUTOTEST_EXPORT bool parsePathDataFast(const QString &dataStr, QPainterPath &path);
Q_QUICK_EXPORT void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation,
int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx,
qreal cury);
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml b/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
index 011fbd497b..09a6521ff6 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
@@ -56,4 +56,47 @@ CanvasTestCase {
}
}
}
+
+ // The test only verifies that assigning malformed strings (esp those ending
+ // with whitespace) to ctx.path does not crash the parser.
+ function test_svgpath_malformed_data() {
+ return [
+ { tag: "empty", path: "" },
+ { tag: "single space", path: " " },
+ { tag: "multiple spaces", path: " " },
+ { tag: "abs and newlines", path: "\t\n "},
+ { tag: "command then trailing whitespace", path: "M0 0 "},
+ { tag: "trailing whitespace after args", path: "M0 0 L10 10 \n "},
+ { tag: "whitespace after command to end", path: "L \t"},
+ { tag: "whitespace between command and numbers", path: "M 10 20 "},
+ { tag: "trailing comma to end", path: "M0 0,"},
+ { tag: "comma then whitespace to end", path: "M0 0, \n"},
+ { tag: "comma between numbers then end", path: "L10,10, "},
+ { tag: "integer to end", path: "M0 0 L10 20"},
+ { tag: "decimal to end", path: "M0 0 L1.5 2.5"},
+ { tag: "trailing dot to end", path: "M0 0 L1. 2."},
+ { tag: "exponent to end", path: "M0 0 L1e2 3e2"},
+ { tag: "signed exponent to end", path: "M0 0 L1e-2 3e-2"},
+ { tag: "bare exponent letter to end", path: "M0 0 L1e"},
+ { tag: "sign only token to end", path: "M0 0 L-"},
+ { tag: "dot only token to end", path: "M0 0 L."}
+ ]
+ }
+
+ function test_svgpath_malformed(data) {
+ var canvas = Qt.createQmlObject(`
+ import QtQuick
+ Canvas {
+ height: 100
+ width:100
+ renderTarget:Canvas.Image
+ }
+ `, testCase, "testCanvas");
+ tryVerify(function() { return canvas.available; });
+ var ctx = canvas.getContext('2d');
+ ctx.beginPath();
+ ctx.path = data.path;
+ ctx.fill();
+ verify(true); // reached here without crashing
+ }
}
diff --git a/tests/auto/quick/qquickpath/tst_qquickpath.cpp b/tests/auto/quick/qquickpath/tst_qquickpath.cpp
index 531a8a898b..89da273993 100644
--- a/tests/auto/quick/qquickpath/tst_qquickpath.cpp
+++ b/tests/auto/quick/qquickpath/tst_qquickpath.cpp
@@ -7,6 +7,7 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQuick/private/qquickpath_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
+#include <QtQuick/private/qquicksvgparser_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -22,6 +23,8 @@ private slots:
void catmullRomCurve();
void closedCatmullRomCurve();
void svg();
+ void svgMalformed_data();
+ void svgMalformed();
void line();
void rectangle_data();
void rectangle();
@@ -282,6 +285,47 @@ void tst_QuickPath::svg()
svg(QSizeF(5,3));
}
+void tst_QuickPath::svgMalformed_data()
+{
+ QTest::addColumn<QString>("svgPath");
+
+#ifdef QT_BUILD_INTERNAL
+ // Malformed path strings must not crash the parser.
+ QTest::newRow("empty") << QString();
+ QTest::newRow("single space") << QStringLiteral(" ");
+ QTest::newRow("multiple spaces") << QStringLiteral(" ");
+ QTest::newRow("tabs and newlines") << QStringLiteral("\t\n ");
+ QTest::newRow("command then trailing whitespace") << QStringLiteral("M0 0 ");
+ QTest::newRow("trailing whitespace after args") << QStringLiteral("M0 0 L10 10 \n ");
+ QTest::newRow("whitespace after command to end") << QStringLiteral("L \t");
+ QTest::newRow("whitespace between command and numbers") << QStringLiteral("M 10 20 ");
+ QTest::newRow("trailing comma to end") << QStringLiteral("M0 0,");
+ QTest::newRow("comma then whitespace to end") << QStringLiteral("M0 0, \n");
+ QTest::newRow("comma between numbers then end") << QStringLiteral("L10,10, ");
+ QTest::newRow("integer to end") << QStringLiteral("M0 0 L10 20");
+ QTest::newRow("decimal to end") << QStringLiteral("M0 0 L1.5 2.5");
+ QTest::newRow("trailing dot to end") << QStringLiteral("M0 0 L1. 2.");
+ QTest::newRow("exponent to end") << QStringLiteral("M0 0 L1e2 3e2");
+ QTest::newRow("signed exponent to end") << QStringLiteral("M0 0 L1e-2 3e-2");
+ QTest::newRow("bare exponent letter to end") << QStringLiteral("M0 0 L1e");
+ QTest::newRow("sign only token to end") << QStringLiteral("M0 0 L-");
+ QTest::newRow("dot only token to end") << QStringLiteral("M0 0 L.");
+#else
+ QSKIP("This test relies on private APIs that are only exported in developer-builds");
+#endif
+}
+
+void tst_QuickPath::svgMalformed()
+{
+#ifdef QT_BUILD_INTERNAL
+ QFETCH(QString, svgPath);
+ QPainterPath path;
+ QQuickSvgParser::parsePathDataFast(svgPath, path);
+#else
+ QSKIP("This test relies on private APIs that are only exported in developer-builds");
+#endif
+}
+
void tst_QuickPath::line(QSizeF scale)
{
QQmlEngine engine;