aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4engine_p.h
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@theqtcompany.com>2016-04-25 15:01:04 +0200
committerErik Verbruggen <erik.verbruggen@qt.io>2016-05-11 11:11:34 +0000
commit74f75a3a120b07bbfe6904512b338db8850874e4 (patch)
treeb3c1047484cb49d98947056a2d8662e7542d2463 /src/qml/jsruntime/qv4engine_p.h
parent78ad5069d4925445a9d39579c1f73c7911fc3582 (diff)
V4: Limit call depth by count, not by checking the native stack.
Getting the native stack size can be really expensive. For example, on Linux/x86_64 (Ubuntu 15.04), it is at least 200,000 instructions for a single-threaded application. With more threads (like qmlscene) it typically ends up around 1M(!) instructions. Worse, it is called twice in the ExecutionEngine constructor. So, now we limit the depth of JavaScript calls to a fixed number, 1234 by default. This can be changed by setting the environment variable QV4_MAX_CALL_DEPTH to the desired depth. Change-Id: Ic13c8efb2769e64fbc73deee6f6fa39d7c0b7af5 Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
Diffstat (limited to 'src/qml/jsruntime/qv4engine_p.h')
-rw-r--r--src/qml/jsruntime/qv4engine_p.h33
1 files changed, 24 insertions, 9 deletions
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 4640f3f4cc..7fc880023f 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -75,15 +75,11 @@ namespace CompiledData {
struct CompilationUnit;
}
-#define CHECK_STACK_LIMITS(v4) \
- if ((v4->jsStackTop <= v4->jsStackLimit) && (reinterpret_cast<quintptr>(&v4) >= v4->cStackLimit || v4->recheckCStackLimits())) {} \
- else \
- return v4->throwRangeError(QStringLiteral("Maximum call stack size exceeded."))
-
-
struct Q_QML_EXPORT ExecutionEngine
{
private:
+ static qint32 maxCallDepth;
+
friend struct ExecutionContextSaver;
friend struct ExecutionContext;
friend struct Heap::ExecutionContext;
@@ -92,6 +88,7 @@ public:
Value *jsStackTop;
quint32 hasException;
+ qint32 callDepth;
MemoryManager *memoryManager;
ExecutableAllocator *executableAllocator;
@@ -101,7 +98,6 @@ public:
ExecutionContext *currentContext;
Value *jsStackLimit;
- quintptr cStackLimit;
WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine.
@@ -431,8 +427,6 @@ public:
InternalClass *newClass(const InternalClass &other);
- bool recheckCStackLimits();
-
// Exception handling
Value *exceptionValue;
StackTrace exceptionStackTrace;
@@ -465,6 +459,8 @@ public:
QV4::ReturnedValue metaTypeToJS(int type, const void *data);
void assertObjectBelongsToEngine(const Heap::Base &baseObject);
+
+ bool checkStackLimits(ReturnedValue &exception);
};
inline void ExecutionEngine::pushContext(Heap::ExecutionContext *context)
@@ -517,7 +513,26 @@ inline void Value::mark(ExecutionEngine *e)
o->mark(e);
}
+#define CHECK_STACK_LIMITS(v4) { ReturnedValue e; if ((v4)->checkStackLimits(e)) return e; } \
+ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4);
+
+struct ExecutionEngineCallDepthRecorder
+{
+ ExecutionEngine *ee;
+ ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ++ee->callDepth; }
+ ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; }
+};
+
+inline bool ExecutionEngine::checkStackLimits(ReturnedValue &exception)
+{
+ if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) {
+ exception = throwRangeError(QStringLiteral("Maximum call stack size exceeded."));
+ return true;
+ }
+
+ return false;
+}
} // namespace QV4