Terceiro teste do Qt 5 com o PVS-Studio

PVS-Studio e Qt

De tempos em tempos, nossa equipe verifica novamente os projetos sobre os quais já escrevemos artigos. Outro projeto verificado novamente foi o Qt. A última vez que testamos com o PVS-Studio em 2014. Desde 2014, o projeto começou a ser verificado regularmente com a ajuda da Coverity. Isso é interessante. Vamos ver se agora podemos encontrar erros interessantes usando o PVS-Studio.

Qt


Artigos anteriores:


Desta vez, o Qt Base (Core, Gui, Widgets, Rede, ...) e o super módulo Qt5 foram testados . Sobre o Qt Creator, planejamos escrever um artigo separado posteriormente. Para verificação, usamos o analisador estático PVS-Studio, cuja versão de teste pode ser baixada do site.

Na minha opinião subjetiva, o código Qt se tornou melhor. Ao longo dos anos desde o último teste, muitos novos diagnósticos apareceram no analisador PVS-Studio. Apesar disso, durante o estudo de revisão dos avisos, não encontrei muitos erros para um projeto desse tamanho. Repito mais uma vez que esta é minha impressão individual. Não fiz nenhuma pesquisa especial sobre a densidade de erros naquele momento ou agora.

Provavelmente, verificações regulares usando o analisador estático do Coverity provavelmente afetaram a qualidade do código. Em 2014, com a ajuda do Coverity, o projeto Qt ( qt-project ) começou a ser verificado e, em 2016, o Qt Creator ( qt-creator ). Minha opinião: se você estiver desenvolvendo um projeto de código aberto, o Coverity Scan pode ser uma boa solução gratuita que melhorará significativamente a qualidade e a confiabilidade de seus projetos.

No entanto, como o leitor pode adivinhar, se eu não tivesse notado algo interessante no relatório do PVS-Studio, não haveria artigo :). E como existe um artigo, isto é, defeitos. Vamos olhar para eles. No total, escrevi 96 erros.

Copiar e colar malsucedido


Vamos começar com os clássicos do gênero, quando a causa do erro é a falta de atenção. Esses erros são subestimados pelos programadores. Para quem ainda não leu, recomendo que você analise estes dois artigos:


Esses erros são interlíngua. Por exemplo, o segundo artigo fornece muitos exemplos de erros nas funções de comparação escritas em C, C ++ e C #. Agora, ao implementar o suporte à linguagem Java no PVS-Studio, encontramos os mesmos padrões de erro. Aqui, por exemplo, está um erro que encontramos recentemente na biblioteca do 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; } } 

Se você observar atentamente, o campo purchaseSequence será comparado a si mesmo. A opção correta:

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

Em geral, tudo está como sempre, e o analisador PVS-Studio terá que "vasculhar os estábulos augianos" em projetos Java. A propósito, convidamos todos a participar do teste da versão Beta do PVS-Studio for Java, que deve aparecer em um futuro próximo. Para fazer isso, escreva-nos (selecione "Quero o analisador para Java").

Agora, de volta aos erros no projeto Qt.

Defeito N1

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

PVS-Studio Warning: V501 CWE-571 Existem subexpressões idênticas 'QWindowsContext :: user32dll.getWindowDpiAwarenessContext' à esquerda e à direita do operador '&&'. qwindowscontext.cpp 150

Nenhuma explicação especial além da mensagem do analisador não é necessária aqui. Parece-me que a expressão deveria ter sido assim:

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

Defeito N2, N3

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

PVS-Studio Warning: V501 CWE-571 Existem subexpressões idênticas à esquerda e à direita do operador '&&' :! WaitingReaders &&! WaitingReaders qreadwritelock.cpp 632

O erro está dentro da condição da macro Q_ASSERT , portanto, não é significativo. Mas ainda assim, isso é um erro. A variável waitingReaders é verificada duas vezes. E, aparentemente, eles se esqueceram de verificar alguma outra variável.

Um erro idêntico foi encontrado na linha 625 do arquivo qreadwritelock.cpp. Vida longa Copiar e colar! :)

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

Aviso do PVS-Studio: V523 CWE-691 A instrução 'then' é equivalente à instrução 'else'. qgraphicsscene_bsp.cpp 179

Provavelmente, o bloco de texto foi copiado, mas eles esqueceram de corrigi-lo.

Defeito 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 A constante de enumeração 'WindingFill' é usada como uma variável de um tipo booleano. qpainterpath.cpp 2479

Concordo, este é um belo erro de gravação! Q_ASSERT não verifica nada, pois a condição é sempre verdadeira. A condição é verdadeira porque a constante nomeada Qt :: WindingFill é 1.

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

