aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4engine.cpp5
-rw-r--r--src/qml/jsruntime/qv4value_p.h2
-rw-r--r--src/qml/memory/qv4mm.cpp2
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp49
4 files changed, 57 insertions, 1 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index f9747207db..a4831422d3 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1090,8 +1090,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 ce85e48b72..128ae3528d 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -869,6 +869,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 cac68bdcaf..342e31da1b 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -1209,6 +1209,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 dc8ef232c1..da116aaed0 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -374,6 +374,8 @@ private slots:
void getThisObject();
void semicolonAfterProperty();
+ void gcCrashRegressionTest();
+
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
static void verifyContextLifetime(QQmlContextData *ctxt);
@@ -9065,6 +9067,53 @@ void tst_qqmlecmascript::semicolonAfterProperty()
QVERIFY(!test.isNull());
}
+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"