PVS-Studio dans les nuages: GitLab CI / CD

Figure 2

Cet article poursuit la série de publications sur l'utilisation de PVS-Studio dans les systèmes cloud. Cette fois, nous allons voir comment l'analyseur fonctionne avec GitLab CI, qui est un produit fabriqué par GitLab Inc. L'intégration de l'analyseur statique dans un système CI permet de détecter les bogues juste après la construction du projet et est un moyen très efficace de réduire le coût de la recherche de bogues.

Une liste de nos autres articles sur l'intégration dans les systèmes CI cloud:


Informations sur le logiciel utilisé


GitLab est un service en ligne conçu pour gérer les référentiels. Vous pouvez l'utiliser directement dans un navigateur sur le site officiel en enregistrant votre compte, ou l'installer et le déployer sur votre propre serveur.

PVS-Studio est un outil conçu pour détecter les erreurs et les vulnérabilités potentielles dans le code source des programmes, écrit en C, C ++, C # et Java. Fonctionne dans les systèmes 64 bits sur Windows, Linux et macOS et peut analyser le code des plates-formes ARM 32 bits, 64 bits et intégrées. Si c'est la première fois que vous utilisez l'analyseur pour vérifier vos projets, nous vous recommandons de lire l' article sur la façon de vérifier rapidement les avertissements PVS-Studio les plus intéressants et d'évaluer les capacités de l'outil.

Le projet OBS sera utilisé pour démontrer les capacités de l'analyseur statique dans le cloud. Open Broadcaster Software est un ensemble gratuit et ouvert de programmes pour l'enregistrement vidéo et le streaming. OBS fournit l'interception en temps réel des appareils et des sources, la composition des scènes, le décodage, l'enregistrement et la diffusion. Les données sont transférées principalement via le protocole de messagerie en temps réel et peuvent être envoyées à n'importe quelle source prenant en charge RTMP - le programme dispose de pré-installations prêtes à l'emploi pour la diffusion en direct sur les plateformes de streaming les plus populaires.

La configuration


Pour commencer à travailler avec GitLab, allez sur le site Web et cliquez sur S'inscrire :

Figure 6

Vous pouvez vous inscrire en associant des comptes d'autres services tels que GitHub, Twitter, Google, BitBucket, Saleforce ou simplement en remplissant le formulaire ouvert. Après autorisation, GitLab nous invite à créer un projet:

Figure 7

Une liste des plateformes disponibles pour importer des fichiers:

Figure 33


Créons un projet vide pour plus de clarté:

Figure 17


Ensuite, nous devons télécharger notre projet dans le référentiel créé. Faites-le en utilisant les conseils qui apparaissent dans la fenêtre du projet créé.

Figure 1


Lorsque vous démarrez la tâche, GitLab CI prend les instructions du fichier .gitlab-ci.yml . Vous pouvez l'ajouter soit en cliquant sur Configurer CI / CD , soit simplement en créant un référentiel local et en le téléchargeant sur le site. Suivons la première option:

Figure 21



Faites maintenant un wrapper minimal pour le script:

image: debian job: script: 

Téléchargez l'analyseur et l'utilitaire sendemail, dont nous aurons besoin plus tard:

 - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail 

Ensuite, nous allons installer les dépendances et les utilitaires pour construire OBS:

 - apt-get -y install build-essential cmake make pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev libio-socket-ssl-perl libnet-ssleay-perl ca-certificates 

Nous devons maintenant créer le fichier avec la licence de l'analyseur (par défaut, le fichier PVS-Studio.lic sera créé dans le répertoire ../.config/PVS-Studio). Ce faisant, vous n'avez pas besoin de spécifier le fichier de licence dans les paramètres d'exécution de l'analyseur, il sera rattrapé automatiquement):

 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY 

Ici PVS_NAME et PVS_KEY sont les noms des variables dont nous spécifions les valeurs dans les paramètres. Ils stockeront la clé de connexion et de licence PVS-Studio. Pour définir leurs valeurs, suivez: Paramètres> CI / CD> Variables.

Figure 23


Générez le projet en utilisant cmake:

 - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 

Après cela, exécutez l'analyseur:

 - pvs-studio-analyzer analyze -o PVS-Studio.log 

PVS-Studio.log stockera les résultats de l'analyse. Le fichier résultant avec le rapport n'est pas destiné à être lu. Pour le rendre accessible à l'œil humain, nous avons besoin de l'utilitaire de conversion de plog. Ce programme convertit le journal de l'analyseur en différents formats. Pour une lecture facile, convertissons le journal au format html:

 - plog-converter -t html PVS-Studio.log -o PVS-Studio.html 

