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>
This commit is contained in:
Lars Knoll 2017-10-17 15:14:59 +02:00
parent aceb0d0cd2
commit bc2427ce32
4 changed files with 34 additions and 7 deletions

View File

@ -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);

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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"