من وقت لآخر ، يعيد فريقنا التحقق من المشاريع التي كتبنا عنها بالفعل مقالات. مشروع آخر تم إعادة فحصه كان Qt. آخر مرة اختبرناها مع PVS-Studio في عام 2014. منذ عام 2014 ، بدأ فحص المشروع بانتظام بمساعدة Coverity. هذا مثير للاهتمام. دعونا نرى ما إذا كان بإمكاننا الآن العثور على أي أخطاء مثيرة للاهتمام باستخدام PVS-Studio.
كيو تي
المقالات السابقة:
هذه المرة
تم اختبار Qt Base (Core ، Gui ، Widgets ، Network ، ...) و
Qt5 super module . حول Qt Creator ، نخطط لكتابة مقال منفصل لاحقًا. للتحقق ، استخدمنا محلل ثابت PVS-Studio ، وهو إصدار تجريبي يمكنك
تنزيله من الموقع.
في رأيي الشخصي ، أصبح كود Qt أفضل. على مر السنين منذ الاختبار الأخير ، ظهرت العديد من التشخيصات الجديدة في محلل PVS-Studio. على الرغم من ذلك ، خلال دراسة مراجعة التحذيرات ، لم أجد الكثير من الأخطاء لمشروع بهذا الحجم. أكرر مرة أخرى أن هذا هو انطباعي الفردي. لم أقم بأي بحث خاص حول كثافة الأخطاء سواء في ذلك الوقت أو الآن.
على الأرجح ، أثرت الفحوصات المنتظمة باستخدام محلل الغلاف الثابت على جودة الشفرة على الأرجح. في عام 2014 ، بمساعدة Coverity ، بدأ فحص مشروع Qt (مشروع
qt ) ، وفي عام 2016 ، تم إنشاء Qt Creator (
qt-creator ). رأيي: إذا كنت تقوم بتطوير مشروع مفتوح ، فإن
Coverity Scan يمكن أن يكون حلاً مجانيًا جيدًا سيحسن بشكل كبير جودة وموثوقية مشاريعك.
ومع ذلك ، كما يمكن للقارئ أن يخمن ، إذا لم أكن قد لاحظت أي شيء مثير للاهتمام في تقرير PVS-Studio ، فلن تكون هناك مقالة :). وبما أن هناك مقال ، أي عيوب. دعونا نلقي نظرة عليهم. في المجموع ، كتبت 96 خطأ.
نسخ ولصق أخطاء فاشلة
لنبدأ بكلاسيكيات هذا النوع ، عندما يكون سبب الخطأ هو عدم الانتباه. يتم تقدير هذه الأخطاء من قبل المبرمجين. بالنسبة لأولئك الذين لم يقرؤوا بعد ، أوصي بإلقاء نظرة على هاتين المادتين:
هذه الأخطاء هي لغة متداخلة. على سبيل المثال ، تقدم المقالة الثانية الكثير من الأمثلة على الأخطاء في وظائف المقارنة المكتوبة في C و C ++ و C #. الآن ، عند تنفيذ دعم لغة جافا في PVS-Studio ، نواجه نفس أنماط الخطأ. هنا ، على سبيل المثال ، يوجد خطأ وجدناه مؤخرًا في مكتبة
Hibernate :
public boolean equals(Object other) { if (other instanceof Id) { Id that = (Id) other; return purchaseSequence.equals(this.purchaseSequence) && that.purchaseNumber == this.purchaseNumber; } else { return false; } }
إذا نظرت عن كثب ، اتضح أن حقل PurchaseSequence يتم مقارنته بنفسه. الخيار الصحيح:
return that.purchaseSequence.equals(this.purchaseSequence) && that.purchaseNumber == this.purchaseNumber;
بشكل عام ، كل شيء كما هو الحال دائمًا ، وسيتعين على محلل PVS-Studio "إشعال اسطبلات Augean" في مشاريع Java. بالمناسبة ، ندعو الجميع للمشاركة في اختبار الإصدار التجريبي من PVS-Studio for Java ، والذي من المقرر أن يظهر في المستقبل القريب. للقيام بذلك ،
اكتب إلينا (حدد "أريد محلل لـ Java").
نعود الآن إلى الأخطاء في مشروع Qt.
عيب N1 static inline int windowDpiAwareness(HWND hwnd) { return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getWindowDpiAwarenessContext ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext( QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd)) : -1; }
تحذير PVS-Studio: V501 CWE-571 هناك تعبيرات فرعية متطابقة "QWindowsContext :: user32dll.getWindowDpiAwarenessContext" إلى اليسار وإلى يمين عامل التشغيل "&&". qwindowscontext.cpp 150
أي تفسير خاص إلى جانب رسالة المحلل غير مطلوب هنا. يبدو لي أن التعبير كان يجب أن يكون هكذا:
return QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext && QWindowsContext::user32dll.getWindowDpiAwarenessContext ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext( QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd)) : -1;
عيب N2 ، N3 void QReadWriteLockPrivate::release() { Q_ASSERT(!recursive); Q_ASSERT(!waitingReaders && !waitingReaders && !readerCount && !writerCount); freelist->release(id); }
تحذير PVS-Studio: V501 CWE-571 هناك تعبيرات فرعية متطابقة إلى اليسار وإلى يمين عامل التشغيل "&&":! WaitingReaders &&! WaitingReaders qreadwritelock.cpp 632
كان الخطأ داخل
حالة الماكرو
Q_ASSERT ، لذلك ليس كبيرا. لكن مع ذلك ، هذا خطأ. تم
التحقق من متغير
waitReaders مرتين. ويبدو أنهم نسوا التحقق من بعض المتغيرات الأخرى.
تم العثور على خطأ متطابق في سطر 625 من ملف qreadwritelock.cpp. تحيا النسخ واللصق! :)
عيب N4 QString QGraphicsSceneBspTree::debug(int index) const { .... if (node->type == Node::Horizontal) { tmp += debug(firstChildIndex(index)); tmp += debug(firstChildIndex(index) + 1); } else { tmp += debug(firstChildIndex(index)); tmp += debug(firstChildIndex(index) + 1); } .... }
تحذير PVS-Studio: V523 CWE-691 عبارة "ثم" تعادل عبارة "آخر". qgraphicsscene_bsp.cpp 179
على الأرجح ، تم نسخ كتلة النص ، لكنهم نسوا تصحيحها.
عيب N5 enum FillRule { OddEvenFill, WindingFill }; QDataStream &operator>>(QDataStream &s, QPainterPath &p) { .... int fillRule; s >> fillRule; Q_ASSERT(fillRule == Qt::OddEvenFill || Qt::WindingFill); .... }
تحذير PVS-Studio: V768 CWE-571 يتم استخدام ثابت التعداد "WindingFill" كمتغير من نوع منطقي. 2479
أوافق ، هذه غلطة جميلة! لا
يتحقق Q_ASSERT من أي شيء ، لأن الشرط دائمًا صحيح. الشرط صحيح لأن الثابت
Qt :: WindingFill هو 1.
عيب N6 bool QVariant::canConvert(int targetTypeId) const { .... if (currentType == QMetaType::SChar || currentType == QMetaType::Char) currentType = QMetaType::UInt; if (targetTypeId == QMetaType::SChar || currentType == QMetaType::Char) targetTypeId = QMetaType::UInt; .... }
قبل قراءة التحذير ، حاول اكتشاف خطأ مطبعي بنفسك. بإضافة صورة ، سأساعدك على عدم قراءة رسالة المحلل على الفور :).
تحذير PVS-Studio: V560 CWE-570 دائمًا ما يكون جزء من التعبير الشرطي خطأ: currentType == QMetaType :: Char. qvariant.cpp 3529
الحالة "currentType == QMetaType :: Char" محددة في الحالة الأولى. إذا تم استيفاء الشرط ،
يتم تعيين قيمة
QMetaType :: UInt للمتغير الحالي . لذلك ، علاوة على ذلك ، لا يمكن أن يكون المتغير
currentType مساوياً لـ
QMetaType :: Char . لذلك ، يشير المحلل إلى أنه في الحالة الثانية
، يكون التعبير الفرعي "currentType == QMetaType :: Char" دائمًا خاطئًا.
في الواقع ، يجب أن يكون الثاني
إذا كان مثل هذا:
if (targetTypeId == QMetaType::SChar || targetTypeId == QMetaType::Char) targetTypeId = QMetaType::UInt;
ملاحظة تشخيصية V560وجد التقرير العديد من تحذيرات V560. ومع ذلك ، لم أعد أنظر إليهم بمجرد أن وجدت حالة مثيرة للاهتمام للمقال ، والتي اعتبرت أعلاه على أنها عيب N6.
لا يمكن تسمية الغالبية العظمى من الرسائل V560 بأنها خاطئة ، ولكن لا فائدة منها. وبعبارة أخرى ، فإن وصفها في مقال ليست مثيرة للاهتمام. لتوضيح ما أعنيه بالضبط ، ضع في اعتبارك إحدى هذه الحالات.
QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, ....) { QString url; if (!doc) return url; if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent())) return findUrlForImage(parent, cacheKey, isPixmap); if (doc && doc->docHandle()) {
تحذير PVS-Stuidio: V560 CWE-571 دائمًا ما يكون جزء من التعبير الشرطي صحيحًا: doc. qtextdocument.cpp 2992
المحلل صحيح تمامًا أن مؤشر
doc ليس دائمًا
nullptr عند إعادة فحصه. لكن هذا ليس خطأ ، كان المبرمج فقط آمنًا. يمكنك تبسيط الرمز عن طريق كتابة:
if (doc->docHandle()) {
عيب N7والحالة الأخيرة التي يمكن تصنيفها على أنها خطأ مطبعي. يحدث الخطأ بسبب الارتباك في أسماء الثوابت ، والتي تختلف فقط في حالة الحرف الأول.
class QWindowsCursor : public QPlatformCursor { public: enum CursorState { CursorShowing, CursorHidden, CursorSuppressed }; .... } QWindowsCursor::CursorState QWindowsCursor::cursorState() { enum { cursorShowing = 0x1, cursorSuppressed = 0x2 }; CURSORINFO cursorInfo; cursorInfo.cbSize = sizeof(CURSORINFO); if (GetCursorInfo(&cursorInfo)) { if (cursorInfo.flags & CursorShowing) .... }
تحذير PVS-Studio: V616 CWE-480 يتم استخدام ثابت "CursorShowing" المسمى بقيمة 0 في عملية أحادي البتات. 669
بمزيد من التفصيل ، قمت بالفعل بتحليل هذا الخطأ في ملاحظة صغيرة منفصلة: "
مرة أخرى ، تبين أن محلل PVS-Studio أصبح أكثر انتباهاً من
الشخص ."
العيوب الأمنية
في الواقع ، يمكن أن تسمى جميع الأخطاء التي تمت مناقشتها في هذه المقالة عيوب الأمان. يتم تصنيفهم جميعًا وفقًا
لتعداد الضعف العام (انظر معرف CWE في رسائل المحلل). إذا تم تصنيف الأخطاء على أنها CWEs ، فمن المحتمل أن تكون خطرًا أمنيًا. يتم شرح ذلك بمزيد من التفصيل في صفحة
PVS-Studio SAST .
ومع ذلك ، أود أن أفرد عددًا من الأخطاء في مجموعة منفصلة. دعونا نلقي نظرة عليها.
عيب N8 ، N9 bool QLocalServerPrivate::addListener() { .... SetSecurityDescriptorOwner(pSD.data(), pTokenUser->User.Sid, FALSE); SetSecurityDescriptorGroup(pSD.data(), pTokenGroup->PrimaryGroup, FALSE); .... }
تحذيرات PVS-Studio:
- V530 CWE-252 القيمة المرجعة للدالة 'SetSecurityDescriptorOwner' مطلوبة لاستخدامها. qlocalserver_win.cpp 167
- V530 CWE-252 القيمة المرجعة للدالة 'SetSecurityDescriptorGroup' مطلوبة لاستخدامها. qlocalserver_win.cpp 168
هناك العديد من الوظائف المتعلقة بالتحكم في الوصول.
الدالتان SetSecurityDescriptorOwner و
SetSecurityDescriptorGroup من بينها.
مع هذه الوظائف تحتاج إلى العمل بعناية فائقة. على سبيل المثال ، يجب عليك التحقق من الحالة التي يعودون إليها. ماذا يحدث إذا فشل الاستدعاء لهذه الوظائف؟ التخمين ليس ضروريًا ، من الضروري كتابة رمز للتعامل مع مثل هذه الحالة.
ليس من الضروري الاستفادة من عدم التحقق وتحويل هذه الأخطاء إلى نقاط ضعف. ومع ذلك ، هذا ليس بأي حال مكانًا للمخاطرة ، وتحتاج إلى كتابة رمز أكثر أمانًا.
عيب N10 bool QLocalServerPrivate::addListener() { .... InitializeAcl(acl, aclSize, ACL_REVISION_DS); .... }
تحذير PVS-Studio: V530 CWE-252 يجب استخدام القيمة المرجعة للدالة 'InitializeAcl'. qlocalserver_win.cpp 144
الوضع مشابه لذلك الذي نوقش أعلاه.
عيب N11 ، N12 static inline void sha1ProcessChunk(....) { .... quint8 chunkBuffer[64]; .... #ifdef SHA1_WIPE_VARIABLES .... memset(chunkBuffer, 0, 64); #endif }
تحذير PVS-Studio: V597 CWE-14 يمكن للمترجم حذف استدعاء وظيفة "memset" ، والذي يتم استخدامه لمسح المخزن المؤقت "chunkBuffer". يجب استخدام الدالة RtlSecureZeroMemory () لمسح البيانات الخاصة. sha1.cpp 189
سيقوم المحول البرمجي بإزالة استدعاء وظيفة
memset . بالفعل عدة مرات في المقالات قمت بتحليل هذا الوضع. لا أشعر برغبة في تكرار نفسي. أشير إلى المقالة "
التنظيف الآمن للبيانات الخاصة ".
وخطأ آخر في نفس الملف sha1.cpp ، في السطر 247.
مؤشرات فارغة
حان الوقت للحديث عن المؤشرات. كان هناك الكثير من الأخطاء حول هذا الموضوع.
عيب N13 QByteArray &QByteArray::append(const char *str, int len) { if (len < 0) len = qstrlen(str); if (str && len) { .... }
تحذير PVS-Studio: V595 CWE-476 تم استخدام مؤشر 'str' قبل التحقق من صلاحيته باستخدام nullptr. خطوط التحقق: 2118 ، 2119. qbytearray.cpp 2118
الوضع الكلاسيكي هو عندما يتم استخدام المؤشر في البداية ثم يتم التحقق من المساواة في
nullptr . هذا نمط خطأ شائع جدًا ،
ونراه بشكل منتظم في جميع المشاريع تقريبًا.
عيب N14 ، N15 static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); } bool QMetaEnum::isFlag() const { const int offset = priv(mobj->d.data)->revision >= 8 ? 2 : 1; return mobj && mobj->d.data[handle + offset] & EnumIsFlag; }
تحذير PVS-Studio: V595 CWE-476 تم استخدام مؤشر "mobj" قبل التحقق من صحته باستخدام nullptr. خطوط التحقق: 2671 ، 2672. qmetaobject.cpp 2671
فقط في حالة ، أحضر جسم الوظيفة الخاصة. لسبب ما ، يبدأ القراء أحيانًا في التوصل إلى مواقف تعمل فيها الشفرة. لا أفهم من أين يأتي هذا الارتياب والرغبة في رؤية ميزة صعبة في الخطأ :). على سبيل المثال ، قد يقترح شخص ما في التعليقات أن
priv هو ماكرو من النموذج:
#define priv(A) foo(sizeof(A))
ثم سيعمل كل شيء.
من أجل تجنب مثل هذه المناقشات ، أحاول أن أذكر أجزاء من التعليمات البرمجية حيث يتم توفير جميع المعلومات التي تؤكد وجود خطأ.
لذلك ، يتم
إلغاء الإشارة إلى مؤشر
modj ثم التحقق منه.
مزيد من المشهد يأتي مع النسخ "الجبار والرهيب" نسخ ولصق. بسبب ما تم الكشف عن نفس الخطأ بالضبط في الدالة
isScoped :
bool QMetaEnum::isScoped() const { const int offset = priv(mobj->d.data)->revision >= 8 ? 2 : 1; return mobj && mobj->d.data[handle + offset] & EnumIsScoped; }
تحذير PVS-Studio: V595 CWE-476 تم استخدام مؤشر "mobj" قبل التحقق من صحته باستخدام nullptr. خطوط التحقق: 2683 ، 2684. qmetaobject.cpp 2683
عيب N16-N21لنأخذ مثالاً آخر ، وأعتقد أنه يكفي.
void QTextCursor::insertFragment(const QTextDocumentFragment &fragment) { if (!d || !d->priv || fragment.isEmpty()) return; d->priv->beginEditBlock(); d->remove(); fragment.d->insert(*this); d->priv->endEditBlock(); if (fragment.d && fragment.d->doc) d->priv->mergeCachedResources(fragment.d->doc->docHandle()); }
تحذير PVS-Studio: V595 CWE-476 تم استخدام مؤشر "fragment.d" قبل التحقق من صحته باستخدام nullptr. خطوط التحقق: 2238 ، 2241. qtextcursor.cpp 2238
كل نفس. انتبه إلى تسلسل العمل مع المؤشر المخزن في الجزء المتغير.
أخطاء أخرى من هذا النوع:
- V595 CWE-476 تم استخدام مؤشر "النافذة" قبل التحقق من صلاحيته باستخدام nullptr. خطوط التحقق: 1846 ، 1848. qapplication.cpp 1846
- V595 CWE-476 تم استخدام مؤشر "النافذة" قبل التحقق من صلاحيته باستخدام nullptr. خطوط التحقق: 1858 ، 1860. qapplication.cpp 1858
- V595 CWE-476 تم استخدام مؤشر "الرد" قبل أن يتم التحقق منه مقابل nullptr. خطوط التحقق: 492 ، 502. qhttpnetworkconnectionchannel.cpp 492
- V595 CWE-476 تم استخدام مؤشر "newHandle" قبل التحقق من صلاحيته باستخدام nullptr. خطوط التحقق: 877 ، 883. qsplitter.cpp 877
- V595 CWE-476 تم استخدام مؤشر "عنصر واجهة المستخدم" قبل التحقق من صلاحيته باستخدام nullptr. خطوط التحقق: 2320 ، 2322. qwindowsvistastyle.cpp 2320
- في الواقع ، هناك المزيد من الأخطاء. لقد سئمت بسرعة من تعلم تحذيرات V595 ، وبالنسبة للمقالة ، كتبت بالفعل أجزاء كافية من التعليمات البرمجية.
عيب N22-N33يوجد رمز حيث يتم التحقق من مؤشر إرجاع عامل التشغيل
الجديد . هذا مضحك بشكل خاص وسط حقيقة أن هناك الكثير من الأماكن التي لا يتم فيها تحديد نتيجة وظيفة
malloc (انظر مجموعة الأخطاء التالية).
bool QTranslatorPrivate::do_load(const QString &realname, const QString &directory) { .... d->unmapPointer = new char[d->unmapLength]; if (d->unmapPointer) { file.seek(0); qint64 readResult = file.read(d->unmapPointer, d->unmapLength); if (readResult == qint64(unmapLength)) ok = true; } .... }
تحذير PVS-Studio: V668 CWE-571 لا يوجد أي معنى في اختبار مؤشر "d-> unmap Pointer" مقابل قيمة خالية ، حيث تم تخصيص الذاكرة باستخدام عامل التشغيل "new". سيتم إنشاء الاستثناء في حالة خطأ تخصيص الذاكرة. qtranslator.cpp 596
التحقق من المؤشر لا معنى له ، لأنه في حالة وجود خطأ في تخصيص الذاكرة ، سيتم
طرح استثناء
std :: bad_alloc . إذا كنت تريد أن يقوم عامل التشغيل
الجديد بإرجاع
nullptr في حالة عدم وجود ذاكرة كافية ، فيجب عليك كتابة:
d->unmapPointer = new (std::nothrow) char[d->unmapLength];
يعرف المحلل عن هذا الاستخدام للعامل
الجديد ولن يعطي تحذيرًا في هذه الحالة.
أخطاء أخرى:
سأعطيهم ملف qt-V668.txt .
عيب N34-N70كما هو موعود ، الآن حان دور الأخطاء عندما لا يتحققون من نتيجة استدعاء الوظائف
malloc ،
calloc ،
strdup ، إلخ. هذه الأخطاء أكثر خطورة مما تبدو للوهلة الأولى. مزيد من التفاصيل: "
لماذا من المهم التحقق مما عادت دالة malloc ."
SourceFiles::SourceFiles() { nodes = (SourceFileNode**)malloc(sizeof(SourceFileNode*)*(num_nodes=3037)); for(int n = 0; n < num_nodes; n++) nodes[n] = nullptr; }
تحذير PVS-Studio: V522 CWE-690 قد يكون هناك إحالة مرجعية لـ "عقد" مؤشر خالية محتملة. تحقق من الخطوط: 138 ، 136. makefiledeps.cpp 138
يتم استخدام المؤشر دون التحقق المسبق.
كل هذه الأخطاء من نفس النوع ، لذلك لن أتناولها بمزيد من التفصيل. سأعطي بقية قائمة التحذير:
qt-V522-V575.txt .
الأخطاء المنطقية في الظروف
عيب N71 QString QEdidParser::parseEdidString(const quint8 *data) { QByteArray buffer(reinterpret_cast<const char *>(data), 13);
تحذير PVS-Studio: V547 CWE-570 Expression 'buffer [i] <' \ 040 '&& buffer [i]>' \ 176 '' دائمًا خطأ. qedidparser.cpp 169
يجب أن تقوم الوظيفة بالإجراء التالي "استبدال الأحرف غير القابلة للطباعة بشرطة". ومع ذلك ، لم تفعل ذلك. دعونا نلقي نظرة فاحصة على هذه الحالة:
if (buffer[i] < '\040' && buffer[i] > '\176')
لا معنى له. لا يمكن أن يكون الحرف أقل من '\ 040' وأكبر من '\ 176' في نفس الوقت. في الحالة يجب عليك استخدام عامل التشغيل '||'. الرمز الصحيح هو:
if (buffer[i] < '\040' || buffer[i] > '\176')
عيب N72خطأ مماثل ، بسبب عدم حظ مستخدمي Windows.
#if defined(Q_OS_WIN) static QString driveSpec(const QString &path) { if (path.size() < 2) return QString(); char c = path.at(0).toLatin1(); if (c < 'a' && c > 'z' && c < 'A' && c > 'Z') return QString(); if (path.at(1).toLatin1() != ':') return QString(); return path.mid(0, 2); } #endif
ينشئ المحلل تحذيرين في وقت واحد:
- V590 CWE-571 خذ بعين الاعتبار فحص تعبير 'c <' a '&& c>' z '&& c <' A '&& c>' Z ''. التعبير زائد أو يحتوي على خطأ مطبعي. qdir.cpp 77
- V560 CWE-570 جزء من التعبير الشرطي خطأ دائمًا: c> 'z'. qdir.cpp 77
خطأ منطقي في الحالة:
if (c < 'a' && c > 'z' && c < 'A' && c > 'Z')
كما أفهمها ، أراد المبرمج العثور على حرف ليس حرفًا من الأبجدية اللاتينية. في هذه الحالة ، يجب أن يكون الشرط كما يلي:
if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z'))
عيب N73 enum SelectionMode { NoSelection, SingleSelection, MultiSelection, ExtendedSelection, ContiguousSelection }; void QAccessibleTableCell::unselectCell() { QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); if (!m_index.isValid() || (selectionMode & QAbstractItemView::NoSelection)) return; .... }
تحذير PVS-Studio: V616 CWE-480 يتم استخدام ثابت QAb abstractItemView :: NoSelection مع قيمة 0 في عملية أحادي المعامل. 976
ثابت
QAb abstractItemView :: NoSelection هو صفر. لذلك ، لا معنى
للتعبير الفرعي (selectMode & QAb abstractItemView :: NoSelection) . ستكون دائمًا 0.
أعتقد أنه يجب كتابتها هنا:
if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection))
عيب N74من الصعب علي فهم الكود التالي. إنه مخطئ ، لكني لا أعرف ما يجب أن يكون. التعليق على وظيفة لا يساعدني أيضًا.
تحذير PVS-Studio: رسالة تعبير V547 CWE-570 خاطئة دائمًا. qwindowscontext.cpp 802
ربما يفترض المبرمج أن وظيفة
FormatMessage ستغير قيمة مؤشر
الرسالة . لكن الأمر ليس كذلك. لا يمكن
للدالة FormatMessage تغيير قيمة المؤشر ، حيث يتم تمريره إلى الدالة حسب القيمة. هنا نموذج أولي لهذه الوظيفة:
DWORD __stdcall FormatMessageW( DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, va_list *Arguments );
تسرب الذاكرة المحتملة
عيب N75-N92 struct SourceDependChildren { SourceFile **children; int num_nodes, used_nodes; SourceDependChildren() : children(nullptr), num_nodes(0), used_nodes(0) { } ~SourceDependChildren() { if (children) free(children); children = nullptr; } void addChild(SourceFile *s) { if(num_nodes <= used_nodes) { num_nodes += 200; children = (SourceFile**)realloc(children, sizeof(SourceFile*)*(num_nodes)); } children[used_nodes++] = s; } };
تحذير PVS-Studio: V701 CWE-401 realloc () تسرب محتمل: عندما يفشل realloc () في تخصيص الذاكرة ، يتم فقدان المؤشر الأصلي "أطفال". خذ بعين الاعتبار تعيين realloc () لمؤشر مؤقت. ١٠٣
يتم تنفيذ توسيع المخزن المؤقت بطريقة خطيرة. إذا تعذر على الدالة
realloc تخصيص الذاكرة ، فسوف تُرجع
NULL . سيتم وضع
NULL على الفور في المتغير التابع ولن تكون هناك إمكانية لتحرير المخزن المؤقت المخصص مسبقًا. سيحدث تسرب للذاكرة.
أخطاء مماثلة:
qt-701.txt .
متفرقات
عيب N93 template<class GradientBase, typename BlendType> static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template(....) { .... if (t+inc*length < qreal(INT_MAX >> (FIXPT_BITS + 1)) && t+inc*length > qreal(INT_MIN >> (FIXPT_BITS + 1))) { .... }
تحذير PVS-Studio: V610 CWE-758 سلوك غير محدد. تحقق من عامل النقل '>>'. المعامل الأيسر "(- 2147483647 - 1)" سلبي. قحطان 4015
لا يمكن تغيير القيمة السالبة لـ
INT_MIN . هذا سلوك غير محدد ، ولا يمكنك الاعتماد على نتيجة هذه العملية. يمكن أن تكون البتات الأكثر أهمية تساوي 0 أو 1.
عيب N94 void QObjectPrivate::addConnection(int signal, Connection *c) { .... if (signal >= connectionLists->count()) connectionLists->resize(signal + 1); ConnectionList &connectionList = (*connectionLists)[signal]; .... if (signal < 0) { .... }
تحذير PVS-Studio: V781 CWE-129 يتم التحقق من قيمة متغير "الإشارة" بعد استخدامه. ربما هناك خطأ في منطق البرنامج. خطوط التحقق: 397 ، 413. qobject.cpp 397
يشير الاختيار
(الإشارة <0) إلى أن قيمة وسيطة
الإشارة قد تكون سالبة. ومع ذلك ، تم استخدام هذه الوسيطة سابقًا لفهرسة الصفيف. اتضح أن الفحص يتم في وقت متأخر جدًا. سيتم بالفعل تعطيل البرنامج.
عيب N95 bool QXmlStreamWriterPrivate::finishStartElement(bool contents) { .... if (inEmptyElement) { write("/>"); QXmlStreamWriterPrivate::Tag &tag = tagStack_pop(); lastNamespaceDeclaration = tag.namespaceDeclarationsSize; lastWasStartElement = false; } else { write(">"); } inStartElement = inEmptyElement = false; lastNamespaceDeclaration = namespaceDeclarations.size(); return hadSomethingWritten; }
تحذير PVS-Studio: V519 CWE-563 يتم تعيين قيم المتغير "lastNamespaceDeclaration" مرتين على التوالي. ربما هذا خطأ. خطوط التحقق: 3188 ، 3194. qxmlstream.cpp 3194
سوف أسلط الضوء على جوهر الخطأ:
if (inEmptyElement) { lastNamespaceDeclaration = tag.namespaceDeclarationsSize; } lastNamespaceDeclaration = namespaceDeclarations.size();
عيب N96 void QRollEffect::scroll() { .... if (currentHeight != totalHeight) { currentHeight = totalHeight * (elapsed/duration) + (2 * totalHeight * (elapsed%duration) + duration) / (2 * duration);
V519 CWE-563 يتم تعيين المتغير "تم" مرتين متتاليتين. ربما هذا خطأ. تحقق من الخطوط: 509 ، 511. qeffects.cpp 511
كل شيء هو نفسه كما في الحالة السابقة. لاحظ المتغير
المنجز .
الخلاصة
حتى أثناء النظر في التقرير بشكل سطحي ، كتبت ما يقرب من 100 خطأ. يسرني نتائج PVS-Studio.
وبالطبع ، فإن عمليات التحقق من التعليمات البرمجية النادرة هذه لا علاقة لها بتحسين جودة وموثوقية التعليمات البرمجية. فهي تُظهر فقط قدرات محلل الكود. يجب تطبيق أدوات التحليل الثابت بانتظام. في هذه الحالة ، تقلل من تكلفة إصلاح الأخطاء وحماية التطبيقات من العديد من الثغرات الأمنية المحتملة.
شكرا لكم على اهتمامكم. لمواكبة منشوراتنا الجديدة ، أدعوك للاشتراك في إحدى قنواتنا:
- VK.com: pvsstudio_rus
- "Old school" RSS: viva64-blog-ru
- تويتر: @ pvsstudio_rus
- انستقرام : @ pvsstudio_rus
- برقية: pvsstudio_rus

إذا كنت تريد مشاركة هذه المقالة مع جمهور ناطق باللغة الإنجليزية ، فيرجى استخدام رابط الترجمة: Andrey Karpov.
فحص ثالث لـ Qt 5 مع PVS-Studio