Vous pouvez exporter le rapport à l'aide d' artefacts , mais nous changerons le point et enverrons le fichier avec les résultats de l'analyseur par e-mail à l'aide de l'utilitaire sendemail:

 - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

.Gitlab-ci.yml complet:

 image: debian job: script: - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail - apt-get -y install build-essential cmake pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev make libio-socket-ssl-perl libnet-ssleay-perl ca-certificates - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 - pvs-studio-analyzer analyze -o PVS-Studio.log - plog-converter -t html PVS-Studio.log -o PVS-Studio.html - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

Cliquez sur valider les modifications . Si nous avons tout fait correctement, nous verrons la sortie: Cette configuration GitLab CI est valide. Pour suivre la progression, passons à l'onglet CI / CD> Pipelines .

Figure 5


Cliquez sur en cours d'exécution . Nous verrons la fenêtre du terminal de la machine virtuelle dans laquelle notre fichier de configuration s'exécute. Après un certain temps, nous recevons un message: le travail a réussi.

Figure 29


Il est donc temps d'ouvrir le fichier html avec des avertissements envoyés par mail.

Résultats d'analyse


Jetons un coup d'œil à certains avertissements du rapport, révélant des erreurs dans le projet Open Broadcaster Software afin d'obtenir l'essence de l'analyse de code statique. Étant donné que le but principal de l'article est de décrire les principes de l'interaction PVS-Studio et GitLab CI / CD, seuls quelques exemples non triviaux ont été sélectionnés. Nous sommes prêts à accorder aux auteurs du projet une licence temporaire et, s'ils le souhaitent, ils sont invités à effectuer une analyse plus approfondie du projet. De plus, ils peuvent utiliser l'une des façons d'obtenir une licence PVS-Studio gratuite .

Tout le monde peut également obtenir une clé d'essai pour explorer les capacités de PVS-Studio et vérifier leurs projets.

Prenons donc attention à quelques exemples d'erreurs trouvées dans Open Broadcaster Software.

Avertissement N1

V547 L' expression 'back_size' est toujours vraie. circlebuf.h (138)

 struct circlebuf { .... size_t capacity; }; static inline void circlebuf_place(struct circlebuf *cb, size_t position,....,const void *data, size_t size) { .... size_t data_end_pos; data_end_pos = position + size; if (data_end_pos > cb->capacity) { size_t back_size = data_end_pos - cb->capacity; if (back_size) { memcpy((uint8_t *)cb->data + position, data, loop_size); } .... } 

La ligne if (data_end_pos> cb-> capacité) vaut vraiment la peine d'être examinée de près. Si la condition est vraie, la variable back_size , définie dans la ligne ci-dessous, sera toujours supérieure à zéro, car nous traitons ici de la soustraction de la valeur notoirement plus petite de la plus grande. En fin de compte, la condition, qui est deux lignes plus bas, sera toujours vraie . La condition redondante n'est pas si inoffensive lorsqu'elle est suivie par le code, modifiant les données.

Avertissements N2, N3

V629 Envisagez d'inspecter l'expression «1 << retrait». Décalage binaire de la valeur 32 bits avec une extension ultérieure au type 64 bits. profiler.c (610)

 static void profile_print_entry(uint64_t active, unsigned indent, ....) { .... active &= (1 << indent) - 1; .... } 

Les opérations confuses sur les types 32 bits et 64 bits semblent suspectes ici. Tout d'abord, le programmeur évalue le masque, en utilisant des types 32 bits (expression (1 << indent) - 1 ), après quoi il se développe implicitement dans le type 64 bits dans l'expression active & = .... Très probablement, lors de l'évaluation du masque, l'utilisation de types 64 bits était également requise.

Version de code correcte:

 active &= ((uint64_t)(1) << indent) - 1; 

Ou:

 active &= (1ull << indent) - 1; 

Par ailleurs, la version copier-coller de ce bloc est ci-dessous, l'analyseur a également émis l'avertissement: V629 Envisagez d'inspecter l'expression «1 << indent». Décalage binaire de la valeur 32 bits avec une extension ultérieure au type 64 bits. profiler.c (719)

Avertissement N4

V761 Quatre blocs de texte identiques ont été trouvés. 'obs-audio-controls.c' (353)

 static float get_true_peak(....) { .... peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); .... } 

