aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2022-11-30 15:29:13 +0100
committerSami Shalayel <sami.shalayel@qt.io>2022-12-02 18:53:12 +0000
commitcda417cf03694256a84b4abe77de0f5f49ebdf32 (patch)
tree20a4627920c717b6a9d3e845080f488fb3e58b5c
parent3f4856dbca0114b8f354df482fec437dc6b04d23 (diff)
qv4qobjectwrapper: return false on failed argument conversion
It was possible to call c++-methods (either invokable or as slot) with wrong arguments, which caused a crash. The reason was that CallMethod(...) converted something to a QObject without checking if it was an actual QObject. The wrongly typed argument would end up reinterpret_cast'ed into another type for the call, which leads to segmentation fault when accessing the argument in the function. Added a test where an int tried to be reinterpret-cast'ed into a QFont. Pick-to: 6.4 6.2 Fixes: QTBUG-108994 Change-Id: I8c45c9124411ad3fd100faed0b03390843f7d034 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/methodTypeMismatch.qml25
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h25
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp77
4 files changed, 128 insertions, 1 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index e249c8a6d8..bf6bc630f8 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -2176,7 +2176,7 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
}
const QQmlMetaObject mo = QQmlMetaType::rawMetaObjectForType(metaType);
- if (!mo.isNull()) {
+ if (!mo.isNull() && v.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
QObject *obj = QQmlMetaType::toQObject(v);
if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) {
diff --git a/tests/auto/qml/qqmlecmascript/data/methodTypeMismatch.qml b/tests/auto/qml/qqmlecmascript/data/methodTypeMismatch.qml
new file mode 100644
index 0000000000..fdf5f4ea11
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/methodTypeMismatch.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+Item {
+ id: self
+ property font myFont
+ property int notMyFont
+
+ property var object
+
+ function callWithFont() {
+ object.method_gadget(myFont) // should be fine
+ }
+ function callWithInt() {
+ object.method_gadget(123)
+ }
+ function callWithInt2() {
+ object.method_gadget(notMyFont)
+ }
+ function callWithNull() {
+ object.method_gadget(null)
+ }
+ function callWithAllowedNull() {
+ object.method_qobject(null)
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index da882d7840..3aa9661380 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -940,6 +940,25 @@ public:
Q_INVOKABLE void method_overload3(char c, QUrl a, QDateTime b) { invoke(38); m_actuals << c << a << b; }
Q_INVOKABLE void method_overload3(char c, QJsonValue a, QTime b) { invoke(39); m_actuals << c << a << b; }
+ Q_PROPERTY(QFont someFont READ someFont WRITE setSomeFont NOTIFY someFontChanged);
+ QFont someFont() { return m_someFont; }
+ void setSomeFont(const QFont &f)
+ {
+ if (f == m_someFont)
+ return;
+ m_someFont = f;
+ emit someFontChanged();
+ }
+ Q_INVOKABLE void method_gadget(QFont f)
+ {
+ invoke(40);
+ m_actuals << f;
+ }
+ Q_INVOKABLE void method_qobject(QObject *o)
+ {
+ invoke(41);
+ m_actuals << QVariant::fromValue(o);
+ }
private:
friend class MyInvokableBaseObject;
@@ -947,6 +966,12 @@ private:
int m_invoked;
bool m_invokedError;
QVariantList m_actuals;
+
+ QFont m_someFont;
+
+public:
+Q_SIGNALS:
+ void someFontChanged();
};
MyInvokableBaseObject::~MyInvokableBaseObject() {}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index f591a87dcb..2e4bf9dc19 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -411,6 +411,7 @@ private slots:
void functionAsDefaultArgument();
void internalClassParentGc();
+ void methodTypeMismatch();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -3482,6 +3483,19 @@ void tst_qqmlecmascript::callQtInvokables()
==> overload 38 should win
*/
QCOMPARE(o->invoked(), 38);
+
+ o->reset();
+ QVERIFY(EVALUATE_VALUE("object.method_gadget(object.someFont)",
+ QV4::Primitive::undefinedValue()));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 40);
+ QCOMPARE(o->actuals(), QVariantList() << QVariant(o->someFont()));
+
+ o->reset();
+ QVERIFY(EVALUATE_ERROR("object.method_gadget(123)"));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), -1);
+ QCOMPARE(o->actuals(), QVariantList());
}
void tst_qqmlecmascript::resolveClashingProperties()
@@ -10259,6 +10273,69 @@ void tst_qqmlecmascript::internalClassParentGc()
QCOMPARE(root->objectName(), "3");
}
+void tst_qqmlecmascript::methodTypeMismatch()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("methodTypeMismatch.qml"));
+
+ QScopedPointer<MyInvokableObject> object(new MyInvokableObject());
+
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY2(o, qPrintable(component.errorString()));
+ o->setProperty("object", QVariant::fromValue(object.get()));
+
+ auto mo = o->metaObject();
+ QVERIFY(mo);
+
+ auto method = mo->method(mo->indexOfMethod("callWithFont()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals(), QVariantList() << QVariant(object->someFont()));
+
+ QRegularExpression argumentConversionErrorMatcher("Could not convert argument 0");
+ QRegularExpression argumentConversionErrorMatcher2(".*/methodTypeMismatch.qml");
+ QRegularExpression typeErrorMatcher(
+ ".*/methodTypeMismatch\\.qml:..: TypeError: Passing incompatible arguments to C\\+\\+ "
+ "functions from JavaScript is not allowed.");
+
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher);
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher2);
+ QTest::ignoreMessage(QtWarningMsg, typeErrorMatcher);
+ object->reset();
+ method = mo->method(mo->indexOfMethod("callWithInt()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals().size(),
+ 0); // actuals() should not contain reinterpret_cast<QFont>(123) !!!
+
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher);
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher2);
+ QTest::ignoreMessage(QtWarningMsg, typeErrorMatcher);
+ object->reset();
+ method = mo->method(mo->indexOfMethod("callWithInt2()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals().size(),
+ 0); // actuals() should not contain reinterpret_cast<QFont>(0) !!!
+
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher);
+ QTest::ignoreMessage(QtWarningMsg, argumentConversionErrorMatcher2);
+ QTest::ignoreMessage(QtWarningMsg, typeErrorMatcher);
+ object->reset();
+ method = mo->method(mo->indexOfMethod("callWithNull()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals().size(),
+ 0); // actuals() should not contain reinterpret_cast<QFont>(nullptr) !!!
+
+ // make sure that null is still accepted by functions accepting, e.g., a QObject*!
+ object->reset();
+ method = mo->method(mo->indexOfMethod("callWithAllowedNull()"));
+ QVERIFY(method.isValid());
+ QVERIFY(method.invoke(o.get()));
+ QCOMPARE(object->actuals(), QVariantList() << QVariant::fromValue((QObject *)nullptr));
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"