Travis CI est un service Web distribué de création et de test de logiciels qui utilise GitHub comme hébergement de code source. En plus des scénarios ci-dessus, vous pouvez ajouter le vôtre, grâce aux nombreuses options de configuration. Dans cet article, nous allons configurer Travis CI pour fonctionner avec PVS-Studio en utilisant l'exemple de code PPSSPP.
Présentation
Travis CI est un service Web de création et de test de logiciels. Il est généralement utilisé conjointement avec la pratique de l'intégration continue.
PPSSPP - émulateur de console de jeu PSP. Le programme est capable d'émuler le lancement de tous les jeux à partir d'images disque conçues pour Sony PSP. Le programme a été publié le 1er novembre 2012. PPSSPP est sous licence GPL v2. Tout le monde peut apporter des améliorations au
code source du projet .
PVS-Studio est un analyseur de code statique pour rechercher les erreurs et les vulnérabilités potentielles dans le code du programme. Dans cet article, pour changer, nous allons lancer PVS-Studio non pas localement sur la machine du développeur, mais dans le cloud, et rechercher des erreurs dans PPSSPP.
Configurer Travis CI
Nous aurons besoin d'un référentiel sur GitHub, où se trouve le projet dont nous avons besoin, ainsi que d'une clé pour PVS-Studio (vous pouvez obtenir une
clé d'essai ou
gratuite pour les projets Open Source ).
Allons sur le site Web de
Travis CI . Après l'autorisation d'utiliser le compte GitHub, nous aurons une liste de référentiels:
Pour le test, j'ai bifurqué PPSSPP.
Nous activons le référentiel que nous voulons collecter:
Pour le moment, Travis CI ne peut pas assembler notre projet, car il n'y a pas d'instructions pour l'assemblage. Par conséquent, il est temps de configurer.
Lors de l'analyse, certaines variables nous seront utiles, par exemple, la clé de PVS-Studio, qu'il ne serait pas souhaitable de spécifier dans le fichier de configuration. Ajoutez donc des variables d'environnement à l'aide des paramètres de construction dans Travis CI:
Nous aurons besoin de:
- PVS_USERNAME - nom d'utilisateur
- PVS_KEY - clé
- MAIL_USER - email qui sera utilisé pour envoyer le rapport
- MAIL_PASSWORD - mot de passe de l'e-mail
Les deux derniers sont facultatifs. Ils seront utilisés pour envoyer les résultats par courrier. Si vous souhaitez envoyer le rapport d'une autre manière, vous n'avez pas besoin de les spécifier.
Nous avons donc ajouté les variables d'environnement dont nous avons besoin:
Créez maintenant un fichier
.travis.yml et placez-le à la racine du projet. Le fichier de configuration pour Travis CI existait déjà dans PPSSPP, cependant, il était trop volumineux et totalement inadapté à l'exemple, j'ai donc dû le simplifier considérablement et ne laisser que les éléments de base.
Tout d'abord, nous indiquons la langue, la version d'Ubuntu Linux que nous voulons utiliser dans la machine virtuelle et les packages nécessaires pour l'assemblage:
language: cpp dist: xenial addons: apt: update: true packages: - ant - aria2 - build-essential - cmake - libgl1-mesa-dev - libglu1-mesa-dev - libsdl2-dev - pv - sendemail - software-properties-common sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:ubuntu-sdk-team/ppa'
Tous les packages répertoriés sont uniquement pour PPSSPP.
Spécifiez maintenant la matrice d'assemblage:
matrix: include: - os: linux compiler: "gcc" env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes - os: linux compiler: "clang" env: PPSSPP_BUILD_TYPE=Linux
Un peu plus sur la section
matrice . Dans Travis CI, il existe deux façons de créer des options de construction: la première consiste à répertorier les compilateurs, les types de systèmes d'exploitation, les variables d'environnement, etc., après quoi une matrice de toutes les combinaisons possibles est générée; le second est une indication explicite de la matrice. Bien sûr, vous pouvez combiner ces deux approches et ajouter un cas unique, ou, à défaut, exclure à l'aide de la section
exclure . En savoir plus à ce sujet
dans la documentation Travis CI .
Il reste à spécifier des instructions d'assemblage spécifiques au projet:
before_install: - travis_retry bash .travis.sh travis_before_install install: - travis_retry bash .travis.sh travis_install script: - bash .travis.sh travis_script after_success: - bash .travis.sh travis_after_success
Travis CI vous permet d'ajouter vos propres équipes pour différentes étapes de la vie d'une machine virtuelle. La section
before_install est effectuée avant l'installation des packages. Ensuite,
installez , qui suit l'installation des packages de la liste
addons.apt , que nous avons indiquée ci-dessus. L'assemblage lui-même a lieu dans un
script . Si tout s'est bien passé, nous
entrons dans
after_success (c'est dans cette section que nous allons exécuter l'analyse statique). Ce ne sont pas toutes les étapes qui peuvent être modifiées; si vous en avez besoin de plus, alors vous devriez regarder dans la
documentation de Travis CI .
Pour faciliter la lecture, les commandes ont été déplacées vers un script séparé
.travis.sh , qui est placé à la racine du projet.
Nous avons donc le fichier
.travis.yml suivant:
language: cpp dist: xenial addons: apt: update: true packages: - ant - aria2 - build-essential - cmake - libgl1-mesa-dev - libglu1-mesa-dev - libsdl2-dev - pv - sendemail - software-properties-common sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:ubuntu-sdk-team/ppa' matrix: include: - os: linux compiler: "gcc" env: PVS_ANALYZE=Yes - os: linux compiler: "clang" before_install: - travis_retry bash .travis.sh travis_before_install install: - travis_retry bash .travis.sh travis_install script: - bash .travis.sh travis_script after_success: - bash .travis.sh travis_after_success
Avant d'installer les packages, mettez à jour les sous-modules. Cela est nécessaire pour construire PPSSPP. Ajoutez la première fonction à
.travis.sh (faites attention à l'extension):
travis_before_install() { git submodule update --init --recursive }
Nous sommes maintenant arrivés directement à la configuration de PVS-Studio pour démarrer automatiquement dans Travis CI. Tout d'abord, nous devons installer le package PVS-Studio sur le système:
travis_install() { if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8 fi if [ "$PVS_ANALYZE" = "Yes" ]; then wget -q -O - https:
Au début de la fonction
travis_install, nous installons les compilateurs dont nous avons besoin en utilisant des variables d'environnement. Ensuite, si la variable
$ PVS_ANALYZE stocke la valeur
Oui (nous l'avons spécifiée dans la section
env lors de la configuration de la matrice d'assemblage), nous installons le
package pvs-studio . En plus de cela, les packages
libio-socket-ssl-perl et
libnet-ssleay-perl sont également indiqués, cependant, ils sont nécessaires pour envoyer les résultats par courrier, ils ne sont donc pas nécessaires si vous choisissez une méthode différente de remise des rapports.
La fonction download_extract télécharge et décompresse l'archive spécifiée:
download_extract() { aria2c -x 16 $1 -o $2 tar -xf $2 }
Il est temps de monter un projet. Cela se produit dans la section
script :
travis_script() { if [ -d cmake-3.6.2-Linux-x86_64 ]; then export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH fi CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}" if [ "$PVS_ANALYZE" = "Yes" ]; then CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}" fi cmake $CMAKE_ARGS CMakeLists.txt make }
En fait, il s'agit d'une configuration d'origine simplifiée, à l'exception de ces lignes:
if [ "$PVS_ANALYZE" = "Yes" ]; then CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}" fi
Dans cette section de code, nous définissons le drapeau d'exportation des commandes de compilation pour
cmake . Cela est nécessaire pour un analyseur de code statique. Vous pouvez en savoir plus à ce sujet dans l'article "
Comment exécuter PVS-Studio sur Linux et macOS ".
Si l'assemblage a réussi, nous nous retrouvons dans
after_success , où nous effectuons une analyse statique:
travis_after_success() { if [ "$PVS_ANALYZE" = "Yes" ]; then pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic \ -o PVS-Studio-${CC}.log \ --disableLicenseExpirationCheck plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html sendemail -t mail@domain.com \ -u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" \ -m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" \ -s smtp.gmail.com:587 \ -xu $MAIL_USER \ -xp $MAIL_PASSWORD \ -o tls=yes \ -f $MAIL_USER \ -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html fi }
Examinons plus en détail les lignes suivantes:
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic \ -o PVS-Studio-${CC}.log \ --disableLicenseExpirationCheck plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
La première ligne génère un fichier de licence à partir du nom d'utilisateur et de la clé que nous avons spécifiés au tout début lors de la configuration des variables d'environnement Travis CI.
La deuxième ligne démarre directement l'analyse. L'indicateur
-j <N> définit le nombre de flux à analyser, l'indicateur
-l <fichier> indique la licence, l'indicateur
-o <fichier> définit le fichier pour la sortie du journal et l'indicateur
-disableLicenseExpirationCheck est nécessaire pour les versions d'essai, car par défaut
pvs- studio-analyzer avertira l'utilisateur de l'expiration de la licence. Pour éviter cela, vous pouvez spécifier cet indicateur.
Le fichier journal contient une sortie brute qui ne peut pas être lue sans conversion, vous devez donc d'abord rendre le fichier lisible.
Nous sautons les journaux via le
convertisseur plog et la sortie est un fichier html.
Dans cet exemple, j'ai décidé d'envoyer des rapports par courrier électronique à l'aide de la commande
sendemail .
En conséquence, nous avons obtenu le fichier
.travis.sh suivant:
#/bin/bash travis_before_install() { git submodule update --init --recursive } download_extract() { aria2c -x 16 $1 -o $2 tar -xf $2 } travis_install() { if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8 fi if [ "$PVS_ANALYZE" = "Yes" ]; then wget -q -O - https:
Il est temps d'ajouter les modifications au référentiel git, après quoi Travis CI démarrera automatiquement la génération. Cliquez sur «ppsspp» pour accéder aux rapports d'assemblage:
Nous allons voir un aperçu de l'assemblage actuel:
En cas de réussite de l'assemblage, nous recevrons par courrier une lettre contenant les résultats de l'analyse statique. Bien sûr, l'envoi postal n'est pas le seul moyen d'obtenir un rapport. Vous pouvez choisir n'importe quelle méthode d'implémentation. Mais il est important de se rappeler qu'après l'assemblage, il sera impossible d'accéder aux fichiers de la machine virtuelle.
Résumé des erreurs
Nous avons terminé avec succès la partie la plus difficile. Maintenant, assurons-nous que tous nos efforts sont justifiés. Considérons quelques points intéressants du rapport sur l'analyse statique, qui m'est parvenu par courrier (pas pour rien que je l'ai signalé).
Optimisation dangereuse
void sha1( unsigned char *input, int ilen, unsigned char output[20] ) { sha1_context ctx; sha1_starts( &ctx ); sha1_update( &ctx, input, ilen ); sha1_finish( &ctx, output ); memset( &ctx, 0, sizeof( sha1_context ) ); }
PVS-Studio Warning:
V597 Le compilateur pourrait supprimer l'appel de fonction 'memset', qui est utilisé pour vider le tampon 'sum'. La fonction RtlSecureZeroMemory () doit être utilisée pour effacer les données privées. sha1.cpp 325
Ce fragment de code est situé dans le module de hachage sécurisé, cependant, il contient une faille de sécurité grave (
CWE-14 ). Considérez la liste d'assembleur générée lors de la compilation de la version de débogage:
; Line 355 mov r8d, 20 xor edx, edx lea rcx, QWORD PTR sum$[rsp] call memset ; Line 356
Tout est en parfait ordre, et la fonction
memset est exécutée, écrasant ainsi des données importantes dans la RAM, cependant, ne vous réjouissez pas jusqu'à présent. Considérez la liste des assembleurs de la version Release avec optimisation:
; 354 : ; 355 : memset( sum, 0, sizeof( sum ) ); ; 356 :}
Comme le montre la liste, le compilateur a ignoré l'appel
memset . En effet, la fonction
sha1 n'appelle plus la structure
ctx après avoir appelé
memset . Par conséquent, le compilateur ne voit pas l'intérêt de perdre du temps processeur à remplacer la mémoire inutilisée à l'avenir. Vous pouvez résoudre ce problème en utilisant la fonction
RtlSecureZeroMemory ou
similaire .
Correctement:
void sha1( unsigned char *input, int ilen, unsigned char output[20] ) { sha1_context ctx; sha1_starts( &ctx ); sha1_update( &ctx, input, ilen ); sha1_finish( &ctx, output ); RtlSecureZeroMemory(&ctx, sizeof( sha1_context ) ); }
Comparaison inutile
static u32 sceAudioOutputPannedBlocking (u32 chan, int leftvol, int rightvol, u32 samplePtr) { int result = 0;
Avertissement PVS-Studio: l'expression
V547 'leftvol> = 0' est toujours vraie. sceAudio.cpp 120
Faites attention à la branche else pour le premier
if . Le code ne sera exécuté que si toutes les conditions sont
leftvol> 0xFFFF || rightvol> 0xFFFF || leftvol <0 || rightvol <0 se révélera être faux. Par conséquent, nous obtenons les instructions suivantes qui seront vraies pour la branche else:
leftvol <= 0xFFFF ,
rightvol <= 0xFFFF ,
leftvol> = 0 et
rightvol> = 0 . Faites attention aux deux dernières déclarations. Est-il judicieux de vérifier ce qui est une condition préalable à l'exécution de ce morceau de code?
Nous pouvons donc calmement supprimer ces déclarations conditionnelles:
static u32 sceAudioOutputPannedBlocking (u32 chan, int leftvol, int rightvol, u32 samplePtr) { int result = 0;
Un autre scénario. Derrière ces conditions redondantes se cache une sorte d'erreur. Ils n'ont peut-être pas vérifié ce qui est requis.
Ctrl + C Ctrl + V contre-attaque
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) { if (!Memory::IsValidAddress(psmfData) || !Memory::IsValidAddress(psmfData)) { return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address"); } .... }
V501 Il existe des sous-expressions identiques '! Memory :: IsValidAddress (psmfData)' à gauche et à droite de '||' opérateur. scePsmf.cpp 703
Faites attention au chèque à l'intérieur
si . Ne vous semble-t-il pas étrange que nous vérifions si l'adresse
psmfData est valide
deux fois plus? Cela me semble donc étrange ... En fait, nous avons, bien sûr, une faute de frappe, et l'idée était de vérifier les deux paramètres d'entrée.
L'option correcte:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) { if (!Memory::IsValidAddress(psmfStruct) || !Memory::IsValidAddress(psmfData)) { return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address"); } .... }
Variable oubliée
extern void ud_translate_att( int size = 0; .... if (size == 8) { ud_asmprintf(u, "b"); } else if (size == 16) { ud_asmprintf(u, "w"); } else if (size == 64) { ud_asmprintf(u, "q"); } .... }
Avertissement PVS-Studio: l'expression
V547 'taille == 8' est toujours fausse. syn-att.c 195
Cette erreur se trouve dans le dossier
ext , donc elle ne s'applique pas tout à fait au projet, mais l'erreur a été trouvée avant que je ne le remarque, j'ai donc décidé de le laisser. Pourtant, cet article ne porte pas sur un examen des erreurs, mais sur l'intégration avec Travis CI, et aucune configuration de l'analyseur n'a été effectuée.
La
taille variable
est initialisée par une constante, cependant, elle n'est pas du tout utilisée dans le code, jusqu'à l'
instruction if , qui, bien sûr, renvoie
false lors de la vérification de la condition, car, comme nous le rappelons, la
taille est nulle. Les vérifications ultérieures n'ont pas non plus de sens.
Apparemment, l'auteur du fragment de code a oublié d'écraser la
taille de la variable avant cela.
Arrêter
Sur ce point, nous nous retrouvons peut-être avec des erreurs. Le but de cet article est de démontrer le travail de PVS-Studio en collaboration avec Travis CI, et non d'analyser le projet aussi complètement que possible. Si vous voulez de plus en plus de belles erreurs, vous pouvez toujours les admirer
ici :).
Conclusion
L'utilisation de services Web pour créer des projets conjointement avec la pratique de l'analyse incrémentielle peut détecter de nombreux problèmes immédiatement après la fusion du code. Certes, un seul assemblage peut ne pas être suffisant, donc la configuration des tests conjointement avec l'analyse statique améliorera considérablement la qualité du code.
Liens utiles

Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Maxim Zvyagintsev.
Comment configurer PVS-Studio dans Travis CI en utilisant l'exemple de l'émulateur de console de jeu PSP .