Troisième test Qt 5 avec PVS-Studio

PVS-Studio & Qt

De temps en temps, notre équipe revérifie les projets sur lesquels nous avons déjà écrit des articles. Un autre projet revérifié était Qt. La dernière fois que nous l'avons testé avec PVS-Studio en 2014. Depuis 2014, le projet a commencé à être régulièrement contrôlé avec l'aide de Coverity. C'est intéressant. Voyons si nous pouvons maintenant trouver des erreurs intéressantes en utilisant PVS-Studio.

Qt


Articles précédents:


Cette fois, Qt Base (Core, Gui, Widgets, Network, ...) et le super module Qt5 ont été testés . À propos de Qt Creator, nous prévoyons d'écrire un article séparé plus tard. Pour vérification, nous avons utilisé l'analyseur statique PVS-Studio, dont vous pouvez télécharger une version d'essai sur le site.

À mon avis, le code Qt est devenu meilleur. Au fil des ans depuis le dernier test, de nombreux nouveaux diagnostics sont apparus dans l'analyseur PVS-Studio. Malgré cela, lors de l'examen des avertissements, je n'ai pas trouvé autant d'erreurs pour un projet de cette taille. Je répète encore une fois que c'est mon impression individuelle. Je n'ai pas fait de recherche spéciale sur la densité des erreurs à ce moment-là ou maintenant.

Très probablement, des vérifications régulières à l'aide de l'analyseur statique Coverity ont très probablement affecté la qualité du code. En 2014, avec l'aide de Coverity, le projet Qt ( qt-project ) a commencé à être vérifié, et en 2016, le Qt Creator ( qt-creator ). Mon avis: si vous développez un projet ouvert, Coverity Scan peut être une bonne solution gratuite qui améliorera considérablement la qualité et la fiabilité de vos projets.

Cependant, comme le lecteur peut le deviner, si je n'avais rien remarqué d'intéressant dans le rapport PVS-Studio, il n'y aurait pas eu d'article :). Et puisqu'il y a un article, c'est-à-dire des défauts. Regardons-les. Au total, j'ai écrit 96 erreurs.

Copier-coller et Typos infructueux


Commençons par les classiques du genre, lorsque la cause de l'erreur est l'inattention. Ces erreurs sont sous-estimées par les programmeurs. Pour ceux qui ne l'ont pas encore lu, je vous recommande de lire ces deux articles:


Ces erreurs sont interlangues. Par exemple, le deuxième article donne de nombreux exemples d'erreurs dans les fonctions de comparaison écrites en C, C ++ et C #. Maintenant, lors de l'implémentation de la prise en charge du langage Java dans PVS-Studio, nous rencontrons les mêmes modèles d'erreur. Voici, par exemple, un bug que nous avons récemment trouvé dans la bibliothèque 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; } } 

Si vous regardez attentivement, il s'avère que le champ PurchaseSequence est comparé à lui-même. L'option correcte:

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

En général, tout est comme toujours, et l'analyseur PVS-Studio devra "ratisser les écuries Augean" dans les projets Java. Soit dit en passant, nous invitons tout le monde à prendre part aux tests de la version bêta de PVS-Studio pour Java, qui devrait apparaître prochainement. Pour ce faire, écrivez-nous (sélectionnez "Je veux l'analyseur pour Java").

Revenons maintenant aux erreurs du projet Qt.

Défaut N1

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

Avertissement PVS-Studio: V501 CWE-571 Il existe des sous-expressions identiques «QWindowsContext :: user32dll.getWindowDpiAwarenessContext» à gauche et à droite de l'opérateur «&&». qwindowscontext.cpp 150

Aucune explication particulière en plus du message de l'analyseur n'est requise ici. Il me semble que l'expression aurait dû être comme ceci:

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

Défaut N2, N3

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

Avertissement PVS-Studio: V501 CWE-571 Il existe des sous-expressions identiques à gauche et à droite de l'opérateur '&&':! WaitingReaders &&! WaitingReaders qreadwritelock.cpp 632

