Tes Qt 5 Ketiga dengan PVS-Studio

PVS-Studio & Qt

Dari waktu ke waktu, tim kami memeriksa kembali proyek yang sudah kami tulis artikelnya. Proyek lain yang diperiksa ulang adalah Qt. Terakhir kali kami mengujinya dengan PVS-Studio pada tahun 2014. Sejak 2014, proyek mulai diperiksa secara teratur dengan bantuan Coverity. Ini menarik. Mari kita lihat apakah kita sekarang dapat menemukan kesalahan menarik menggunakan PVS-Studio.

Qt


Artikel sebelumnya:


Kali ini Qt Base (Core, Gui, Widgets, Network, ...) dan modul super Qt5 diuji . Tentang Qt Creator, kami berencana untuk menulis artikel terpisah nanti. Untuk verifikasi, kami menggunakan penganalisa statis PVS-Studio, versi percobaan yang dapat Anda unduh dari situs.

Menurut pendapat subjektif saya, kode Qt telah menjadi lebih baik. Selama bertahun-tahun sejak tes terakhir, banyak diagnostik baru telah muncul di alat analisa PVS-Studio. Meskipun demikian, selama studi peninjauan peringatan, saya tidak menemukan banyak kesalahan untuk proyek sebesar ini. Saya ulangi sekali lagi bahwa ini adalah kesan pribadi saya. Saya tidak melakukan riset khusus tentang kepadatan kesalahan dulu atau sekarang.

Kemungkinan besar, pemeriksaan rutin menggunakan penganalisa statis Coverity kemungkinan besar memengaruhi kualitas kode. Pada tahun 2014, dengan bantuan Coverity, proyek Qt ( qt-project ) mulai diperiksa, dan pada 2016, Qt Creator ( qt-creator ). Pendapat saya: jika Anda sedang mengembangkan proyek terbuka, maka Coverity Scan dapat menjadi solusi gratis yang baik yang secara signifikan akan meningkatkan kualitas dan keandalan proyek Anda.

Namun, seperti yang bisa ditebak oleh pembaca, jika saya tidak melihat sesuatu yang menarik dalam laporan PVS-Studio, maka tidak akan ada artikel :). Dan karena ada artikel, yaitu cacat. Mari lihat mereka. Secara total, saya menulis 96 kesalahan.

Salin-Tempel dan Kesalahan Jenis yang tidak berhasil


Mari kita mulai dengan genre klasik, ketika penyebab kesalahan adalah kurangnya perhatian. Kesalahan ini diremehkan oleh programmer. Bagi yang belum membaca, saya sarankan Anda melihat dua artikel ini:


Kesalahan ini adalah antarbahasa. Sebagai contoh, artikel kedua memberikan banyak contoh kesalahan dalam fungsi perbandingan yang ditulis dalam C, C ++ dan C #. Sekarang, ketika mengimplementasikan dukungan bahasa Java di PVS-Studio, kami menemukan pola kesalahan yang sama. Di sini, misalnya, adalah bug yang baru-baru ini kami temukan di perpustakaan 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; } } 

Jika Anda melihat lebih dekat, ternyata bidang Purchasingequequence dibandingkan dengan dirinya sendiri. Opsi yang benar:

 return that.purchaseSequence.equals(this.purchaseSequence) && that.purchaseNumber == this.purchaseNumber; 

Secara umum, semuanya seperti biasa, dan penganalisa PVS-Studio harus "menyapu kandang Augean" di proyek-proyek Jawa. Ngomong-ngomong, kami mengundang semua orang untuk ikut serta dalam pengujian versi Beta PVS-Studio untuk Java, yang akan segera muncul dalam waktu dekat. Untuk melakukan ini, tulis kepada kami (pilih "Saya ingin penganalisis untuk Java").

Sekarang kembali ke kesalahan dalam proyek Qt.

Cacat N1

 static inline int windowDpiAwareness(HWND hwnd) { return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getWindowDpiAwarenessContext ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext( QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd)) : -1; } 

Peringatan PVS-Studio: V501 CWE-571 Ada sub-ekspresi yang identik 'QWindowsContext :: user32dll.getWindowDpiAwarenessContext' di kiri dan di kanan operator '&&'. qwindowscontext.cpp 150