Antes de ler o aviso, tente identificar um erro de digitação. Ao adicionar uma imagem, ajudarei você a não ler a mensagem do analisador imediatamente :).

Hora de pensar


Aviso do PVS-Studio: V560 CWE-570 Uma parte da expressão condicional é sempre falsa: currentType == QMetaType :: Char. qvariant.cpp 3529

A condição "currentType == QMetaType :: Char" é verificada no primeiro se . Se a condição for atendida, a variável currentType receberá o valor QMetaType :: UInt . Portanto, a variável currentType não pode mais ser igual a QMetaType :: Char . Portanto, o analisador relata que, no segundo caso, a subexpressão "currentType == QMetaType :: Char" é sempre falsa.

De fato, o segundo se deve ser assim:

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


Nota de diagnóstico do V560

O relatório encontrou muitos avisos do V560. No entanto, não os observei mais assim que encontrei um caso interessante para o artigo, que foi considerado acima como um defeito N6.

A grande maioria das mensagens V560 não pode ser chamada de falsa, mas não há uso delas. Em outras palavras, descrevê-los em um artigo não é interessante. Para deixar claro o que exatamente eu quero dizer, considere um desses casos.

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

Aviso PVS-Stuidio: V560 CWE-571 Uma parte da expressão condicional é sempre verdadeira: doc. qtextdocument.cpp 2992

O analisador está absolutamente correto que o ponteiro do documento nem sempre é nullptr quando é verificado novamente. Mas isso não é um erro, apenas o programador estava seguro. Você pode simplificar o código escrevendo:

 if (doc->docHandle()) { 

Defeito N7

E o último caso, que pode ser classificado como erro de digitação. O erro ocorre devido a confusão nos nomes das constantes, que diferem apenas no caso da primeira letra.

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

Aviso do PVS-Studio: V616 CWE-480 A constante nomeada 'CursorShowing' com o valor 0 é usada na operação bit a bit. qwindowscursor.cpp 669

Mais detalhadamente, eu já analisei esse erro em uma pequena nota separada: " Mais uma vez, o analisador PVS-Studio mostrou-se mais atento que uma pessoa ".

Falhas de segurança


De fato, todos os erros discutidos neste artigo podem ser chamados de defeitos de segurança. Todos eles são classificados de acordo com a Enumeração de fraqueza comum (consulte ID do CWE nas mensagens do analisador). Se os erros são classificados como CWEs, eles são potencialmente um risco de segurança. Isso é explicado em mais detalhes na página PVS-Studio SAST .

No entanto, gostaria de destacar uma série de erros em um grupo separado. Vamos dar uma olhada neles.

Defeito N8, N9

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

Avisos do PVS-Studio:

  • V530 CWE-252 O valor de retorno da função 'SetSecurityDescriptorOwner' deve ser utilizado. qlocalserver_win.cpp 167
  • V530 CWE-252 O valor de retorno da função 'SetSecurityDescriptorGroup' deve ser utilizado. qlocalserver_win.cpp 168

Existem várias funções relacionadas ao controle de acesso. As funções SetSecurityDescriptorOwner e SetSecurityDescriptorGroup estão entre elas.

Com essas funções, você precisa trabalhar com muito cuidado. Por exemplo, você deve verificar o status que eles retornam. O que acontece se a chamada para essas funções falhar? Adivinhar não é necessário, é necessário escrever código para lidar com esse caso.

Não é necessário tirar proveito da falta de verificação e transformar esses erros em vulnerabilidades. No entanto, este não é, em nenhum caso, um local de risco e você precisa escrever um código mais seguro.

Defeito N10

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

PVS-Studio Warning: V530 CWE-252 O valor de retorno da função 'InitializeAcl' deve ser utilizado. qlocalserver_win.cpp 144

A situação é semelhante à discutida acima.

Defeito N11, N12

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

PVS-Studio Warning: V597 CWE-14 O compilador pode excluir a chamada de função 'memset', usada para liberar o buffer 'chunkBuffer'. A função RtlSecureZeroMemory () deve ser usada para apagar os dados particulares. sha1.cpp 189

O compilador removerá a chamada de função memset . Já muitas vezes em artigos analisei essa situação. Não tenho vontade de me repetir. Refiro-me ao artigo " Limpeza segura de dados particulares ".

E outro erro está no mesmo arquivo sha1.cpp, na linha 247.

Ponteiros nulos


É hora de falar sobre indicadores. Houve muitos erros neste tópico.

Defeito N13

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

Aviso do PVS-Studio: V595 CWE-476 O ponteiro 'str' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 2118, 2119. qbytearray.cpp 2118

A situação clássica é quando um ponteiro é usado no início e depois verificado quanto à igualdade nullptr . Esse é um padrão de erro muito comum e o vemos regularmente em quase todos os projetos.

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

Aviso do PVS-Studio: V595 CWE-476 O ponteiro 'mobj' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 2671, 2672. qmetaobject.cpp 2671

Por precaução, trago o corpo da função privada . Por alguma razão, às vezes os leitores começam a apresentar situações nas quais o código funcionará. Não entendo de onde vem essa desconfiança e o desejo de ver um recurso complicado por engano :). Por exemplo, alguém pode sugerir nos comentários que priv é uma macro do formulário:

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