L'erreur se trouve dans la condition de macro Q_ASSERT , elle n'est donc pas significative. Mais encore, c'est une erreur. La variable waitReaders est vérifiée deux fois. Et apparemment, ils ont oublié de vérifier une autre variable.

Une erreur identique se trouve dans la ligne 625 du fichier qreadwritelock.cpp. Vive le copier-coller! :)

Défaut 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 Warning: V523 CWE-691 L'instruction 'then' est équivalente à l'instruction 'else'. qgraphicsscene_bsp.cpp 179

Très probablement, le bloc de texte a été copié, mais ils ont oublié de le corriger.

Défaut N5

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

Avertissement PVS-Studio: V768 CWE-571 La constante d'énumération 'WindingFill' est utilisée comme variable de type booléen. qpainterpath.cpp 2479

D'accord, c'est un beau bêtisier! Q_ASSERT ne vérifie rien, car la condition est toujours vraie. La condition est vraie car la constante nommée Qt :: WindingFill est 1.

Défaut 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; .... } 

Avant de lire l'avertissement, essayez de repérer une faute de frappe vous-même. En ajoutant une image, je vous aiderai à ne pas lire immédiatement le message de l'analyseur :).

Il est temps de réfléchir


Avertissement PVS-Studio: V560 CWE-570 Une partie de l'expression conditionnelle est toujours fausse: currentType == QMetaType :: Char. qvariant.cpp 3529

La condition "currentType == QMetaType :: Char" est vérifiée dans le premier if . Si la condition est remplie, la variable currentType reçoit la valeur QMetaType :: UInt . Par conséquent, la variable currentType ne peut plus être égale à QMetaType :: Char . Par conséquent, l'analyseur signale que dans le second if, la sous - expression "currentType == QMetaType :: Char" est toujours fausse.

En fait, le deuxième if devrait être comme ceci:

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


Note de diagnostic V560

Le rapport a trouvé de nombreux avertissements V560. Cependant, je ne les ai plus regardés dès que j'ai trouvé un cas intéressant pour l'article, qui était considéré ci-dessus comme un défaut N6.

La grande majorité des messages V560 ne peuvent pas être appelés faux, mais ils ne servent à rien. En d'autres termes, les décrire dans un article n'est pas intéressant. Pour bien comprendre ce que je veux dire exactement, considérons un de ces cas.

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

Avertissement PVS-Stuidio: V560 CWE-571 Une partie de l'expression conditionnelle est toujours vraie: doc. qtextdocument.cpp 2992

L'analyseur est absolument correct que le pointeur de doc n'est pas toujours nullptr quand il est revérifié. Mais ce n'est pas une erreur, juste le programmeur était en sécurité. Vous pouvez simplifier le code en écrivant:

 if (doc->docHandle()) { 

Défaut N7

Et le dernier cas, qui peut être classé comme une faute de frappe. L'erreur se produit en raison d'une confusion dans les noms des constantes, qui ne diffèrent que dans le cas de la première lettre.

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

Avertissement PVS-Studio: V616 CWE-480 La constante nommée 'CursorShowing' avec la valeur 0 est utilisée dans l'opération au niveau du bit. qwindowscursor.cpp 669

Plus en détail, j'ai déjà analysé cette erreur dans une petite note séparée: " Encore une fois, l'analyseur PVS-Studio s'est avéré être plus attentif qu'une personne ."

Failles de sécurité


En fait, toutes les erreurs discutées dans cet article peuvent être appelées défauts de sécurité. Tous sont classés selon l' énumération commune des faiblesses (voir CWE ID dans les messages de l'analyseur). Si les erreurs sont classées comme CWE, elles constituent potentiellement un risque pour la sécurité. Ceci est expliqué plus en détail sur la page PVS-Studio SAST .

Cependant, je voudrais signaler un certain nombre d'erreurs dans un groupe distinct. Jetons-y un œil.

Défaut N8, N9

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

