PVS-Studio dans les nuages ​​- Exécution de l'analyse sur Travis CI

À l'heure actuelle, les systèmes cloud CI sont un service très demandé. Dans cet article, nous vous expliquerons comment intégrer l'analyse du code source dans une plateforme cloud CI avec les outils déjà disponibles dans PVS-Studio. À titre d'exemple, nous utiliserons le service Travis CI.

Image 1


Pourquoi considérons-nous des clouds tiers et ne créons pas les nôtres? Il y a un certain nombre de raisons, la principale étant que la mise en œuvre SaaS est une procédure assez coûteuse et difficile. En fait, il est simple et trivial d'intégrer directement l'analyse PVS-Studio dans une plate-forme cloud tierce - qu'il s'agisse de plates-formes ouvertes comme CircleCI, Travis CI, GitLab ou d'une solution d'entreprise spécifique utilisée uniquement dans une certaine entreprise. On peut donc dire que PVS-Studio est déjà disponible "dans les nuages". Un autre problème est la mise en œuvre et la garantie de l'accès à l'infrastructure 24/7. C'est une tâche plus compliquée. PVS-Studio ne fournira pas directement sa propre plate-forme cloud pour exécuter des analyses.

Quelques informations sur le logiciel utilisé


Travis CI est un service de création et de test de logiciels qui utilise GitHub comme stockage. Travis CI ne nécessite pas de changement de code de programmation pour utiliser le service. Tous les paramètres sont définis dans le fichier .travis.yml situé à la racine du référentiel.

Nous prendrons LXC (Linux Containers) comme projet de test pour PVS-Studio. Il s'agit d'un système de virtualisation au niveau du système d'exploitation pour lancer plusieurs instances du système d'exploitation Linux sur un nœud.

Le projet est petit, mais largement suffisant pour la démonstration. Sortie de la commande cloc:
La langue
les fichiers
vierge
commenter
code
C
124
11937
6758
50836
En-tête C / C ++
65
1117
3676
3774
Remarque: Les développeurs LXC utilisent déjà Travis CI, nous allons donc prendre leur fichier de configuration comme base et le modifier pour nos besoins.

La configuration


Pour commencer à travailler avec Travis CI, nous suivons le lien et nous connectons à l'aide d'un compte GitHub.

Image 17

Dans la fenêtre ouverte, nous devons nous connecter à Travis CI.

Image 16

Après autorisation, il redirige vers la page d'accueil "Première fois ici? Permet de commencer! ” , où nous trouvons une brève description de ce qui doit être fait par la suite pour commencer:

  • activer les référentiels;
  • ajoutez le fichier .travis.yml dans le référentiel;
  • démarrer la première génération.

Image 18

Commençons à faire ces actions.

Pour ajouter notre référentiel dans Travis CI, nous allons dans les paramètres de profil par le lien et appuyez sur "Activer".

Image 19

Une fois cliqué, une fenêtre s'ouvrira pour sélectionner les référentiels auxquels l'application Travis CI aura accès.

Remarque: pour donner accès au référentiel, votre compte doit disposer des droits d'administrateur pour ce faire.

Image 38

Après cela, nous choisissons le bon référentiel, confirmons le choix avec le bouton "Approuver et installer", et nous serons redirigés vers la page des paramètres de profil.

Ajoutons quelques variables que nous utiliserons pour créer le fichier de licence de l'analyseur et envoyer ses rapports. Pour ce faire, nous allons aller à la page des paramètres - le bouton "Paramètres" à droite du référentiel nécessaire.

Image 39

La fenêtre des paramètres s'ouvre.

Image 41

Brève description des paramètres;

  • Section "Général" - configuration des déclencheurs de tâche à démarrage automatique;
  • La section "Annulation automatique" permet de configurer l'annulation automatique de la construction;
  • La section «Variables d'environnement» permet de définir des variables d'environnement qui contiennent à la fois des informations ouvertes et confidentielles, telles que les informations de connexion, les clés ssh;
  • La section "Cron Jobs" est une configuration du programme d'exécution des tâches.

