From 88de6960747aa567c6947913f9e5715cfb972d46 Mon Sep 17 00:00:00 2001 From: Ahmad Samir Date: Thu, 25 May 2023 19:22:05 +0300 Subject: [PATCH] Moc: check sizes of specific member QLists are within INT_MAX range Parts of the public API, e.g. QMetaMethod::methodIndex and similar functions return int, and other parts of the code expect int values, at least for Qt6 this can't be changed, so use qsizetype internally and assert the values fit in an int. As pointed out in code review, not many people will build moc in debug mode, so asserts aren't that useful here. Instead print error messages and exit, like is already done in other parts of the code. Change-Id: Id305165caa996c899f30770a757098fe2f9a96f6 Reviewed-by: Thiago Macieira --- src/tools/moc/generator.cpp | 38 ++++++++++++++++++---------- src/tools/moc/moc.cpp | 49 ++++++++++++++++++++++++++++++++++--- src/tools/moc/moc.h | 2 ++ 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 7aa0628168..f3a53dfccd 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -135,7 +135,7 @@ static int aggregateParameterCount(const QList &list) { int sum = 0; for (int i = 0; i < list.size(); ++i) - sum += list.at(i).arguments.size() + 1; // +1 for return type + sum += int(list.at(i).arguments.size()) + 1; // +1 for return type return sum; } @@ -293,7 +293,13 @@ void Generator::generateCode() fprintf(out, " %4d, %4d, // classinfo\n", int(cdef->classInfoList.size()), int(cdef->classInfoList.size() ? index : 0)); index += cdef->classInfoList.size() * 2; - const qsizetype methodCount = cdef->signalList.size() + cdef->slotList.size() + cdef->methodList.size(); + qsizetype methodCount = 0; + if (qAddOverflow(cdef->signalList.size(), cdef->slotList.size(), &methodCount) + || qAddOverflow(cdef->methodList.size(), methodCount, &methodCount)) { + parser->error("internal limit exceeded: the total number of member functions" + " (including signals and slots) is too big."); + } + fprintf(out, " %4" PRIdQSIZETYPE ", %4d, // methods\n", methodCount, methodCount ? index : 0); index += methodCount * QMetaObjectPrivate::IntsPerMethod; if (cdef->revisionedMethods) @@ -305,7 +311,7 @@ void Generator::generateCode() + aggregateParameterCount(cdef->constructorList); index += totalParameterCount * 2 // types and parameter names - methodCount // return "parameters" don't have names - - cdef->constructorList.size(); // "this" parameters don't have names + - int(cdef->constructorList.size()); // "this" parameters don't have names fprintf(out, " %4d, %4d, // properties\n", int(cdef->propertyList.size()), int(cdef->propertyList.size() ? index : 0)); index += cdef->propertyList.size() * QMetaObjectPrivate::IntsPerProperty; @@ -333,8 +339,14 @@ void Generator::generateCode() // generateClassInfos(); + qsizetype propEnumCount = 0; // all property metatypes + all enum metatypes + 1 for the type of the current class itself - int initialMetaTypeOffset = cdef->propertyList.size() + cdef->enumList.size() + 1; + if (qAddOverflow(cdef->propertyList.size(), cdef->enumList.size(), &propEnumCount) + || qAddOverflow(propEnumCount, qsizetype(1), &propEnumCount) + || propEnumCount >= std::numeric_limits::max()) { + parser->error("internal limit exceeded: number of property and enum metatypes is too big."); + } + int initialMetaTypeOffset = int(propEnumCount); // // Build signals array first, otherwise the signal indices would be wrong @@ -606,7 +618,7 @@ void Generator::generateCode() // // Generate internal signal functions // - for (int signalindex = 0; signalindex < cdef->signalList.size(); ++signalindex) + for (int signalindex = 0; signalindex < int(cdef->signalList.size()); ++signalindex) generateSignal(&cdef->signalList[signalindex], signalindex); // @@ -712,7 +724,7 @@ void Generator::generateFunctions(const QList &list, const char *fu comment.append(" | MethodIsConst "); } - int argc = f.arguments.size(); + const int argc = int(f.arguments.size()); fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x, %4d /* %s */,\n", stridx(f.name), argc, paramsIndex, stridx(f.tag), flags, initialMetatypeOffset, comment.constData()); @@ -963,7 +975,7 @@ void Generator::generateMetacall() QMultiMap Generator::automaticPropertyMetaTypesHelper() { QMultiMap automaticPropertyMetaTypes; - for (int i = 0; i < cdef->propertyList.size(); ++i) { + for (int i = 0; i < int(cdef->propertyList.size()); ++i) { const QByteArray propertyType = cdef->propertyList.at(i).type; if (registerableMetaType(propertyType) && !isBuiltinType(propertyType)) automaticPropertyMetaTypes.insert(propertyType, i); @@ -1012,7 +1024,7 @@ void Generator::generateStaticMetacall() if (!cdef->constructorList.isEmpty()) { fprintf(out, " if (_c == QMetaObject::CreateInstance) {\n"); fprintf(out, " switch (_id) {\n"); - const int ctorend = cdef->constructorList.size(); + const int ctorend = int(cdef->constructorList.size()); for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) { fprintf(out, " case %d: { %s *_r = new %s(", ctorindex, cdef->classname.constData(), cdef->classname.constData()); @@ -1136,7 +1148,7 @@ void Generator::generateStaticMetacall() fprintf(out, " else if (_c == QMetaObject::IndexOfMethod) {\n"); fprintf(out, " int *result = reinterpret_cast(_a[0]);\n"); bool anythingUsed = false; - for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) { + for (int methodindex = 0; methodindex < int(cdef->signalList.size()); ++methodindex) { const FunctionDef &f = cdef->signalList.at(methodindex); if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic) continue; @@ -1234,7 +1246,7 @@ void Generator::generateStaticMetacall() if (needTempVarForGet) fprintf(out, " void *_v = _a[0];\n"); fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); if (p.read.isEmpty() && p.member.isEmpty()) continue; @@ -1275,7 +1287,7 @@ void Generator::generateStaticMetacall() setupMemberAccess(); fprintf(out, " void *_v = _a[0];\n"); fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); if (p.constant) continue; @@ -1328,7 +1340,7 @@ void Generator::generateStaticMetacall() if (needReset) { setupMemberAccess(); fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); if (p.reset.isEmpty()) continue; @@ -1349,7 +1361,7 @@ void Generator::generateStaticMetacall() if (hasBindableProperties) { setupMemberAccess(); fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); if (p.bind.isEmpty()) continue; diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index f8c36a5abd..39fc5ca466 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -318,6 +318,9 @@ void Moc::parseFunctionArguments(FunctionDef *def) def->arguments.removeLast(); def->isRawSlot = true; } + + if (Q_UNLIKELY(def->arguments.size() >= std::numeric_limits::max())) + error("number of function arguments exceeds std::numeric_limits::max()"); } bool Moc::testFunctionAttribute(FunctionDef *def) @@ -603,6 +606,42 @@ void Moc::prependNamespaces(BaseDef &def, const QList &namespaceLi } } +void Moc::checkListSizes(const ClassDef &def) +{ + if (Q_UNLIKELY(def.nonClassSignalList.size() > std::numeric_limits::max())) + error("number of signals defined in parent class(es) exceeds " + "std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.propertyList.size() > std::numeric_limits::max())) + error("number of bindable properties exceeds std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.classInfoList.size() > std::numeric_limits::max())) + error("number of times Q_CLASSINFO macro is used exceeds " + "std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.enumList.size() > std::numeric_limits::max())) + error("number of enumerations exceeds std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.superclassList.size() > std::numeric_limits::max())) + error("number of super classes exceeds std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.constructorList.size() > std::numeric_limits::max())) + error("number of constructor parameters exceeds std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.signalList.size() > std::numeric_limits::max())) + error("number of signals exceeds std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.slotList.size() > std::numeric_limits::max())) + error("number of declared slots exceeds std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.methodList.size() > std::numeric_limits::max())) + error("number of methods exceeds std::numeric_limits::max()."); + + if (Q_UNLIKELY(def.publicList.size() > std::numeric_limits::max())) + error("number of public functions declared in this class exceeds " + "std::numeric_limits::max()."); +} + void Moc::parse() { QList namespaceList; @@ -974,6 +1013,8 @@ void Moc::parse() checkProperties(&def); + checkListSizes(def); + classList += def; QHash &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets; classHash.insert(def.classname, def.qualified); @@ -993,8 +1034,10 @@ void Moc::parse() if (it != classList.end()) { it->classInfoList += def.classInfoList; + Q_ASSERT(it->classInfoList.size() <= std::numeric_limits::max()); it->enumDeclarations.insert(def.enumDeclarations); it->enumList += def.enumList; + Q_ASSERT(it->enumList.size() <= std::numeric_limits::max()); it->flagAliases.insert(def.flagAliases); } else { knownGadgets.insert(def.classname, def.qualified); @@ -1915,7 +1958,7 @@ void Moc::checkProperties(ClassDef *cdef) } if (!p.notify.isEmpty()) { int notifyId = -1; - for (int j = 0; j < cdef->signalList.size(); ++j) { + for (int j = 0; j < int(cdef->signalList.size()); ++j) { const FunctionDef &f = cdef->signalList.at(j); if (f.name != p.notify) { continue; @@ -1926,10 +1969,10 @@ void Moc::checkProperties(ClassDef *cdef) } p.notifyId = notifyId; if (notifyId == -1) { - int index = cdef->nonClassSignalList.indexOf(p.notify); + const int index = int(cdef->nonClassSignalList.indexOf(p.notify)); if (index == -1) { cdef->nonClassSignalList << p.notify; - p.notifyId = -1 - cdef->nonClassSignalList.size(); + p.notifyId = int(-1 - cdef->nonClassSignalList.size()); } else { p.notifyId = -2 - index; } diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index 512428356e..640e19e234 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -280,6 +280,8 @@ public: void checkSuperClasses(ClassDef *def); void checkProperties(ClassDef* cdef); + + void checkListSizes(const ClassDef &def); }; inline QByteArray noRef(const QByteArray &type)