PVS-Studio passe aux nuages: CircleCI

Image 2

Nous poursuivons la série d'articles sur l'utilisation de l'analyseur statique PVS-Studio dans les systèmes CI cloud. Aujourd'hui, nous envisageons un autre service - CircleCI. Cette fois, le lecteur multimédia Kodi agira comme un projet d'analyse, dans le code source duquel nous essaierons de trouver des endroits intéressants.

Remarque D'autres articles sur l'intégration de PVS-Studio dans les systèmes CI cloud peuvent être trouvés ici:


Avant de passer directement à la configuration et à l'analyse des avertissements de l'analyseur, disons quelques mots sur le logiciel utilisé et analysé.

CircleCI est un service CI basé sur le cloud pour automatiser l'assemblage, les tests et la publication de logiciels. Il prend en charge l'assemblage de projets à la fois dans des conteneurs et dans des machines virtuelles exécutant Windows, Linux et macOS.

Kodi est un lecteur multimédia multiplateforme gratuit et open source. Vous permet de lire des fichiers audio et vidéo situés à la fois sur votre ordinateur personnel et sur un réseau local ou Internet. Il prend en charge les thèmes et les fonctionnalités en installant des plugins. Disponible pour Windows, Linux, macOS et Android.

PVS-Studio est un analyseur de code statique pour rechercher les erreurs et les vulnérabilités potentielles dans le code écrit en C, C ++, C # et Java. Fonctionne sous Windows, Linux et macOS.

Personnalisation


Accédez à la page d'accueil de CircleCI et cliquez sur le bouton «S'inscrire»

Image 1

Sur la page suivante, nous serons invités à vous authentifier avec un compte GitHub ou Bitbucket. Sélectionnez GitHub et accédez à la page d'autorisation de l'application CircleCI.

Image 3

Nous autorisons l'application (bouton vert «Autoriser circleci») et nous redirigeons vers la page d'accueil «Bienvenue sur CircleCI!»

Image 4

Sur cette page, nous pouvons configurer immédiatement quels projets seront assemblés dans CircleCI. Nous marquons notre référentiel et cliquons sur «Suivre».

Après avoir ajouté le référentiel, CircleCI démarrera automatiquement la construction, mais depuis il n'y a pas encore de fichier de configuration dans le référentiel - la tâche de construction échouera.

Image 5

Avant d'ajouter le fichier de configuration, nous ajouterons au projet des variables contenant des données de licence pour l'analyseur. Pour ce faire, cliquez sur «Paramètres» dans le volet gauche, puis dans le groupe «ORGANISATION» sélectionnez l'élément «Projets» et cliquez sur l'engrenage à droite du projet dont nous avons besoin. Une fenêtre de paramètres s'ouvre.

Image 6

Nous sommes intéressés par la section "Variables d'environnement". Nous y allons et créons les variables PVS_USERNAME et PVS_KEY contenant le nom d'utilisateur et la clé de licence pour l'analyseur.

Image 7

Lors du démarrage d'une génération de projet, CircleCI lit la configuration de la tâche à partir d'un fichier dans le référentiel le long du chemin .circleci / config.yml. Ajoutez-le.

Tout d'abord, nous indiquons l'image de la machine virtuelle où l'analyseur va démarrer. Une liste complète des images est disponible ici .

version: 2 jobs: build: machine: image: ubuntu-1604:201903-01 

Ensuite, ajoutez les référentiels nécessaires à apt et installez les dépendances du projet:

 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget 

Ajoutez le référentiel PVS-Studio et installez l'analyseur:

 - run: wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - && sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - run: sudo -- sh -c "apt-get update && apt-get install pvs-studio -y" 

Collectons les dépendances du projet:

 - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local 

Générez des Makefiles dans le répertoire d'assembly:

 - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 

L'étape suivante consiste à configurer et exécuter une analyse statique de notre projet.

Créez d'abord un fichier avec la licence de l'analyseur. La deuxième commande démarre la compilation de la trace de l'assembly du projet.

Après le traçage, nous commençons directement l'analyse statique. Lors de l'utilisation d'une licence d'essai, l'analyseur doit être démarré avec le paramètre:

--disableLicenseExpirationCheck .

La dernière commande convertit le fichier de résultats de l'analyseur en un rapport html:

 - run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log 

Après avoir terminé les tests, enregistrez les rapports de l'analyseur:

 - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 

Texte complet du fichier .circleci / config.yml:

 version: 2.1 jobs: build: machine: image: ubuntu-1604:201903-01 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget - run: wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add – && sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - run: sudo -- sh -c "apt-get update && apt-get install pvs-studio -y" - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. - run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 

Nous téléchargeons le fichier dans le référentiel et CircleCI commencera automatiquement l'assemblage du projet.

Image 12

Après la fin de la tâche, les fichiers avec les résultats de l'analyseur peuvent être téléchargés sur l'onglet «Artefacts».

Image 11

Résultats d'analyse


Eh bien, regardons maintenant quelques avertissements émis par l'analyseur pendant le travail.

Avertissement PVS-Studio : V504 Il est très probable que le point-virgule ';' est manquant après le mot clé "return". AdvancedSettings.cpp: 1476

 void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap) { if (!arttypes) return artworkMap.clear(); const TiXmlNode* arttype = arttypes->FirstChild("arttype"); .... } 

À en juger par la mise en forme du code, la logique d'exécution suivante a été supposée:

  • si arttypes est un pointeur nul, terminez l'exécution de la méthode;
  • si arttypes est un pointeur différent de zéro, effacez le vecteur artworkMap, puis effectuez d'autres actions.

Cependant, le caractère manquant ';' fait des ajustements; par conséquent, la logique d'exécution ne correspond pas du tout au formatage. En conséquence, il devient le suivant:

  • si arttypes est un pointeur nul, le vecteur artworkMap est effacé et la méthode se termine;
  • si arttypes est un pointeur non nul, d'autres actions sont effectuées, mais le vecteur artworkMap n'est pas nettoyé.

En général, il est très peu probable qu'il n'y ait pas d'erreur. Et presque personne n'écrirait d'expressions dans l'esprit du retour artworkMap.clear (); :).

Avertissements de PVS-Studio :

  • V547 L' expression 'lastsector' est toujours fausse. udf25.cpp: 636
  • V547 L' expression 'lastsector' est toujours fausse. udf25.cpp: 644
  • V571 Contrôle récurrent. La condition 'if (lastsector)' a déjà été vérifiée à la ligne 636. udf25.cpp: 644

 int udf25::UDFGetAVDP( struct avdp_t *avdp) { .... uint32_t lastsector; .... lastsector = 0; // <= .... for(;;) { .... if( lastsector ) { // <= V547 lbnum = lastsector; terminate = 1; } else { //! @todo Find last sector of the disc (this is optional). if( lastsector ) // <= V547 lbnum = lastsector - 256; else return 0; } } .... } 

Faites attention aux endroits marqués avec // <= . La valeur 0 est écrite dans la variable lastsector , puis elle est utilisée deux fois comme expression conditionnelle de l' instruction if . Étant donné que la valeur de la variable ne change ni dans la boucle ni entre ces affectations, les branches des deux instructions if ne seront pas exécutées.

Il se peut que la fonctionnalité nécessaire n'ait pas encore été implémentée (attention aux tâches ).

Par ailleurs, comme vous pouvez le voir, l'analyseur a immédiatement émis 3 avertissements pour ce code. Parfois, cependant, même quelques avertissements ne suffisent pas, et les utilisateurs continuent de croire que l'analyseur se trompe ... Un collègue a écrit plus à ce sujet dans l'article " Un jour à partir du support utilisateur de PVS-Studio " :).

Avertissement PVS-Studio : V547 L' expression 'values.size ()! = 2' est toujours fausse. GUIControlSettings.cpp: 1174

 bool CGUIControlRangeSetting::OnClick() { .... std::vector<CVariant> values; SettingConstPtr listDefintion = settingList->GetDefinition(); switch (listDefintion->GetType()) { case SettingType::Integer: values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorUpper)); break; case SettingType::Number: values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorUpper)); break; default: return false; } if (values.size() != 2) return false; SetValid(CSettingUtils::SetList(settingList, values)); return IsValid(); } 