Então tudo vai funcionar.

Para evitar essas discussões, tento citar fragmentos de código onde todas as informações que confirmam a existência de um erro são fornecidas.

Portanto, o ponteiro modj é desreferenciado e depois verificado.

Mais adiante, aparece o Copy-Paste "poderoso e terrível". Por causa do que exatamente o mesmo erro é detectado na função isScoped :

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

Aviso do PVS-Studio: V595 CWE-476 O ponteiro 'mobj' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 2683, 2684. qmetaobject.cpp 2683

Defeito N16-N21

Considere outro exemplo e, penso, suficiente.

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

Aviso do PVS-Studio: V595 CWE-476 O ponteiro 'fragment.d' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 2238, 2241. qtextcursor.cpp 2238

Tudo a mesma coisa. Preste atenção na sequência do trabalho com o ponteiro armazenado na variável fragment.d .

Outros erros deste tipo:

  • V595 CWE-476 O ponteiro da 'janela' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 1846, 1848. qapplication.cpp 1846
  • V595 CWE-476 O ponteiro da 'janela' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 1858, 1860. qapplication.cpp 1858
  • V595 CWE-476 O ponteiro 'reply' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 492, 502. qhttpnetworkconnectionchannel.cpp 492
  • V595 CWE-476 O ponteiro 'newHandle' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 877, 883. qsplitter.cpp 877
  • V595 CWE-476 O ponteiro 'widget' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 2320, 2322. qwindowsvistastyle.cpp 2320
  • De fato, há mais erros. Eu rapidamente me cansei de aprender os avisos do V595 e, para o artigo, já escrevi fragmentos de código suficientes.

Defeito N22-N33

Há um código no qual um ponteiro é verificado e o novo operador retorna. Isso é especialmente engraçado, devido ao fato de que existem muitos locais onde o resultado da função malloc não é verificado (consulte o grupo de erros a seguir).

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

Aviso do PVS-Studio: V668 CWE-571 Não há sentido em testar o ponteiro 'd-> unmap Pointer' contra nulo, pois a memória foi alocada usando o operador 'new'. A exceção será gerada no caso de erro de alocação de memória. qtranslator.cpp 596

A verificação do ponteiro não faz sentido, porque, no caso de um erro de alocação de memória, será lançada uma exceção std :: bad_alloc . Se você deseja que o novo operador retorne nullptr quando não houver memória suficiente, escreva:

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

O analisador conhece esse uso do novo operador e não emitirá um aviso nesse caso.

Outros erros: darei a eles o arquivo qt-V668.txt .

Defeito N34-N70

Como prometido, agora é a vez dos erros quando eles não verificam o resultado da chamada das funções malloc , calloc , strdup , etc. Esses erros são mais graves do que parecem à primeira vista. Mais detalhes: " Por que é importante verificar o que a função malloc retornou ."

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

Aviso do PVS-Studio: V522 CWE-690 Pode haver desreferenciação de um potencial 'nodo' de ponteiro nulo. Verifique as linhas: 138, 136. makefiledeps.cpp 138

O ponteiro é usado sem verificação prévia.

Todos esses erros são do mesmo tipo, por isso não vou me aprofundar neles com mais detalhes. Vou dar o restante da lista de aviso: qt-V522-V575.txt .

Erros lógicos nas condições


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

Aviso do PVS-Studio: V547 CWE-570 Expressão 'buffer [i] <' \ 040 '&& buffer [i]>' \ 176 '' é sempre falso. qedidparser.cpp 169

A função deve executar a seguinte ação “Substituir caracteres não imprimíveis pelo traço”. No entanto, ela não faz. Vamos dar uma olhada mais de perto nesta condição:

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

Isso não faz sentido. Um caractere não pode ser menor que '\ 040' e maior que '\ 176' ao mesmo tempo. Na condição, você deve usar o operador '||'. O código correto é:

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

