diff options
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 5 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4value_p.h | 2 | ||||
-rw-r--r-- | src/qml/memory/qv4mm.cpp | 2 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 49 |
4 files changed, 57 insertions, 1 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 23582e0ccb..c81661033a 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1263,8 +1263,11 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) void ExecutionEngine::markObjects(MarkStack *markStack) { for (int i = 0; i < NClasses; ++i) - if (classes[i]) + if (classes[i]) { classes[i]->mark(markStack); + if (markStack->top >= markStack->limit) + markStack->drain(); + } markStack->drain(); identifierTable->markObjects(markStack); diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 4e901721cb..ae12033eb4 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -555,6 +555,8 @@ struct ValueArray { } else { while (v < end) { v->mark(markStack); + if (markStack->top >= markStack->limit) + markStack->drain(); ++v; } } diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index c7f52eac6b..a54cbed2c9 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -1219,6 +1219,8 @@ void MemoryManager::collectFromJSStack(MarkStack *markStack) const Q_ASSERT(m->inUse()); // Skip pointers to already freed objects, they are bogus as well m->mark(markStack); + if (markStack->top >= markStack->limit) + markStack->drain(); } ++v; } 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" |