Penjelasan khusus selain pesan analisa tidak diperlukan di sini. Tampaknya bagi saya ungkapan itu seharusnya seperti ini:

 return QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext && QWindowsContext::user32dll.getWindowDpiAwarenessContext ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext( QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd)) : -1; 

Cacat N2, N3

 void QReadWriteLockPrivate::release() { Q_ASSERT(!recursive); Q_ASSERT(!waitingReaders && !waitingReaders && !readerCount && !writerCount); freelist->release(id); } 

Peringatan PVS-Studio: V501 CWE-571 Ada sub-ekspresi yang identik ke kiri dan ke kanan operator '&&':! WaitingReaders &&!! WaitingReaders qreadwritelock.cpp 632

Kesalahan ada di dalam kondisi makro Q_ASSERT , jadi itu tidak signifikan. Tapi tetap saja, ini sebuah kesalahan. Variabel waitingReaders diperiksa dua kali. Dan ternyata mereka lupa memeriksa beberapa variabel lainnya.

Kesalahan identik ditemukan di baris 625 file qreadwritelock.cpp. Salin-Tempel hidup lama! :)

Cacat 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); } .... } 

Peringatan PVS-Studio: V523 CWE-691 Pernyataan 'then' sama dengan pernyataan 'else'. qgraphicsscene_bsp.cpp 179

Kemungkinan besar, blok teks disalin, tetapi mereka lupa memperbaikinya.

Cacat N5

 enum FillRule { OddEvenFill, WindingFill }; QDataStream &operator>>(QDataStream &s, QPainterPath &p) { .... int fillRule; s >> fillRule; Q_ASSERT(fillRule == Qt::OddEvenFill || Qt::WindingFill); .... } 

PVS-Studio Warning: V768 CWE-571 Konstanta enumerasi 'WindingFill' digunakan sebagai variabel tipe-Boolean. qpainterpath.cpp 2479

Setuju, ini blooper yang indah! Q_ASSERT tidak memeriksa apa pun, karena kondisinya selalu benar. Kondisi ini benar karena konstanta bernama Qt :: WindingFill adalah 1.

Cacat 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; .... } 

Sebelum membaca peringatan, coba cari kesalahan ketik sendiri. Dengan menambahkan gambar, saya akan membantu Anda untuk tidak segera membaca pesan analisa :).

Waktunya berpikir


Peringatan PVS-Studio: V560 CWE-570 Bagian dari ekspresi kondisional selalu salah: currentType == QMetaType :: Char. qvariant.cpp 3529

Kondisi "currentType == QMetaType :: Char" diperiksa di if pertama. Jika kondisi terpenuhi, maka variabel currentType ditugaskan nilai QMetaType :: UInt . Oleh karena itu, lebih lanjut variabel currentType tidak lagi sama dengan QMetaType :: Char . Oleh karena itu, penganalisa melaporkan bahwa di kedua jika, subekspresi "currentType == QMetaType :: Char" selalu salah.

Bahkan, yang kedua kalau harus seperti ini:

 if (targetTypeId == QMetaType::SChar || targetTypeId == QMetaType::Char) targetTypeId = QMetaType::UInt; 


Catatan Diagnostik V560

Laporan itu menemukan banyak peringatan V560. Namun, saya tidak melihat mereka lagi segera setelah saya menemukan kasus yang menarik untuk artikel tersebut, yang dianggap di atas sebagai cacat N6.

Sebagian besar pesan V560 tidak dapat disebut false, tetapi tidak ada gunanya. Dengan kata lain, mendeskripsikannya dalam sebuah artikel tidaklah menarik. Untuk memperjelas apa yang sebenarnya saya maksud, pertimbangkan satu kasus seperti itu.

 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()) { // <= .... } 

Peringatan PVS-Stuidio: V560 CWE-571 Bagian dari ekspresi kondisional selalu benar: doc. qtextdocument.cpp 2992

Analyzer benar-benar benar bahwa pointer doc tidak selalu nullptr ketika diperiksa ulang. Tapi ini bukan kesalahan, hanya programmer saja yang aman. Anda dapat menyederhanakan kode dengan menulis:

 if (doc->docHandle()) { 

Cacat N7

Dan kasus terakhir, yang bisa digolongkan sebagai salah ketik. Kesalahan terjadi karena kebingungan dalam nama-nama konstanta, yang hanya berbeda dalam kasus huruf pertama.

 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) .... } 