Dans la section "Variables d'environnement", nous allons créer des variables PVS_USERNAME et PVS_KEY contenant respectivement un nom d'utilisateur et une clé de licence pour l'analyseur statique. Si vous n'avez pas de licence PVS-Studio permanente, vous pouvez demander une licence d'essai .

Image 5

Ici, nous allons créer les variables MAIL_USER et MAIL_PASSWORD , contenant un nom d'utilisateur et un mot de passe de courriel, que nous utiliserons pour envoyer des rapports.

Image 4

Lors de l'exécution de tâches, Travis CI prend les instructions du fichier .travis.yml, situé à la racine du référentiel.

En utilisant Travis CI, nous pouvons exécuter une analyse statique à la fois directement sur la machine virtuelle et utiliser un conteneur préconfiguré pour ce faire. Les résultats de ces approches ne sont pas différents les uns des autres. Cependant, l'utilisation d'un conteneur préconfiguré peut être utile. Par exemple, si nous avons déjà un conteneur avec un environnement spécifique, à l'intérieur duquel un produit logiciel est construit et testé et nous ne voulons pas restaurer cet environnement dans Travis CI.

Créons une configuration pour exécuter l'analyseur sur une machine virtuelle.

Pour la construction et les tests, nous utiliserons une machine virtuelle sur Ubuntu Trusty, sa description est disponible par le lien .

Tout d'abord, nous précisons que le projet est écrit en C et listons les compilateurs que nous utiliserons pour la construction:

language: c compiler: - gcc - clang 

Remarque: si vous spécifiez plusieurs compilateurs, les tâches s'exécuteront simultanément pour chacun d'eux. Lisez plus ici .

Avant la construction, nous devons ajouter le référentiel d'analyseur, définir les dépendances et les packages supplémentaires:

 before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - 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 - sudo apt-get update -qq - sudo apt-get install -qq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates 

Avant de construire un projet, nous devons préparer votre environnement:

 script: - ./coccinelle/run-coccinelle.sh -i - git diff --exit-code - export CFLAGS="-Wall -Werror" - export LDFLAGS="-pthread -lpthread" - ./autogen.sh - rm -Rf build - mkdir build - cd build - ../configure --enable-tests --with-distro=unknown 

Ensuite, nous devons créer un fichier de licence et commencer à analyser le projet.

Ensuite, nous créons un fichier de licence pour l'analyseur par la première commande. Les données des variables $ PVS_USERNAME et $ PVS_KEY sont extraites des paramètres du projet.

 - pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic 

Par la commande suivante, nous commençons à tracer la construction du projet.

 - pvs-studio-analyzer trace -- make -j4 

Après cela, nous exécutons une analyse statique.
Remarque: lorsque vous utilisez une licence d'essai, vous devez spécifier le paramètre --disableLicenseExpirationCheck .
  - pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-${CC}.log --disableLicenseExpirationCheck 

Le fichier contenant les résultats de l'analyse est converti en rapport html par la dernière commande.

 - plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html 

Étant donné que TravisCI ne vous permet pas de modifier le format des notifications par e-mail, dans la dernière étape, nous utiliserons le paquet sendemail pour envoyer des rapports:

 - 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 

Voici le texte intégral du fichier de configuration pour exécuter l'analyseur sur la machine virtuelle:

 language: c compiler: - gcc - clang before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - 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 - sudo apt-get update -qq - sudo apt-get install -qq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates script: - ./coccinelle/run-coccinelle.sh -i - git diff --exit-code - export CFLAGS="-Wall -Werror" - export LDFLAGS="-pthread -lpthread" - ./autogen.sh - rm -Rf build - mkdir build - cd build - ../configure --enable-tests --with-distro=unknown - pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic - pvs-studio-analyzer trace -- make -j4 - 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 

