aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2020-01-06 13:55:53 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2020-01-07 10:20:30 +0100
commite72b032cc1c5a8a07a99fc6522a692c36f369abc (patch)
tree710d5ddbb4f3fe8a69064b67bde9da9cf4d16b58 /tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
parent09c4ec32028aff82dcec6f1c74d721f7c6279738 (diff)
QV4MM: Fix crash caused by MarkStack overflow
MemoryManager::collectFromJSStack did push to the mark stack without checking if there is actually still space available. To fix this, we now drain the stack once we hit the limit. The test case is a slightly modified version compared to the reported one, removing one loop. This was required as our regular expression does not throw an exception when there are too many capture groups. However, to trigger the bug, looping was not actually necessary. Change-Id: I4d00865f25a989c380f4f5b221f4068c80b71d2b Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp')
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp49
1 files changed, 49 insertions, 0 deletions
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 70885eb69c..370ef5f065 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -381,6 +381,8 @@ private slots:
void semicolonAfterProperty();
void hugeStack();
+ void gcCrashRegressionTest();
+
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
static void verifyContextLifetime(QQmlContextData *ctxt);
@@ -9211,6 +9213,53 @@ void tst_qqmlecmascript::hugeStack()
QCOMPARE(qvariant_cast<QJSValue>(huge).property(QLatin1String("length")).toInt(), 33059);
}
+void tst_qqmlecmascript::gcCrashRegressionTest()
+{
+ const QString qmljs = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs";
+ if (!QFile::exists(qmljs)) {
+ QSKIP("Tets requires qmljs");
+ }
+ QProcess process;
+
+ QTemporaryFile infile;
+ QVERIFY(infile.open());
+ infile.write(R"js(
+ function i_want_to_break_free() {
+ var n = 400;
+ var m = 10;
+ var regex = new RegExp("(ab)".repeat(n), "g"); // g flag to trigger the vulnerable path
+ var part = "ab".repeat(n); // matches have to be at least size 2 to prevent interning
+ var s = (part + "|").repeat(m);
+ var cnt = 0;
+ var ary = [];
+ s.replace(regex, function() {
+ for (var i = 1; i < arguments.length-2; ++i) {
+ if (typeof arguments[i] !== 'string') {
+ i_am_free = arguments[i];
+ throw "success";
+ }
+ ary[cnt++] = arguments[i]; // root everything to force GC
+ }
+ return "x";
+ });
+ }
+ try { i_want_to_break_free(); } catch (e) {console.log("hi") }
+ console.log(typeof(i_am_free)); // will print "object"
+ )js");
+ infile.close();
+
+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
+ environment.insert("QV4_GC_MAX_STACK_SIZE", "32768");
+
+ process.setProcessEnvironment(environment);
+ process.start(qmljs, QStringList({infile.fileName()}));
+ QVERIFY(process.waitForStarted());
+ const qint64 pid = process.processId();
+ QVERIFY(pid != 0);
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(process.exitCode(), 0);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"