Peringatan PVS-Studio: V616 CWE-480 'CursorShowing' dinamai konstan dengan nilai 0 digunakan dalam operasi bitwise. qwindowscursor.cpp 669

Secara lebih rinci, saya sudah menganalisis kesalahan ini dalam sebuah catatan kecil yang terpisah: " Sekali lagi, penganalisa PVS-Studio ternyata lebih penuh perhatian daripada seseorang ."

Kelemahan keamanan


Bahkan, semua kesalahan yang dibahas dalam artikel ini bisa disebut cacat keamanan. Semuanya diklasifikasikan menurut Penghitungan Kelemahan Umum (lihat ID CWE dalam pesan analisis). Jika kesalahan diklasifikasikan sebagai CWE, mereka berpotensi risiko keamanan. Ini dijelaskan lebih terinci di halaman SAST PVS-Studio .

Namun, saya ingin memilih sejumlah kesalahan menjadi kelompok yang terpisah. Mari kita lihat mereka.

Cacat N8, N9

 bool QLocalServerPrivate::addListener() { .... SetSecurityDescriptorOwner(pSD.data(), pTokenUser->User.Sid, FALSE); SetSecurityDescriptorGroup(pSD.data(), pTokenGroup->PrimaryGroup, FALSE); .... } 

Peringatan PVS-Studio:

  • V530 CWE-252 Nilai kembali fungsi 'SetSecurityDescriptorOwner' diperlukan untuk digunakan. qlocalserver_win.cpp 167
  • V530 CWE-252 Nilai kembali fungsi 'SetSecurityDescriptorGroup' diperlukan untuk digunakan. qlocalserver_win.cpp 168

Ada berbagai fungsi yang terkait dengan kontrol akses. Fungsi-fungsi SetSecurityDescriptorOwner dan SetSecurityDescriptorGroup adalah di antara mereka.

Dengan fungsi seperti itu Anda harus bekerja dengan sangat hati-hati. Misalnya, Anda harus memeriksa status yang mereka kembalikan. Apa yang terjadi jika panggilan ke fungsi-fungsi ini gagal? Menebak tidak perlu, perlu menulis kode untuk menangani kasus ini.

Tidak perlu mengambil keuntungan dari kurangnya verifikasi dan mengubah kesalahan tersebut menjadi kerentanan. Namun, ini sama sekali bukan tempat untuk risiko, dan Anda perlu menulis kode yang lebih aman.

Cacat N10

 bool QLocalServerPrivate::addListener() { .... InitializeAcl(acl, aclSize, ACL_REVISION_DS); .... } 

PVS-Studio Warning: V530 CWE-252 Nilai pengembalian fungsi 'InitializeAcl' harus digunakan. qlocalserver_win.cpp 144

Situasinya mirip dengan yang dibahas di atas.

Cacat N11, N12

 static inline void sha1ProcessChunk(....) { .... quint8 chunkBuffer[64]; .... #ifdef SHA1_WIPE_VARIABLES .... memset(chunkBuffer, 0, 64); #endif } 

Peringatan PVS-Studio: V597 CWE-14 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'chunkBuffer'. Fungsi RtlSecureZeroMemory () harus digunakan untuk menghapus data pribadi. sha1.cpp 189

Kompiler akan menghapus panggilan fungsi memset . Sudah berkali-kali dalam artikel saya menganalisis situasi ini. Saya tidak ingin mengulang sendiri. Saya merujuk ke artikel " Pembersihan Data Pribadi yang Aman ".

Dan kesalahan lain ada di file sha1.cpp yang sama, di baris 247.

Pointer kosong


Sudah waktunya untuk berbicara tentang petunjuk. Ada banyak kesalahan dalam topik ini.

Cacat N13

 QByteArray &QByteArray::append(const char *str, int len) { if (len < 0) len = qstrlen(str); if (str && len) { .... } 

Peringatan PVS-Studio: V595 CWE-476 Pointer 'str' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 2118, 2119. qbytearray.cpp 2118

Situasi klasik adalah ketika pointer digunakan di awal dan kemudian diperiksa untuk kesetaraan nullptr . Ini adalah pola kesalahan yang sangat umum, dan kami melihatnya secara teratur di hampir semua proyek.

Cacat 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; } 

Peringatan PVS-Studio: V595 CWE-476 Pointer 'mobj' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 2671, 2672. qmetaobject.cpp 2671

Untuk jaga-jaga, saya membawa tubuh fungsi priv . Untuk beberapa alasan, terkadang pembaca mulai menemukan situasi di mana kode akan berfungsi. Saya tidak mengerti dari mana ketidakpercayaan ini berasal dan keinginan untuk melihat fitur yang rumit salah :). Misalnya, seseorang dapat menyarankan dalam komentar bahwa priv adalah makro dari formulir:

 #define priv(A) foo(sizeof(A)) 