Pour exécuter PVS-Studio dans un conteneur, créons-le à l'aide du Dockerfile suivant:

 FROM docker.io/ubuntu:trusty ENV CFLAGS="-Wall -Werror" ENV LDFLAGS="-pthread -lpthread" RUN apt-get update && apt-get install -y software-properties-common wget \ && wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - \ && wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list \ && apt-get update \ && apt-get install -yqq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio git libtool autotools-dev automake pkg-config clang make libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates \ && rm -rf /var/lib/apt/lists/* 

Dans ce cas, le fichier de configuration peut ressembler à ceci:

 before_install: - docker pull docker.io/oandreev/lxc env: - CC=gcc - CC=clang script: - docker run --rm --cap-add SYS_PTRACE -v $(pwd):/pvs -w /pvs docker.io/oandreev/lxc /bin/bash -c " ./coccinelle/run-coccinelle.sh -i && git diff --exit-code && ./autogen.sh && mkdir build && cd build && ../configure CC=$CC && pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic && pvs-studio-analyzer trace -- make -j4 && pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-$CC.log --disableLicenseExpirationCheck && plog-converter -t html -o PVS-Studio-$CC.html PVS-Studio-$CC.log && 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" 

Comme vous pouvez le voir, dans ce cas, nous ne faisons rien à l'intérieur de la machine virtuelle, et toutes les actions de construction et de test du projet ont lieu à l'intérieur du conteneur.

Remarque : lorsque vous démarrez le conteneur, vous devez spécifier le paramètre --cap-add SYS_PTRACE ou --security-opt seccomp: unconfined , car un appel système ptrace est utilisé pour le suivi du compilateur.

Ensuite, nous chargeons le fichier de configuration à la racine du référentiel et voyons que Travis CI a été notifié des changements dans le projet et a automatiquement démarré la construction.

Les détails de la progression de la construction et de la vérification de l'analyseur sont visibles dans la console.

Image 2

Une fois les tests terminés, nous recevrons deux e-mails: le premier - avec des résultats d'analyse statique pour la construction d'un projet à l'aide de gcc, et le second - pour clang, respectivement.

En bref sur les résultats de la vérification


En général, le projet est assez propre, l'analyseur n'a émis que 24 avertissements de haute certitude et 46 avertissements de certitude moyenne. Regardons quelques notifications intéressantes:

Conditions redondantes dans if


V590 Envisagez d'inspecter l' expression «ret! = (- 1) && ret == 1». L'expression est excessive ou contient une erreur d'impression. attach.c 107

 #define EOF -1 static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { .... while (getline(&line, &line_bufsz, proc_file) != -1) { ret = sscanf(line, "CapBnd: %llx", &info->capability_mask); if (ret != EOF && ret == 1) // <= { found = true; break; } } .... } 

Si ret == 1 , il n'est certainement pas égal à -1 (EOF). Contrôle redondant, ret! = EOF peut être supprimé.

Deux avertissements similaires ont été émis:

  • V590 Envisagez d'inspecter l'expression «ret! = (- 1) && ret == 1». L'expression est excessive ou contient une erreur d'impression. attach.c 579
  • V590 Envisagez d'inspecter l'expression «ret! = (- 1) && ret == 1». L'expression est excessive ou contient une erreur d'impression. attach.c 583

Perte de bits hauts


V784 La taille du masque de bits est inférieure à la taille du premier opérande. Cela entraînera la perte de bits supérieurs. conf.c 1879

 struct mount_opt { char *name; int clear; int flag; }; static void parse_mntopt(char *opt, unsigned long *flags, char **data, size_t size) { struct mount_opt *mo; /* If opt is found in mount_opt, set or clear flags. * Otherwise append it to data. */ for (mo = &mount_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { if (mo->clear) { *flags &= ~mo->flag; // <= } else { *flags |= mo->flag; } return; } } .... } 

Sous Linux, long est une variable entière 64 bits, mo-> flag est une variable entière 32 bits. L'utilisation de l' indicateur mo-> comme masque de bits entraînera la perte de 32 bits de poids fort. Un masque de bits est implicitement converti en une variable entière 64 bits après l'inversion au niveau du bit. Des bits élevés de ce masque peuvent être perdus.

Je vais le montrer en utilisant un exemple:

 unsigned long long x; unsigned y; .... x &= ~y; 

Image 3


Voici la version correcte du code:

 *flags &= ~(unsigned long)(mo->flag); 

L'analyseur a émis un autre avertissement similaire:

  • V784 La taille du masque de bits est inférieure à la taille du premier opérande. Cela entraînera la perte de bits supérieurs. conf.c 1933

Boucle suspecte


V612 Un «retour» inconditionnel dans une boucle. conf.c 3477

 #define lxc_list_for_each(__iterator, __list) \ for (__iterator = (__list)->next; __iterator != __list; \ __iterator = __iterator->next) static bool verify_start_hooks(struct lxc_conf *conf) { char path[PATH_MAX]; struct lxc_list *it; lxc_list_for_each (it, &conf->hooks[LXCHOOK_START]) { int ret; char *hookname = it->elem; ret = snprintf(path, PATH_MAX, "%s%s", conf->rootfs.path ? conf->rootfs.mount : "", hookname); if (ret < 0 || ret >= PATH_MAX) return false; ret = access(path, X_OK); if (ret < 0) { SYSERROR("Start hook \"%s\" not found in container", hookname); return false; } return true; // <= } return true; } 

La boucle est démarrée et interrompue à la première itération. Cela aurait pu être fait intentionnellement, mais dans ce cas, la boucle aurait pu être omise.

Index de tableau hors limites


V557 Le sous-ensemble de la baie est possible. La valeur de l'index «octets - 1» pourrait atteindre -1. network.c 2570

 static int lxc_create_network_unpriv_exec(const char *lxcpath, const char *lxcname, struct lxc_netdev *netdev, pid_t pid, unsigned int hooks_version) { int bytes; char buffer[PATH_MAX] = {0}; .... bytes = lxc_read_nointr(pipefd[0], &buffer, PATH_MAX); if (bytes < 0) { SYSERROR("Failed to read from pipe file descriptor"); close(pipefd[0]); } else { buffer[bytes - 1] = '\0'; } .... } 

Les octets sont lus dans le tampon du tube. En cas d'erreur, la fonction lxc_read_nointr retournera une valeur négative. Si tout se passe bien, un terminal null est écrit par le dernier élément. Cependant, si 0 octet est lu, l'index sera hors des limites du tampon, conduisant à un comportement indéfini.

L'analyseur a émis un autre avertissement similaire:

  • V557 Le sous-ensemble de la baie est possible. La valeur de l'index «octets - 1» pourrait atteindre -1. network.c 2725

Débordement de tampon


V576 Format incorrect. Pensez à vérifier le troisième argument réel de la fonction 'sscanf'. Il est dangereux d'utiliser un spécificateur de chaîne sans spécification de largeur. Un débordement de tampon est possible. lxc_unshare.c 205

 static bool lookup_user(const char *oparg, uid_t *uid) { char name[PATH_MAX]; .... if (sscanf(oparg, "%u", uid) < 1) { /* not a uid -- perhaps a username */ if (sscanf(oparg, "%s", name) < 1) // <= { free(buf); return false; } .... } .... } 

Dans ce cas, l'utilisation de sscanf peut être dangereuse, car si le tampon oparq est plus grand que le tampon de nom , l'index sera hors limites lors de la formation du tampon de nom .

Conclusion


Comme nous le voyons, c'est une tâche assez simple de configurer une vérification de l'analyseur de code statique dans un cloud. Pour cela, nous avons juste besoin d'ajouter un fichier dans un référentiel et de passer peu de temps à configurer le système CI. En conséquence, nous aurons un outil pour détecter le problème au stade de l'écriture du code. L'outil nous permet d'empêcher les bogues d'accéder aux prochaines étapes des tests, où leur correction nécessitera beaucoup de temps et d'efforts.

Bien sûr, l'utilisation de PVS-Studio avec des plateformes cloud n'est pas seulement limitée à Travis CI. Semblable à la méthode décrite dans l'article, avec de petites différences, l'analyse PVS-Studio peut être intégrée à d'autres solutions CI populaires dans le cloud, telles que CircleCI, GitLab, etc.

Liens utiles


  • Pour plus d'informations sur l'exécution de PVS-Studio sur Linux et MacOS, suivez le lien .
  • Vous pouvez également lire sur la création, la configuration et l'utilisation de conteneurs avec l'analyseur de code statique PVS-Studio installé par le lien .
  • Documentation TravisCI .

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


All Articles