Avertissements de PVS-Studio:

  • V530 CWE-252 La valeur de retour de la fonction 'SetSecurityDescriptorOwner' doit être utilisée. qlocalserver_win.cpp 167
  • V530 CWE-252 La valeur de retour de la fonction 'SetSecurityDescriptorGroup' doit être utilisée. qlocalserver_win.cpp 168

Il existe différentes fonctions liées au contrôle d'accès. Les fonctions SetSecurityDescriptorOwner et SetSecurityDescriptorGroup en font partie.

Avec de telles fonctions, vous devez travailler très soigneusement. Par exemple, vous devez vérifier le statut qu'ils renvoient. Que se passe-t-il si l'appel à ces fonctions échoue? Deviner n'est pas nécessaire, il est nécessaire d'écrire du code pour gérer un tel cas.

Il n'est pas nécessaire de tirer parti du manque de vérification et de transformer ces erreurs en vulnérabilités. Cependant, ce n'est en aucun cas un lieu de risque, et vous devez écrire du code plus sécurisé.

Défaut N10

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

PVS-Studio Warning: V530 CWE-252 La valeur de retour de la fonction 'InitializeAcl' doit être utilisée. qlocalserver_win.cpp 144

La situation est similaire à celle évoquée ci-dessus.

Défaut N11, N12

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

Avertissement PVS-Studio: V597 CWE-14 Le compilateur peut supprimer l'appel de fonction 'memset', qui est utilisé pour vider le tampon 'chunkBuffer'. La fonction RtlSecureZeroMemory () doit être utilisée pour effacer les données privées. sha1.cpp 189

Le compilateur supprimera l'appel de fonction memset . Déjà plusieurs fois dans des articles, j'ai analysé cette situation. Je n'ai pas envie de me répéter. Je me réfère à l'article " Nettoyage sécurisé des données privées ".

Et une autre erreur se trouve dans le même fichier sha1.cpp, à la ligne 247.

Pointeurs nuls


Il est temps de parler de pointeurs. Il y a eu beaucoup d'erreurs sur ce sujet.

Défaut N13

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

Avertissement PVS-Studio: V595 CWE-476 Le pointeur 'str' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 2118, 2119. qbytearray.cpp 2118

La situation classique est lorsqu'un pointeur est utilisé au début, puis vérifié l'égalité nullptr . Il s'agit d'un modèle d'erreur très courant, et nous le constatons régulièrement dans presque tous les projets.

Défaut 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; } 

Avertissement PVS-Studio: V595 CWE-476 Le pointeur 'mobj' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 2671, 2672. qmetaobject.cpp 2671

Au cas où, j'apporte le corps de la fonction privée . Pour une raison quelconque, les lecteurs commencent parfois à trouver des situations dans lesquelles le code fonctionnera. Je ne comprends pas d'où vient cette méfiance et le désir de voir une fonctionnalité délicate par erreur :). Par exemple, quelqu'un peut suggérer dans les commentaires que priv est une macro du formulaire:

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

Ensuite, tout fonctionnera.

Afin d'éviter de telles discussions, j'essaie de citer des fragments de code où toutes les informations confirmant l'existence d'une erreur sont fournies.

Ainsi, le pointeur modj est déréférencé puis vérifié.

Plus loin sur la scène vient le copier-coller «puissant et terrible». En raison de la détection exacte de la même erreur dans la fonction isScoped :

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

Avertissement PVS-Studio: V595 CWE-476 Le pointeur 'mobj' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 2683, 2684. qmetaobject.cpp 2683

Défaut N16-N21

Prenons un autre exemple et, je pense, assez.

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

Avertissement PVS-Studio: V595 CWE-476 Le pointeur 'fragment.d' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 2238, 2241. qtextcursor.cpp 2238

Tout de même. Faites attention à la séquence de travail avec le pointeur stocké dans la variable fragment.d .