Maka semuanya akan bekerja.

Untuk menghindari diskusi seperti itu, saya mencoba mengutip fragmen kode di mana semua informasi yang mengkonfirmasi adanya kesalahan disediakan.

Jadi, penunjuk modj dereferensi dan kemudian diperiksa.

Lebih jauh lagi pada adegan datang Copy-Paste "perkasa dan mengerikan". Karena kesalahan yang persis sama terdeteksi pada fungsi isScoped :

 bool QMetaEnum::isScoped() const { const int offset = priv(mobj->d.data)->revision >= 8 ? 2 : 1; return mobj && mobj->d.data[handle + offset] & EnumIsScoped; } 

Peringatan PVS-Studio: V595 CWE-476 Pointer 'mobj' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 2683, 2684. qmetaobject.cpp 2683

Cacat N16-N21

Pertimbangkan contoh lain dan, saya pikir, cukup.

 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()); } 

Peringatan PVS-Studio: V595 CWE-476 Pointer 'fragment.d' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 2238, 2241. qtextcursor.cpp 2238

Semua sama saja. Perhatikan urutan pekerjaan dengan pointer yang disimpan dalam fragmen variabel.

Kesalahan lain dari jenis ini:

  • V595 CWE-476 Pointer 'window' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 1846, 1848. qapplication.cpp 1846
  • V595 CWE-476 Pointer 'window' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 1858, 1860. qapplication.cpp 1858
  • V595 CWE-476 Pointer 'reply' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 492, 502. qhttpnetworkconnectionchannel.cpp 492
  • V595 CWE-476 Pointer 'newHandle' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 877, 883. qsplitter.cpp 877
  • V595 CWE-476 Pointer 'widget' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 2320, 2322. qwindowsvistastyle.cpp 2320
  • Bahkan, ada lebih banyak kesalahan. Dengan cepat aku bosan mempelajari peringatan V595, dan untuk artikel itu aku sudah menulis cukup banyak fragmen kode.

Cacat N22-N33

Ada kode di mana pointer diperiksa bahwa operator baru kembali. Ini sangat lucu di tengah fakta bahwa ada banyak tempat di mana hasil dari fungsi malloc tidak diperiksa (lihat kelompok kesalahan berikut).

 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; } .... } 

Peringatan PVS-Studio: V668 CWE-571 Tidak ada gunanya menguji pointer 'd-> unmap Pointer' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. qtranslator.cpp 596

Memeriksa pointer tidak masuk akal, karena jika terjadi kesalahan alokasi memori, pengecualian std :: bad_alloc akan dibuang . Jika Anda ingin operator baru mengembalikan nullptr ketika tidak ada cukup memori, maka Anda harus menulis:

 d->unmapPointer = new (std::nothrow) char[d->unmapLength]; 

Penganalisa tahu tentang penggunaan operator baru ini dan tidak akan memberikan peringatan dalam kasus ini.

Kesalahan lainnya: Saya akan memberi mereka file qt-V668.txt .

Cacat N34-N70

Seperti yang dijanjikan, sekarang giliran kesalahan ketika mereka tidak memeriksa hasil dari memanggil fungsi malloc , calloc , strdup , dll. Kesalahan ini lebih serius daripada yang terlihat pada pandangan pertama. Lebih detail: " Mengapa penting untuk memeriksa apa fungsi malloc dikembalikan ."

 SourceFiles::SourceFiles() { nodes = (SourceFileNode**)malloc(sizeof(SourceFileNode*)*(num_nodes=3037)); for(int n = 0; n < num_nodes; n++) nodes[n] = nullptr; } 

Peringatan PVS-Studio: V522 CWE-690 Mungkin ada dereferensi 'simpul' penunjuk null potensial. Periksa baris: 138, 136. makefiledeps.cpp 138

Pointer digunakan tanpa verifikasi sebelumnya.

Semua kesalahan ini bertipe sama, jadi saya tidak akan membahasnya lebih detail. Saya akan memberikan sisa daftar peringatan: qt-V522-V575.txt .