Quatre blocs identiques. Dans presque tous les cas, un tel code indique une erreur de copier-coller . Très probablement, ces fonctions doivent avoir été appelées avec des arguments différents. Même si ce n'est pas le cas, ce code semble étrange. Une bonne solution serait d'écrire le bloc une seule fois et de l'envelopper dans une boucle:

 for(size_t i = 0; i < 3; i++) { peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); } 

Avertissement n5

V560 Une partie de l'expression conditionnelle est toujours fausse: '! Modificateurs'. obs-hotkey.c (662)

 typedef struct obs_key_combination obs_key_combination_t; struct obs_key_combination { uint32_t modifiers; obs_key_t key; }; static inline void load_binding(....) { obs_key_combination_t combo = {0}; uint32_t *modifiers = &combo.modifiers; load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY); load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY); load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY); load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { .... } .... } 

Définition de la fonction load_modifier :

 static inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag) { if (obs_data_get_bool(data, name)) *modifiers |= flag; } 

Comme nous pouvons le voir, modifiers est un pointeur, initialisé par l'adresse du champ modifiers de la structure combo . Comme sa valeur ne change pas avant la vérification, elle restera non nulle. De plus, après l'initialisation avant la vérification, le pointeur est utilisé lors de l'appel de la fonction load_modifier , où il est déréférencé. En conséquence, la vérification ! Modifiers est inutile, car en raison de l'opérateur && , nous obtiendrons toujours faux lors de l'évaluation de l'expression logique. Je pense que le programmeur voulait vérifier une valeur entière par l'adresse qui est stockée dans le pointeur des modificateurs , mais a oublié de déréférencer ce pointeur.

Il me semble donc que le chèque doit être le suivant:

 if (!*modifiers && ....) Or like this: if (!combo.modifiers && ....) 

Avertissement N6

V575 Le pointeur null potentiel est transmis à la fonction 'strncpy'. Inspectez le premier argument. Vérifiez les lignes: 2904, 2903. rtmp.c (2904)

 static int PublisherAuth(....) { .... ptr = malloc(r->Link.app.av_len + pubToken.av_len); strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); .... } 

Le plus souvent, ce code n'est pas sûr, car il ignore que malloc peut renvoyer un pointeur nul. Si malloc retourne NULL , un comportement non défini se produira dans ce cas, car le premier argument de la fonction strncpy aura la valeur NULL .

Pour plus d'informations sur les raisons pour lesquelles il est important de vérifier la valeur de retour de la fonction malloc , consultez l' article correspondant .

Avertissements N7, N8, N9

Devinez quels cas contiennent des calculs incorrects:

 class OBSProjector : public OBSQTDisplay { .... float sourceX, sourceY, ....; .... } .... void OBSProjector::OBSRenderMultiview(....) { OBSProjector *window = (OBSProjector *)data; .... auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX += window->scenesCX; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX = window->scenesCX; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default:// MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } } .... } 

Avertissements de l'analyseur:

  • V636 L' expression 'i / 6' a été implicitement convertie du type 'size_t' en type 'float'. Pensez à utiliser un transtypage de type explicite pour éviter la perte d'une partie fractionnaire. Un exemple: double A = (double) (X) / Y;. window-projecteur.cpp (330)
  • V636 L' expression 'i / 2' a été implicitement convertie du type 'size_t' en type 'float'. Pensez à utiliser un transtypage de type explicite pour éviter la perte d'une partie fractionnaire. Un exemple: double A = (double) (X) / Y;. window-projecteur.cpp (334)
  • V636 L' expression 'i / 2' a été implicitement convertie du type 'size_t' en type 'float'. Pensez à utiliser un transtypage de type explicite pour éviter la perte d'une partie fractionnaire. Un exemple: double A = (double) (X) / Y;. window-projecteur.cpp (340)

Voici la bonne réponse: dans celles où je ne suis pas jeté pour flotter. L'analyseur nous montre des fragments avec une division entière. Un tel code pourrait ne pas fonctionner comme le programmeur l'avait espéré.

Conclusion


Comme nous pouvons le voir, l'intégration de l'analyseur PVS-Studio dans votre projet sur GitLab est un processus assez simple. Pour ce faire, il vous suffit d'écrire un seul fichier de configuration et de le placer dans votre référentiel cloud. Étant donné que GitLab possède sa propre machine virtuelle intégrée, nous n'avons même pas besoin de passer beaucoup de temps à configurer le système CI. La vérification du code vous permettra de trouver des problèmes juste après la construction. Cela permet d'éliminer les problèmes au stade où leur complexité et leur coût sont encore faibles.

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


All Articles