aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler/qv4compiler.cpp
diff options
context:
space:
mode:
authorLuca Di Sera <luca.disera@qt.io>2025-03-21 14:59:29 +0100
committerLuca Di Sera <luca.disera@qt.io>2025-04-09 13:36:21 +0200
commitf6f20242e4ac2fba54ad8d8dff0d9540290b441a (patch)
tree2834cbc024d15a6049cead218b60e017ccc37543 /src/qml/compiler/qv4compiler.cpp
parent9ac0e70cdbf99e06761b6edb28937fded8791bdb (diff)
Avoid incorrect access to arguments in signal bindings to arrow functions
Signal handler in QML can be bound to a Javascript expression. For example: ``` onHello: console.log(10) ``` Where "onHello" is a Signal Handler. When this is the case a certain amount of code will be generated for the Signal Handler, performing some setup routines and executing the provided expression, generally in a way that is somewhat equivalent to the same expression being executed as the body of a function call. It is possible to bind a Signal Handler to a anonymous function, say: ``` onHello: function () { ... } ``` Or: ``` onHello: () => { ... } ``` When this is the case, if the usual process was followed, executing the Signal Handler would simply produce an anonymous function, but would never actually call it. Instead, when such a literal function expression is bound to a Signal Handler, it is treated specially. In particular, while the code generation will generally behave similarly, on execution of the Signal Handler, the expression will be directly called while the "wrapping" function that was generated around the expression is ignored. While this works correctly in many cases, it can misbehave on certain occasions. For example, an arrow function doesn't generally set up its own context for a call, instead borrowing from the outer context at the time of creation. When making such a call to an arrow function, in a QML context, it is then possible to execute code in an environment that wasn't properly set up for it. In particular, if the body of the arrow function introduces a `LoadLocal` instruction, which assumes, when interpreted, an available `CallContext`, it is possible to try and access memory that was never correctly set up. It is, for example, possible to do so by usage of `arguments`, a special reference that allows access to a pack of arguments in all non-arrow functions, which will produce a `LoadLocal` instruction when accessed, for example: ``` onHello: () => { console.log(arguments) } ``` Internally, when generating code for a JavaScript program or expression, an analysis is performed to understand whether the special "arguments" reference is being used. When that is the case and the context in which the reference is used is under a context where the reference can exist, a local variable for "arguments" is injected and a "LoadLocal" instruction is later generated to access it. The analysis considers a binding scope as being able to provide the "arguments" reference, something that is generally true due to the "wrapping" that is performed when generating an expression for the binding, and which needs to be guaranteed in certain cases. While this breaks down in the face of directly calling the "inner" function in a Signal Handler binding, such that the analysis might itself not be thorough enough in those cases, at the time the analysis is performed we cannot currently know whether the binding we are dealing with is that of a Signal Handler. Furthermore, we don't always bypass the "wrapping" function in a Signal Handler, as there are other cases where this can create issues, for example the usage of "this" in an arrow function. When this is the case, instead of directly calling the "inner" function, the normal "wrapping" function is called to perform setup routines and obtain the function itself by executing the bound expression, subsequently calling the function obtained in this way. To avoid the issue with the usage of "arguments", we re-use the same methodology, ensuring that when the special "arguments" object is referenced we never bypass the setup provided by the binding expression. This ensures that the arrow function will be created in a context where `arguments` is present and where the necessary setup for the call is performed. The special object will be always be empty in that context, which aligns the behavior to that of non-signal bindings. To do so, an additional case was added to the code in `writeFunction` that sets up a binding expression to later skip to its inner function. A few test cases were added to inspect usages of the "arguments" special reference under binding contexts. Fixes: QTBUG-134215 Change-Id: Ib7fdfee91709358f2ee465b1926809ca4617d6f6 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/compiler/qv4compiler.cpp')
-rw-r--r--src/qml/compiler/qv4compiler.cpp1
1 files changed, 1 insertions, 0 deletions
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index ad1390a650..f4b416d209 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -441,6 +441,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->flags |= CompiledData::Function::IsClosureWrapper;
if (!irFunction->returnsClosure
+ || (irFunction->usesArgumentsObject == Context::ArgumentsObjectUsed)
|| irFunction->innerFunctionAccessesThis
|| irFunction->innerFunctionAccessesNewTarget) {
// If the inner function does things with this and new.target we need to do some work in