Kesalahan logis dalam kondisi


Cacat N71

 QString QEdidParser::parseEdidString(const quint8 *data) { QByteArray buffer(reinterpret_cast<const char *>(data), 13); // Erase carriage return and line feed buffer = buffer.replace('\r', '\0').replace('\n', '\0'); // Replace non-printable characters with dash for (int i = 0; i < buffer.count(); ++i) { if (buffer[i] < '\040' && buffer[i] > '\176') buffer[i] = '-'; } return QString::fromLatin1(buffer.trimmed()); } 

Peringatan PVS-Studio: V547 CWE-570 Ekspresi 'buffer [i] <' \ 040 '&& buffer [i]>' \ 176 '' selalu salah. qedidparser.cpp 169

Fungsi harus melakukan tindakan berikut "Ganti karakter yang tidak dapat dicetak dengan tanda hubung". Namun, dia tidak melakukannya. Mari kita perhatikan lebih dekat kondisi ini:

 if (buffer[i] < '\040' && buffer[i] > '\176') 

Itu tidak masuk akal. Karakter tidak boleh kurang dari '\ 040' dan lebih besar dari '\ 176' secara bersamaan. Dalam kondisi Anda harus menggunakan operator '||'. Kode yang benar adalah:

 if (buffer[i] < '\040' || buffer[i] > '\176') 

Cacat N72

Kesalahan serupa, karena pengguna Windows tidak beruntung.

 #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 

Penganalisa menghasilkan dua peringatan sekaligus:

  • V590 CWE-571 Pertimbangkan untuk memeriksa ekspresi 'c <' a '&& c>' z '&& c <' A '&& c>' Z '. Ekspresi berlebihan atau mengandung kesalahan cetak. qdir.cpp 77
  • V560 CWE-570 Bagian dari ekspresi kondisional selalu salah: c> 'z'. qdir.cpp 77

Kesalahan logis dalam kondisi:

 if (c < 'a' && c > 'z' && c < 'A' && c > 'Z') 

Seperti yang saya pahami, programmer ingin menemukan karakter yang bukan huruf alfabet Latin. Dalam hal ini, kondisinya harus seperti ini:

 if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) 

Cacat N73

 enum SelectionMode { NoSelection, SingleSelection, MultiSelection, ExtendedSelection, ContiguousSelection }; void QAccessibleTableCell::unselectCell() { QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); if (!m_index.isValid() || (selectionMode & QAbstractItemView::NoSelection)) return; .... } 

Peringatan PVS-Studio: V616 CWE-480 The 'QAbstractItemView :: NoSelection' dinamai konstan dengan nilai 0 digunakan dalam operasi bitwise. itemviews.cpp 976

Konstanta QAbstractItemView :: NoSelection bernama adalah nol. Oleh karena itu, subekspresi (selectionMode & QAbstractItemView :: NoSelection) tidak masuk akal. Itu akan selalu 0.

Saya pikir itu harus ditulis di sini:

 if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection)) 

Cacat N74

Kode berikut sulit bagi saya untuk mengerti. Dia salah, tapi aku tidak tahu harus jadi apa dia. Mengomentari suatu fungsi juga tidak membantu saya.

 // Re-engineered from the inline function _com_error::ErrorMessage(). // We cannot use it directly since it uses swprintf_s(), which is not // present in the MSVCRT.DLL found on Windows XP (QTBUG-35617). static inline QString errorMessageFromComError(const _com_error &comError) { TCHAR *message = nullptr; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, DWORD(comError.Error()), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), message, 0, NULL); if (message) { const QString result = QString::fromWCharArray(message).trimmed(); LocalFree(static_cast<HLOCAL>(message)); return result; } if (const WORD wCode = comError.WCode()) return QString::asprintf("IDispatch error #%u", uint(wCode)); return QString::asprintf("Unknown error 0x0%x", uint(comError.Error())); } 

Peringatan PVS-Studio: 'pesan' Ekspresi V547 CWE-570 selalu salah. qwindowscontext.cpp 802

Pemrogram mungkin berasumsi bahwa fungsi FormatMessage akan mengubah nilai penunjuk pesan . Tapi ini tidak benar. Fungsi FormatMessage tidak dapat mengubah nilai pointer, karena diteruskan ke fungsi berdasarkan nilai. Berikut adalah prototipe dari fungsi ini:

 DWORD __stdcall FormatMessageW( DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, va_list *Arguments ); 