Autres erreurs de ce type:

  • V595 CWE-476 Le pointeur "fenêtre" a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 1846, 1848. qapplication.cpp 1846
  • V595 CWE-476 Le pointeur "fenêtre" a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 1858, 1860. qapplication.cpp 1858
  • V595 CWE-476 Le pointeur de «réponse» a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 492, 502. qhttpnetworkconnectionchannel.cpp 492
  • V595 CWE-476 Le pointeur 'newHandle' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 877, 883. qsplitter.cpp 877
  • V595 CWE-476 Le pointeur "widget" a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 2320, 2322. qwindowsvistastyle.cpp 2320
  • En fait, il y a plus d'erreurs. Je me suis rapidement fatigué d'apprendre les avertissements de la V595, et pour l'article, j'ai déjà écrit suffisamment de fragments de code.

Défaut N22-N33

Il y a du code où un pointeur est vérifié que le nouvel opérateur retourne. C'est particulièrement drôle au milieu du fait qu'il y a beaucoup d'endroits où le résultat de la fonction malloc n'est pas vérifié (voir le groupe d'erreurs suivant).

 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 Warning: V668 CWE-571 Il est inutile de tester le pointeur 'd-> unmap Pointer' contre null, car la mémoire a été allouée à l'aide de l'opérateur 'new'. L'exception sera générée en cas d'erreur d'allocation de mémoire. qtranslator.cpp 596

Vérifier le pointeur n'a pas de sens, car en cas d'erreur d'allocation de mémoire, une exception std :: bad_alloc sera levée . Si vous voulez que le nouvel opérateur retourne nullptr quand il n'y a pas assez de mémoire, alors vous devez écrire:

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

L'analyseur connaît cette utilisation du nouvel opérateur et ne donnerait pas d'avertissement dans ce cas.

Autres erreurs: je leur donnerai le fichier qt-V668.txt .

Défaut N34-N70

Comme promis, c'est maintenant le tour des erreurs quand ils ne vérifient pas le résultat de l'appel des fonctions malloc , calloc , strdup , etc. Ces erreurs sont plus graves qu'il n'y paraît à première vue. Plus de détails: " Pourquoi est-il important de vérifier ce que la fonction malloc a renvoyé ."

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

Avertissement PVS-Studio: V522 CWE-690 Il peut y avoir un déréférencement d'un "noeud" de pointeur nul potentiel. Vérifiez les lignes: 138, 136. makefiledeps.cpp 138

Le pointeur est utilisé sans vérification préalable.

Toutes ces erreurs sont du même type, je ne m'étendrai donc pas sur elles plus en détail. Je donnerai le reste de la liste d'avertissement: qt-V522-V575.txt .

Erreurs logiques dans les conditions


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

Avertissement PVS-Studio: V547 CWE-570 Expression 'buffer [i] <' \ 040 '&& buffer [i]>' \ 176 '' est toujours false. qedidparser.cpp 169

La fonction doit effectuer l'action suivante «Remplacer les caractères non imprimables par un tiret». Mais ce n'est pas le cas. Examinons de plus près cette condition:

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

Cela n'a aucun sens. Un caractère ne peut pas être inférieur à '\ 040' et supérieur à '\ 176' en même temps. Dans la condition, vous devez utiliser l'opérateur '||'. Le bon code est:

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

Défaut N72

Une erreur similaire, à cause de laquelle les utilisateurs de Windows n'ont pas de chance.

 #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 

L'analyseur génère deux avertissements à la fois:

  • V590 CWE-571 Envisagez d'inspecter l'expression 'c <' a '&& c>' z '&& c <' A '&& c>' Z ''. L'expression est excessive ou contient une erreur d'impression. qdir.cpp 77
  • V560 CWE-570 Une partie de l'expression conditionnelle est toujours fausse: c> 'z'. qdir.cpp 77

Une erreur logique est dans la condition:

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

Si je comprends bien, le programmeur voulait trouver un caractère qui n'est pas une lettre de l'alphabet latin. Dans ce cas, la condition doit être la suivante:

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

Défaut N73

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

Avertissement PVS-Studio: V616 CWE-480 La constante nommée 'QAbstractItemView :: NoSelection' avec la valeur 0 est utilisée dans l'opération au niveau du bit. itemviews.cpp 976

La constante nommée QAbstractItemView :: NoSelection est nulle. Par conséquent, la sous-expression (selectionMode & QAbstractItemView :: NoSelection) n'a pas de sens. Ce sera toujours 0.

Je pense que cela devrait être écrit ici:

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

Défaut N74

Le code suivant est difficile à comprendre pour moi. Il a tort, mais je ne sais pas ce qu'il devrait être. Commenter une fonction ne m'aide pas non plus.

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

PVS-Studio Warning: V547 CWE-570 Expression 'message' is always false. qwindowscontext.cpp 802

Le programmeur suppose probablement que la fonction FormatMessage modifiera la valeur du pointeur de message . Mais ce n'est pas le cas. La fonction FormatMessage ne peut pas modifier la valeur d'un pointeur, car elle est transmise à la fonction par valeur. Voici un prototype de cette fonction:

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


Fuites potentielles de mémoire


Défaut 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; } }; 

