aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2017-10-17 15:14:59 +0200
committerLars Knoll <lars.knoll@qt.io>2017-10-23 06:27:33 +0000
commitbc2427ce32efbfa3759e2658ba53289428527071 (patch)
treed4b454d585c1f66cb87cec8b3449a084a7c692b2
parentaceb0d0cd2da89aebbf17729869b9e977290c826 (diff)
Never truncate the JS stack
Truncating it can lead to all sorts of crazy side effects, especially as we'd be extending it again when leaving the function. When that happens already freed JS objects could suddenly become visible to the GC again. Fix this by copying the CallData to set up a new stack frame. This is not yet ideal, as we're copying too much data, but that can be fixed separately. Change-Id: I02a39ce479475bae326f9eddfe6654fbcf8e6d35 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp23
-rw-r--r--src/qml/memory/qv4mm.cpp4
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp12
4 files changed, 34 insertions, 7 deletions
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 2e97d8074a..97b0c5259f 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1028,6 +1028,7 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, C
ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
{
+ Q_ASSERT(engine->jsStackTop >= callData->args + callData->argc());
if (!callData->thisObject.isObject()) {
Q_ASSERT(!callData->thisObject.isEmpty());
if (callData->thisObject.isNullOrUndefined()) {
@@ -1058,6 +1059,7 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameInde
ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData)
{
+ Q_ASSERT(engine->jsStackTop >= callData->args + callData->argc());
Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
callData->function = l->getter(l, engine, callData->thisObject);
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index f27a7d452c..4abd3582dd 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -509,6 +509,15 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
Value *jsStackTop = engine->jsStackTop;
+ Q_ASSERT(engine->jsStackTop >= callData->args + callData->argc() - 1);
+ Value *stack = engine->jsStackTop;
+ engine->jsStackTop += sizeof(CallData)/sizeof(Value) - 1 + qMax(callData->argc(), int(function->compiledFunction->nRegisters));
+ memcpy(stack, callData, sizeof(CallData) - sizeof(Value) + callData->argc()*sizeof(Value));
+ // clear out remaining arguments and local registers
+ callData = reinterpret_cast<CallData *>(stack);
+ for (Value *v = callData->args + callData->argc(); v < engine->jsStackTop; ++v)
+ *v = Encode::undefined();
+
CppStackFrame frame;
frame.parent = engine->currentStackFrame;
frame.v4Function = function;
@@ -516,12 +525,6 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
frame.jsFrame = callData;
engine->currentStackFrame = &frame;
- engine->jsStackTop = reinterpret_cast<QV4::Value *>(callData) + function->compiledFunction->nRegisters + 1;
- // clear out remaining arguments and local registers
- for (Value *v = callData->args + callData->argc(); v < jsStackTop; ++v)
- *v = Encode::undefined();
-
- QV4::Value *stack = reinterpret_cast<QV4::Value *>(callData);
const uchar *exceptionHandler = 0;
QV4::Value &accumulator = frame.jsFrame->accumulator;
@@ -741,6 +744,7 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
STORE_IP();
STORE_ACC();
QV4::CallData *cData = reinterpret_cast<QV4::CallData *>(stack + callData);
+ Q_ASSERT(cData->args + cData->argc() <= engine->jsStackTop);
acc = Runtime::method_callValue(engine, accumulator, cData);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallValue)
@@ -748,6 +752,7 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
MOTH_BEGIN_INSTR(CallProperty)
STORE_IP();
QV4::CallData *cData = reinterpret_cast<QV4::CallData *>(stack + callData);
+ Q_ASSERT(cData->args + cData->argc() <= engine->jsStackTop);
cData->thisObject = STACK_VALUE(base);
acc = Runtime::method_callProperty(engine, name, cData);
CHECK_EXCEPTION;
@@ -756,6 +761,7 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
MOTH_BEGIN_INSTR(CallPropertyLookup)
STORE_IP();
QV4::CallData *cData = reinterpret_cast<QV4::CallData *>(stack + callData);
+ Q_ASSERT(cData->args + cData->argc() <= engine->jsStackTop);
cData->thisObject = STACK_VALUE(base);
acc = Runtime::method_callPropertyLookup(engine, lookupIndex, cData);
CHECK_EXCEPTION;
@@ -764,6 +770,7 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
MOTH_BEGIN_INSTR(CallElement)
STORE_IP();
QV4::CallData *cData = reinterpret_cast<QV4::CallData *>(stack + callData);
+ Q_ASSERT(cData->args + cData->argc() <= engine->jsStackTop);
cData->thisObject = STACK_VALUE(base);
acc = Runtime::method_callElement(engine, STACK_VALUE(index), cData);
CHECK_EXCEPTION;
@@ -772,6 +779,7 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
MOTH_BEGIN_INSTR(CallName)
STORE_IP();
QV4::CallData *cData = reinterpret_cast<QV4::CallData *>(stack + callData);
+ Q_ASSERT(cData->args + cData->argc() <= engine->jsStackTop);
acc = Runtime::method_callName(engine, name, cData);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallName)
@@ -779,6 +787,7 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
STORE_IP();
QV4::CallData *cData = reinterpret_cast<QV4::CallData *>(stack + callData);
+ Q_ASSERT(cData->args + cData->argc() <= engine->jsStackTop);
acc = Runtime::method_callPossiblyDirectEval(engine, cData);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallPossiblyDirectEval)
@@ -786,6 +795,7 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
MOTH_BEGIN_INSTR(CallGlobalLookup)
STORE_IP();
QV4::CallData *cData = reinterpret_cast<QV4::CallData *>(stack + callData);
+ Q_ASSERT(cData->args + cData->argc() <= engine->jsStackTop);
acc = Runtime::method_callGlobalLookup(engine, index, cData);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallGlobalLookup)
@@ -934,6 +944,7 @@ QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function)
MOTH_BEGIN_INSTR(Construct)
STORE_IP();
QV4::CallData *cData = reinterpret_cast<QV4::CallData *>(stack + callData);
+ Q_ASSERT(cData->args + cData->argc() <= engine->jsStackTop);
acc = Runtime::method_construct(engine, STACK_VALUE(func), cData);
CHECK_EXCEPTION;
MOTH_END_INSTR(Construct)
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index b9bc2c34a3..21c95bbe6e 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -1256,9 +1256,11 @@ void MemoryManager::collectFromJSStack(MarkStack *markStack) const
Value *top = engine->jsStackTop;
while (v < top) {
Managed *m = v->managed();
- if (m && m->inUse())
+ if (m) {
+ Q_ASSERT(m->inUse());
// Skip pointers to already freed objects, they are bogus as well
m->mark(markStack);
+ }
++v;
}
}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 899f14b51d..faab61cd3d 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -344,6 +344,7 @@ private slots:
void singleBlockLoops();
void qtbug_60547();
void delayLoadingArgs();
+ void manyArguments();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -8375,6 +8376,17 @@ void tst_qqmlecmascript::delayLoadingArgs()
QCOMPARE(ret.toInt(), 42); // esp. not 44.
}
+void tst_qqmlecmascript::manyArguments()
+{
+ const char *testCase =
+ "function x() { var sum; for (var i = 0; i < arguments.length; ++i) sum += arguments[i][0]; }"
+ "x([0],[1],[2],[3],[4],[5],[6],[7],[8],[9], [0],[1],[2],[3],[4],[5],[6],[7],[8],[9], [0],[1],[2],[3],[4],[5],[6],[7],[8],[9])";
+
+ QJSEngine engine;
+ engine.evaluate(testCase);
+}
+
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"