Kebocoran memori potensial


Cacat 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; } }; 

Peringatan PVS-Studio: V701 CWE-401 realloc () kemungkinan kebocoran: ketika realloc () gagal mengalokasikan memori, penunjuk asli 'anak-anak' hilang. Pertimbangkan untuk menetapkan realloc () ke pointer sementara. makefiledeps.cpp 103

Perluasan buffer dilakukan dengan cara yang berbahaya. Jika fungsi realokasi tidak dapat mengalokasikan memori, itu akan mengembalikan NULL . NULL ini akan segera ditempatkan dalam variabel anak - anak dan tidak akan ada kemungkinan untuk membebaskan buffer yang dialokasikan sebelumnya. Kebocoran memori akan terjadi.

Kesalahan serupa: qt-701.txt .

Lain-lain


Cacat 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))) { .... } 

Peringatan PVS-Studio: V610 CWE-758 Perilaku tidak spesifik. Periksa operator shift '>>'. Operan kiri '(- 2147483647 - 1)' negatif. qdrawhelper.cpp 4015

Nilai negatif INT_MIN tidak dapat digeser. Ini adalah perilaku yang tidak ditentukan, dan Anda tidak dapat mengandalkan hasil dari operasi semacam itu. Bit paling signifikan bisa sama dengan 0 atau 1.

Cacat N94

 void QObjectPrivate::addConnection(int signal, Connection *c) { .... if (signal >= connectionLists->count()) connectionLists->resize(signal + 1); ConnectionList &connectionList = (*connectionLists)[signal]; .... if (signal < 0) { .... } 

Peringatan PVS-Studio: V781 CWE-129 Nilai dari variabel 'sinyal' diperiksa setelah digunakan. Mungkin ada kesalahan dalam logika program. Periksa baris: 397, 413. qobject.cpp 397

Pemeriksaan (sinyal <0) menunjukkan bahwa nilai argumen sinyal mungkin negatif. Namun, argumen ini sebelumnya digunakan untuk mengindeks array. Ternyata pemeriksaan dilakukan terlambat. Program sudah akan terganggu.

Cacat 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 Warning: V519 CWE-563 Variabel 'lastNamespaceDeclaration' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 3188, 3194. qxmlstream.cpp 3194

Saya akan menyoroti esensi kesalahan:

 if (inEmptyElement) { lastNamespaceDeclaration = tag.namespaceDeclarationsSize; } lastNamespaceDeclaration = namespaceDeclarations.size(); 

Cacat N96

 void QRollEffect::scroll() { .... if (currentHeight != totalHeight) { currentHeight = totalHeight * (elapsed/duration) + (2 * totalHeight * (elapsed%duration) + duration) / (2 * duration); // equiv. to int((totalHeight*elapsed) / duration + 0.5) done = (currentHeight >= totalHeight); } done = (currentHeight >= totalHeight) && (currentWidth >= totalWidth); .... } 

V519 CWE-563 Variabel 'selesai' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 509, 511. qeffects.cpp 511

Semuanya sama seperti pada kasus sebelumnya. Perhatikan variabel yang dilakukan .

Kesimpulan


Bahkan melihat melalui laporan itu secara dangkal, saya menulis hampir 100 kesalahan. Saya senang dengan hasil PVS-Studio.

Tentu saja, pemeriksaan kode langka semacam itu tidak ada hubungannya dengan peningkatan kualitas dan keandalan kode. Mereka hanya menunjukkan kemampuan penganalisa kode. Alat analisis statis harus diterapkan secara teratur. Dalam hal ini, mereka mengurangi biaya memperbaiki bug dan melindungi aplikasi dari banyak kerentanan potensial.

Terima kasih atas perhatian anda Untuk mengikuti publikasi baru kami, saya mengundang Anda untuk berlangganan salah satu saluran kami:
  1. VK.com : pvsstudio_rus
  2. "Sekolah tua" RSS: viva64-blog-ru
  3. Twitter: @pvsstudio_rus
  4. Instagram: @pvsstudio_rus
  5. Telegram: @pvsstudio_rus



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Andrey Karpov. Pemeriksaan Ketiga Qt 5 dengan PVS-Studio

Source: https://habr.com/ru/post/id426485/


All Articles