Avertissement PVS-Studio: V701 CWE-401 possible fuite de realloc (): lorsque realloc () échoue dans l'allocation de mémoire, le pointeur d'origine «enfants» est perdu. Pensez à affecter realloc () à un pointeur temporaire. makefiledeps.cpp 103

L'expansion du tampon est implémentée de manière dangereuse. Si la fonction realloc ne peut pas allouer de mémoire, elle renverra NULL . Ce NULL sera immédiatement placé dans la variable enfants et il n'y aura aucune possibilité de libérer le tampon alloué plus tôt. Une fuite de mémoire se produira.

Erreurs similaires: qt-701.txt .

Divers


Défaut 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))) { .... } 

Avertissement PVS-Studio: V610 CWE-758 Comportement non spécifié. Vérifiez l'opérateur de décalage '>>'. L'opérande gauche «(- 2147483647 - 1)» est négatif. qdrawhelper.cpp 4015

La valeur négative de INT_MIN ne peut pas être décalée. Il s'agit d'un comportement non spécifié et vous ne pouvez pas vous fier au résultat d'une telle opération. Les bits les plus significatifs peuvent être égaux à 0 ou 1.

Défaut 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 Warning: V781 CWE-129 La valeur de la variable 'signal' est vérifiée après son utilisation. Il y a peut-être une erreur dans la logique du programme. Vérifiez les lignes: 397, 413. qobject.cpp 397

Une vérification (signal <0) indique que la valeur de l'argument signal peut être négative. Cependant, cet argument était précédemment utilisé pour indexer le tableau. Il s'avère que la vérification est effectuée trop tard. Le programme sera déjà interrompu.

Défaut 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; } 

Avertissement PVS-Studio: V519 CWE-563 La variable 'lastNamespaceDeclaration' reçoit des valeurs successives deux fois. C'est peut-être une erreur. Vérifiez les lignes: 3188, 3194. qxmlstream.cpp 3194

Je vais souligner l'essence de l'erreur:

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

Défaut 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 La variable 'done' reçoit des valeurs successives deux fois. C'est peut-être une erreur. Vérifiez les lignes: 509, 511. qeffects.cpp 511

Tout est le même que dans le cas précédent. Notez la variable done .

Conclusion


Même en regardant superficiellement le rapport, j'ai écrit près de 100 erreurs. Je suis satisfait des résultats de PVS-Studio.

Bien sûr, ces vérifications de code rares n'ont rien à voir avec l'amélioration de la qualité et de la fiabilité du code. Ils ne montrent que les capacités de l'analyseur de code. Des outils d'analyse statique doivent être appliqués régulièrement. Dans ce cas, ils réduisent le coût de la correction des bogues et protègent les applications de nombreuses vulnérabilités potentielles.

Merci de votre attention. Pour être au courant de nos nouvelles publications, je vous invite à vous abonner à l'une de nos chaînes:
  1. VK.com: pvsstudio_rus
  2. «Old school» RSS: viva64-blog-ru
  3. Twitter: @pvsstudio_rus
  4. Instagram: @pvsstudio_rus
  5. Télégramme: @pvsstudio_rus



Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Andrey Karpov. Une troisième vérification de Qt 5 avec PVS-Studio

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


All Articles