Defeito N72

Um erro semelhante, devido ao qual os usuários do Windows não têm sorte.

 #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 

O analisador gera dois avisos ao mesmo tempo:

  • V590 CWE-571 Considere examinar a expressão 'c <' a '&& c>' z '&& c <' A '&& c>' Z ''. A expressão é excessiva ou contém uma impressão incorreta. qdir.cpp 77
  • V560 CWE-570 Uma parte da expressão condicional é sempre falsa: c> 'z'. qdir.cpp 77

Um erro lógico está na condição:

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

Pelo que entendi, o programador queria encontrar um caractere que não seja uma letra do alfabeto latino. Nesse caso, a condição deve ser assim:

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

Defeito N73

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

Aviso PVS-Studio: V616 CWE-480 A constante denominada 'QAbstractItemView :: NoSelection' com o valor 0 é usada na operação bit a bit. itemviews.cpp 976

A constante nomeada QAbstractItemView :: NoSelection é zero. Portanto, a subexpressão (selectionMode & QAbstractItemView :: NoSelection) não faz sentido. Será sempre 0.

Eu acho que deveria ser escrito aqui:

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

Defeito N74

O código a seguir é difícil de entender. Ele está errado, mas eu não sei o que ele deveria ser. Comentar sobre uma função também não me ajuda.

 // 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 A expressão 'message' é sempre falsa. qwindowscontext.cpp 802

O programador provavelmente assume que a função FormatMessage alterará o valor do ponteiro da mensagem . Mas isso não é verdade. A função FormatMessage não pode alterar o valor de um ponteiro, pois é passado para a função por valor. Aqui está um protótipo dessa função:

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


Potenciais vazamentos de memória


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

Aviso do PVS-Studio: V701 CWE-401 realloc () possível vazamento: quando realloc () falha na alocação de memória, o ponteiro original 'filhos' é perdido. Considere atribuir realloc () a um ponteiro temporário. makefiledeps.cpp 103

A expansão do buffer é implementada de maneira perigosa. Se a função realloc não puder alocar memória, ela retornará NULL . Este NULL será imediatamente colocado na variável filhos e não haverá possibilidade de alguma forma liberar o buffer alocado anteriormente. Um vazamento de memória ocorrerá.

Erros semelhantes: qt-701.txt .

Diversos


Defeito 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 Warning: V610 CWE-758 Comportamento não especificado. Verifique o operador de turno '>>'. O operando esquerdo '(- 2147483647 - 1)' é negativo. qdrawhelper.cpp 4015

O valor negativo de INT_MIN não pode ser alterado. Esse é um comportamento não especificado e você não pode confiar no resultado dessa operação. Os bits mais significativos podem ser iguais a 0 ou 1.

Defeito 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 O valor da variável 'signal' é verificado após ser utilizado. Talvez haja um erro na lógica do programa. Verifique as linhas: 397, 413. qobject.cpp 397

Uma verificação (sinal <0) indica que o valor do argumento do sinal pode ser negativo. No entanto, esse argumento foi usado anteriormente para indexar a matriz. Acontece que a verificação é realizada tarde demais. O programa já será interrompido.

Defeito 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 A variável 'lastNamespaceDeclaration' recebe valores duas vezes sucessivos. Talvez isso seja um erro. Verifique as linhas: 3188, 3194. qxmlstream.cpp 3194

Vou destacar a essência do erro:

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

Defeito 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 A variável 'done' recebe valores duas vezes sucessivamente. Talvez isso seja um erro. Verifique as linhas: 509, 511. qeffects.cpp 511

Tudo é o mesmo que no caso anterior. Observe a variável concluída .

Conclusão


Mesmo analisando superficialmente o relatório, escrevi quase 100 erros. Estou satisfeito com os resultados do PVS-Studio.

Obviamente, essas verificações raras de código não têm nada a ver com a melhoria da qualidade e confiabilidade do código. Eles demonstram apenas os recursos do analisador de código. As ferramentas de análise estática devem ser aplicadas regularmente. Nesse caso, eles reduzem o custo de correção de bugs e protegem os aplicativos de muitas possíveis vulnerabilidades.

Obrigado pela atenção. Para acompanhar nossas novas publicações, convido você a se inscrever em um de nossos canais:
  1. VK.com: pvsstudio_rus
  2. RSS da “velha escola”: viva64-blog-ru
  3. Twitter: @pvsstudio_rus
  4. Instagram: @pvsstudio_rus
  5. Telegrama: @pvsstudio_rus



Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Andrey Karpov. Uma terceira verificação do Qt 5 com o PVS-Studio

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


All Articles