Dans ce cas, la vérification de values.size ()! = 2 est redondante, car le résultat de l'expression conditionnelle sera toujours faux . En fait, si l'exécution va dans l'une des branches case de l' instruction switch , 2 éléments seront ajoutés au vecteur, et comme il était vide, alors sa taille deviendra égale à deux; sinon (lors de l'exécution de la branche par défaut ), la méthode se fermera.

Avertissement PVS-Studio : L' expression V547 'prio == 0x7fffffff' est toujours vraie. DBusReserve.cpp: 57

 bool CDBusReserve::AcquireDevice(const std::string& device) { .... int prio = INT_MAX; .... res = dbus_bus_request_name( m_conn, service.c_str(), DBUS_NAME_FLAG_DO_NOT_QUEUE | (prio == INT_MAX ? 0 : DBUS_NAME_FLAG_ALLOW_REPLACEMENT), // <= error); .... } 

La variable prio est initialisée avec la valeur INT_MAX , après quoi elle est également utilisée dans l'opérateur ternaire en comparaison prio == INT_MAX . Cependant, entre le lieu d'initialisation et d'utilisation, sa valeur ne change pas, par conséquent, la valeur de l'expression prio == INT_MAX est vraie , et l'opérateur ternaire retournera toujours 0.

Avertissements de PVS-Studio :

  • V575 Le pointeur null potentiel est passé dans la fonction 'memcpy'. Inspectez le premier argument. Vérifiez les lignes: 39, 38. DVDOverlayImage.h: 39
  • V575 Le pointeur null potentiel est passé dans la fonction 'memcpy'. Inspectez le premier argument. Vérifiez les lignes: 44, 43. DVDOverlayImage.h: 44

 CDVDOverlayImage(const CDVDOverlayImage& src) : CDVDOverlay(src) { Data = (uint8_t*)malloc(src.linesize * src.height); memcpy(data, src.data, src.linesize * src.height); // <= if(src.palette) { palette = (uint32_t*)malloc(src.palette_colors * 4); memcpy(palette, src.palette, src.palette_colors * 4); // <= } .... } 

Les deux avertissements ont le même modèle - le pointeur obtenu à la suite de l'appel de la fonction malloc est utilisé plus loin dans la fonction memcpy sans vérifier d'abord NULL .

Quelqu'un peut vouloir s'opposer à ces avertissements dans la clé suivante: malloc ne retournera jamais un pointeur nul, et si c'est le cas, laissez l'application tomber mieux. C'est un sujet pour une longue discussion, mais d'une manière ou d'une autre je propose de lire la note de mon collègue - " Pourquoi est-il important de vérifier que la fonction malloc est revenue ".

Si vous le souhaitez, vous pouvez configurer l'analyseur pour qu'il se comporte de telle manière qu'il ne considère pas que malloc peut renvoyer un pointeur nul - alors il n'y aura pas de tels avertissements. En savoir plus à ce sujet ici .

PVS-Studio Warning : V522 Il pourrait y avoir un déréférencement d'une potentielle entrée de pointeur nul. Vérifiez les lignes: 985, 981. emu_msvcrt.cpp: 985

 struct dirent *dll_readdir(DIR *dirp) { .... struct dirent *entry = NULL; entry = (dirent*) malloc(sizeof(*entry)); if (dirData->curr_index < dirData->items.Size() + 2) { if (dirData->curr_index == 0) strncpy(entry->d_name, ".\0", 2); .... } 

La situation est similaire à celle décrite ci-dessus. Le pointeur obtenu suite à l'appel de malloc est écrit dans la variable d' entrée , après quoi il est utilisé sans vérifier NULL ( entrée-> nom_d ).

PVS-Studio Warning : V773 La portée de visibilité du pointeur 'progressHandler' a été fermée sans libérer la mémoire. Une fuite de mémoire est possible. PVRGUIChannelIconUpdater.cpp: 94

 void CPVRGUIChannelIconUpdater::SearchAndUpdateMissingChannelIcons() const { .... CPVRGUIProgressHandler* progressHandler = new CPVRGUIProgressHandler(g_localizeStrings.Get(19286)); for (const auto& group : m_groups) { const std::vector<PVRChannelGroupMember> members = group->GetMembers(); int channelIndex = 0; for (const auto& member : members) { progressHandler->UpdateProgress(member.channel->ChannelName(), channelIndex++, members.size()); .... } progressHandler->DestroyProgress(); } 

Le pointeur progressHandler contient la valeur obtenue en appelant l' opérateur new . Cependant, la suppression d'opérateur n'est pas appelée pour ce pointeur, ce qui provoque une fuite de mémoire.

Avertissement PVS-Studio : le dépassement de la matrice V557 est possible. L'index 'idx' pointe au-delà de la limite du tableau. PlayerCoreFactory.cpp: 240

 std::vector<CPlayerCoreConfig *> m_vecPlayerConfigs; bool CPlayerCoreFactory::PlaysVideo(const std::string& player) const { CSingleLock lock(m_section); size_t idx = GetPlayerIndex(player); if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size()) return false; return m_vecPlayerConfigs[idx]->m_bPlaysVideo; } 

L' instruction if impose des restrictions sur la taille du vecteur m_vecPlayerConfigs en raison de l'expression conditionnelle et quitte la méthode si elle est vraie. Par conséquent, si l'exécution du code a atteint la dernière instruction de retour , la taille du vecteur m_vecPlayerConfigs est dans la plage spécifiée: [1; idx]. Cependant, quelques lignes ci-dessous correspondent à l' appel idx : m_vecPlayerConfigs [idx] -> m_bPlaysVideo . Par conséquent, si idx est égal à la taille du vecteur, il dépassera la bordure de la plage autorisée.

Et enfin, jetez un œil à quelques avertissements sur le code de la bibliothèque Platinum .

Avertissement PVS-Studio : V542 Envisagez d'inspecter une conversion de type impair: «bool» en «char *». PltCtrlPoint.cpp: 1617

 NPT_Result PLT_CtrlPoint::ProcessSubscribeResponse(...) { .... bool subscription = (request.GetMethod().ToUppercase() == "SUBSCRIBE"); .... NPT_String prefix = NPT_String::Format(" PLT_CtrlPoint::ProcessSubscribeResponse %ubscribe for service \"%s\" (result = %d, status code = %d)", (const char*)subscription?"S":"Uns", // <= (const char*)service->GetServiceID(), res, response?response->GetStatusCode():0); .... } 

Dans ce cas, la priorité des opérations est confuse. Const char * n'est pas le résultat du calcul de l'opérateur ternaire ( abonnement? "S": "Uns" ), mais l' abonnement variable. Au moins, ça a l'air bizarre.

Avertissement PVS-Studio : V560 Une partie de l'expression conditionnelle est toujours fausse: c == '\ t'. NptUtils.cpp: 863

 NPT_Result NPT_ParseMimeParameters(....) { .... case NPT_MIME_PARAMETER_PARSER_STATE_NEED_EQUALS: if (c < ' ') return NPT_ERROR_INVALID_SYNTAX; // END or CTLs are invalid if (c == ' ' || c == '\t') continue; // ignore leading whitespace .... } 

Le code d'espace est 0x20, le code de tabulation est 0x09. Par conséquent, la sous-expression c == '\ t' sera toujours fausse , car ce cas est déjà couvert par l'expression c <'' (si vrai, la fonction sera fermée).

Conclusion


Comme vous pouvez le voir dans cet article, sur le prochain système CI (CircleCI), nous avons réussi à configurer la vérification de projet à l'aide de PVS-Studio. Je vous suggère de télécharger et d'essayer l' analyseur sur votre projet. Si vous avez des questions sur la configuration ou l'utilisation de l'analyseur, n'hésitez pas à nous écrire , nous serons heureux de vous aider.

Et, bien sûr, le code de la négligence envers vous, mes amis. :)



Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Sergey Vasiliev, Ilya Gainulin. PVS-Studio dans les nuages: CircleCI .

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


All Articles