Cet article fait suite à une série de publications sur l'utilisation de PVS-Studio dans les systèmes cloud. Cette fois, nous examinerons l'analyseur conjointement avec GitLab CI, un produit de GitLab Inc. L'intégration d'un analyseur statique dans le système CI vous permet d'identifier les bogues immédiatement après la phase de construction du projet et est un moyen très efficace de réduire le coût de détection des erreurs.
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 de gestion de référentiels. Il peut être utilisé directement dans le navigateur du site officiel en enregistrant un compte, ou installé et déployé sur votre propre serveur.
PVS-Studio est un outil d'analyse de code statique conçu pour détecter les erreurs et les vulnérabilités potentielles dans les programmes écrits en C, C ++, C # et Java. Il fonctionne sur les systèmes 64 bits sous Windows, Linux et macOS et peut analyser le code destiné aux plates-formes ARM 32 bits, 64 bits et intégrées. Si c'est la première fois que vous essayez l'analyse de code statique pour tester vos projets, nous vous recommandons de lire l'
article sur la façon de voir rapidement les avertissements PVS-Studio les plus intéressants et d'évaluer les capacités de cet outil.
Le projet OBS sera utilisé pour démontrer le fonctionnement d'un analyseur statique dans le cloud.
Open Broadcaster Software est une suite logicielle gratuite et open source pour l'enregistrement vidéo et le streaming. OBS offre la possibilité d'intercepter des appareils et des sources en temps réel, la composition des scènes, le décodage, l'enregistrement et la diffusion. La transmission des données s'effectue principalement via le protocole de messagerie en temps réel, et les données peuvent être transférées vers n'importe quelle source prenant en charge RTMP - le programme a des préréglages prêts à l'emploi pour une diffusion directe sur les plateformes de streaming les plus populaires.
Personnalisation
Pour commencer avec GitLab, allez sur le site et cliquez sur le bouton
S'inscrire :
Vous pouvez vous inscrire en liant les comptes de services tels que: GitHub, Twitter, Google, BitBucket, Saleforce ou simplement en remplissant le formulaire qui s'ouvre. Après autorisation, GitLab nous rencontre avec une proposition de création de projet:
Liste des plateformes à partir desquelles vous pouvez importer:
Pour plus de clarté, créez un projet vide:
Ensuite, nous devons télécharger notre projet dans le référentiel créé. Cela se fait à l'aide des invites qui apparaissent dans la fenêtre du projet créé.
Lorsque la tâche démarre, GitLab CI prend les instructions du
fichier .gitlab-ci.yml . Vous pouvez l'ajouter en cliquant sur le bouton
Configurer CI / CD , ou simplement en le créant dans le référentiel local et en le téléchargeant sur le site. Nous utiliserons la première option:
Faisons un wrapper minimal pour le script:
image: debian job: script:
Téléchargez l'analyseur et l'utilitaire sendemail, dont nous aurons besoin à l'avenir:
- apt-get update && apt-get -y install wget gnupg - wget -O - https:
Ensuite, installez 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 un fichier avec la licence de l'analyseur. Par défaut, le fichier PVS-Studio.lic sera créé dans le répertoire ../.config/PVS-Studio. Dans ce cas, le fichier de licence peut être omis des paramètres de lancement de l'analyseur, il sera récupéré 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, accédez à: Paramètres> CI / CD> Variables.
Générez le projet en utilisant cmake:
- cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4
Exécutez ensuite l'analyseur:
- pvs-studio-analyzer analyze -o PVS-Studio.log
PVS-Studio.log stockera les résultats de l'analyse. Le fichier de rapport résultant n'est pas destiné à la lecture, et pour le mettre dans une vue 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 plus de lisibilité, nous allons convertir au format html:
- plog-converter -t html PVS-Studio.log -o PVS-Studio.html
Le rapport peut être téléchargé à l'aide d'
artefacts , mais nous utiliserons une méthode alternative et enverrons le fichier avec les résultats de l'analyseur au courrier électronique à 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:
Cliquez sur le bouton
commit changes . Si nous avons tout fait correctement, nous verrons l'inscription:
Cette configuration GitLab CI est valide. Pour suivre la progression, accédez à l'onglet
CI / CD> Pipelines.Cliquez sur en
cours d'
exécution . Nous verrons la fenêtre du terminal de la machine virtuelle sur laquelle notre fichier de configuration s'exécute. Après un certain temps, nous obtenons le message: le
travail a réussi.Alors, il est temps d'aller à l'e-mail et d'ouvrir le fichier html avec des avertissements.
Résultats de validation
Examinons maintenant certains avertissements du rapport qui indiquent des erreurs dans le projet Open Broadcaster Software pour démontrer l'essence de l'analyse de code statique. Le but de cet article est de décrire les principes d'interaction entre PVS-Studio et GitLab CI / CD, donc seuls quelques fragments de code intéressants avec des erreurs ont été écrits. Nous sommes prêts à accorder une licence temporaire aux auteurs du projet, et s'ils le souhaitent, ils peuvent effectuer une analyse plus approfondie du projet. Ou ils peuvent profiter de l'une des
options de licence gratuite de PVS-Studio.
Tout le monde peut également
recevoir indépendamment
une clé d'essai pour explorer les capacités de PVS-Studio et vérifier leurs projets.
Examinons donc quelques exemples d'erreurs trouvées dans Open Broadcaster Software.
Avertissement N1V547 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); } .... }
Faites attention à la ligne:
si (data_end_pos> cb-> capacité) , la réalisation de la condition signifie que la variable
back_size , définie dans la ligne ci-dessous, sera toujours supérieure à zéro, car le plus petit du plus grand est évidemment soustrait, ce qui signifie que la condition est une autre ligne ci-dessous, sera toujours
vraie . Une condition excessive n'est pas si inoffensive lorsqu'elle est sous un code qui modifie les données.
Avertissements N2, N3V629 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; .... }
Le mélange d'opérations sur les types 32 bits et 64 bits semble suspect ici. Tout d'abord, le masque est calculé à l'aide de types 32 bits (expression
(1 << indent) - 1 ), puis il est implicitement étendu au type 64 bits dans l'expression
active & = .... Très probablement, lors du calcul du masque, l'utilisation de types 64 bits a également été supposée.
La version correcte du code:
active &= ((uint64_t)(1) << indent) - 1;
Ou:
active &= (1ull << indent) - 1;
En outre, le copier-coller de ce bloc de code est ci-dessous, pour lequel l'analyseur a également émis un 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 N4V761 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. Presque toujours, un tel code indique une erreur de
copier-coller . Très probablement, ces fonctions auraient dû être appelées avec des arguments différents. Même si ce n'est pas le cas, ce code a l'air bizarre. Une bonne solution serait d'écrire un bloc une 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 N5V560 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)) { .... } .... }
La 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 qui est initialisé avec l'adresse du champ
modifiers de la structure
combo . Comme sa valeur ne change pas au lieu de vérification, elle restera non nulle. De plus - entre le lieu d'initialisation et de vérification, le pointeur est utilisé lors de l'appel de la fonction
load_modifier , où il est déréférencé. Par conséquent, vérifier
! Modifiers n'a pas de sens, car à la suite de l'opérateur
&& , nous obtiendrons toujours
faux lors de l'évaluation d'une expression logique. Je pense que le programmeur voulait vérifier la valeur entière située à l'adresse stockée dans le pointeur des
modificateurs , mais a oublié de déréférencer ce pointeur.
C'est-à-dire Il me semble que le chèque devrait être comme ceci:
if (!*modifiers && ....) : if (!combo.modifiers && ....)
Avertissement N6V575 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); .... }
Souvent, ce code n'est pas sûr car il ne tient pas compte du fait que
malloc peut renvoyer un pointeur nul. Si
malloc renvoie
NULL ,
un comportement non défini se produira dans ce cas, car le premier argument de
strncpy sera
NULL .
Vous pouvez en savoir plus sur les raisons pour lesquelles il est important de vérifier la valeur de retour de la fonction
malloc dans l'
article correspondant.Avertissements N7, N8, N9Essayons de deviner dans quels cas les mauvais calculs peuvent se produire:
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:
Avertissements de l'analyseur:
- V636 L' expression 'i / 6' a été implicitement convertie du type 'size_t' en type 'float'. Pensez à utiliser une conversion 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)
La bonne réponse: dans ceux où
i n'est pas jeté pour
flotter . Dans les expressions que l'analyseur nous montre, une division entière se produit. Ce code peut ne pas fonctionner exactement comme prévu par le programmeur.
Conclusion
Comme nous pouvons le voir, l'intégration de l'analyseur statique PVS-Studio dans votre projet GitLab est assez simple. Pour ce faire, il vous suffit d'écrire un 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 pour configurer le système CI. La vérification du code vous permettra d'identifier les problèmes immédiatement après la génération, ce qui aidera à les résoudre lorsque la complexité et le coût des modifications sont encore faibles.

Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Vladislav Stolyarov.
PVS-Studio dans les nuages: GitLab CI / CD .