À l'heure actuelle, les systèmes cloud CI sont un service très populaire. Dans cet article, nous expliquerons comment, en utilisant les outils existants disponibles dans PVS-Studio, vous pouvez intégrer l'analyse de code source à la plate-forme cloud CI, en utilisant le service Travis CI à titre d'exemple.
Pourquoi examinons-nous des clouds tiers et ne créons-nous pas les nôtres? Il existe un certain nombre de raisons, et la principale est que le SaaS est une procédure assez coûteuse et difficile. En fait, l'intégration directe de l'analyse PVS-Studio avec 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écialisée utilisée dans une seule entreprise spécifique) est une tâche assez simple et triviale. Autrement dit, nous pouvons dire que
PVS-Studio est déjà disponible «dans les nuages» . Un problème complètement différent est l'organisation et la fourniture d'infrastructures pour un tel travail 24/7. Il s'agit d'une tâche complètement différente, et PVS-Studio n'a pas l'intention de fournir sa propre plate-forme cloud directement pour exécuter une analyse sur celle-ci.
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 changer le code du programme pour utiliser le service, tous les paramètres se produisent dans le fichier
.travis.yml situé à la racine du référentiel.
Nous prendrons
LXC (Linux Containers) comme projet de test pour tester avec PVS-Studio. Il s'agit d'un système de virtualisation au niveau du système d'exploitation pour exécuter plusieurs instances du système d'exploitation Linux sur un seul nœud.
Le projet est petit, mais plus que suffisant pour le démontrer. La sortie de la commande cloc:
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.
Personnalisation
Pour commencer avec Travis CI,
suivez le lien et authentifiez-vous en utilisant un compte GitHub.
Dans la fenĂŞtre qui s'ouvre, vous devez autoriser Travis CI.
Après autorisation, une redirection vers la page d'accueil «Première fois ici? Permet de commencer! ”
, qui décrit brièvement ce qui doit être fait ensuite pour commencer:
- activer les référentiels;
- ajoutez le fichier .travis.yml au référentiel;
- exécutez la première version.
Nous allons commencer à réaliser ces points.
Pour ajouter notre référentiel à Travis CI, accédez aux paramètres du profil
via le lien et cliquez sur le bouton "Activer".
Après avoir cliqué, une fenêtre s'ouvre avec un choix de référentiels auxquels l'application Travis CI aura accès.
Remarque: pour donner accès au référentiel, le compte doit disposer de droits d'administrateur sur celui-ci.
Nous sélectionnons le référentiel souhaité, confirmons la sélection avec le bouton "Approuver et installer", et nous serons redirigés vers la page des paramètres de profil.
Créez immédiatement les variables que nous utiliserons pour créer le fichier de licence de l'analyseur et envoyer ses rapports. Pour ce faire, accédez à la page des paramètres - le bouton «Paramètres» à droite du référentiel souhaité.
La fenêtre des paramètres s'ouvre.
Brève description des paramètres:
- Section "Général" - définition des déclencheurs pour les tâches d'exécution automatique;
- Section "Annulation automatique" - vous permet de configurer l'assemblage d'annulation automatique;
- Section "Variables d'environnement" - vous permet de définir des variables d'environnement contenant à la fois des informations publiques et confidentielles, telles que les informations d'identification, les clés ssh;
- Section «Cron Jobs» - définition du calendrier de lancement des tâches.
Dans la section "Variables d'environnement", nous créons les variables
PVS_USERNAME et
PVS_KEY contenant respectivement le nom d'utilisateur et la clé de licence pour l'analyseur statique. Si vous ne disposez pas d'une licence PVS-Studio permanente, vous pouvez
demander une licence d'essai .
Créez immédiatement les variables
MAIL_USER et
MAIL_PASSWORD contenant le nom d'utilisateur et le mot de passe de la boîte aux lettres, que nous utiliserons pour envoyer des rapports.
Lorsque la tâche est lancée, Travis CI prend les instructions du fichier .travis.yml situé à la racine du référentiel.
À l'aide de Travis CI, nous pouvons exécuter une analyse statique directement dans la machine virtuelle, ou utiliser un conteneur préconfiguré pour cela. Les résultats de ces approches ne sont pas différents les uns des autres, mais l'utilisation d'un conteneur préconfiguré peut être utile, par exemple, si nous avons déjà un conteneur avec un environnement spécifique dans lequel le produit logiciel est construit et testé, et qu'il n'y a pas de désir de restaurer cet environnement dans Travis CI .
Créons une configuration pour exécuter l'analyseur dans une machine virtuelle.
Pour l'assemblage et les tests, nous utiliserons une machine virtuelle basée sur Ubuntu Trusty, sa description peut être trouvée
ici .
Tout d'abord, nous indiquons que le projet est écrit en C et nous listons les compilateurs que nous utiliserons pour l'assemblage:
language: c compiler: - gcc - clang
Remarque : si vous spécifiez plusieurs compilateurs, les tâches seront lancées en parallèle pour chacun d'entre eux. En savoir plus
dans la documentation .
Avant de commencer la construction, nous devons ajouter le référentiel d'analyseur, installer les dépendances et les packages supplémentaires:
before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https:
Avant de construire le projet, vous devez préparer l'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 avec une licence et exécuter une analyse de projet.
La première commande crée un fichier de licence pour l'analyseur. 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
La commande suivante démarre la trace de l'assembly du projet:
- pvs-studio-analyzer trace -- make -j4
Après avoir commencé l'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
Avec la dernière commande, le fichier de résultats de l'analyseur est converti en rapport html.
- plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
Étant donné que TravisCI ne permet pas de modifier le format des notifications par e-mail, nous utiliserons le package sendemail pour envoyer des rapports à la dernière étape:
- 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
Texte complet du fichier de configuration pour exécuter l'analyseur dans une machine virtuelle:
language: c compiler: - gcc - clang before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https:
Pour exécuter un analyseur statique dans un conteneur, créez-le d'abord à 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:
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 absolument toutes les actions d'assemblage et de test du projet ont lieu à l'intérieur du conteneur.
Remarque : lors du démarrage du conteneur, vous devez spécifier le
paramètre --cap-add SYS_PTRACE ou
--security-opt seccomp: unconfined , car l'appel système ptrace est utilisé pour compiler la trace.
Nous chargeons le fichier de configuration à la racine du référentiel et constatons que Travis CI a reçu une notification de la présence de modifications dans le projet et a automatiquement démarré l'assemblage.
Des informations détaillées sur la progression de l'assemblage et la vérification par l'analyseur peuvent être consultées dans la console.
Une fois les tests terminés, nous recevrons 2 lettres par la poste: l'une contenant les résultats de l'analyse statique pour la construction du projet à l'aide de gcc et la seconde avec clang, respectivement.
En bref sur les résultats des tests
En général, le projet est assez propre, l'analyseur n'a émis que 24 avertissements critiques et 46 avertissements moyens. Pour démontrer le travail, considérez 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)
Si
ret == 1 , alors il n'est certainement pas égal à -1 (EOF). Validation excessive,
ret! = EOF peut être supprimé.
Deux autres avertissements 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; for (mo = &mount_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { if (mo->clear) { *flags &= ~mo->flag;
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 des 32 bits les plus significatifs. La conversion implicite du masque binaire en une variable entière 64 bits après l'inversion au niveau du bit est effectuée. Les bits hauts de ce masque seront nuls.
Démontrez avec un exemple:
unsigned long long x; unsigned y; .... x &= ~y;
Le bon code est:
*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
Cycle suspect
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; }
Le cycle démarre et à la première itération il est interrompu. Peut-être que c'était prévu, mais alors le cycle peut être omis.
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 du tube dans le tampon. En cas d'erreur, la fonction
lxc_read_nointr retournera une valeur négative. Si tout s'est bien passé, le terminal nul est écrit comme dernier élément. Cependant, si 0 octet est lu, le tampon sortira des limites, ce qui entraînera 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) { if (sscanf(oparg, "%s", name) < 1)
L'utilisation de
sscanf dans ce cas peut ĂŞtre dangereuse, car si la longueur du tampon
oparq est supérieure à la longueur du tampon de
nom , il ira à l'étranger lorsque le tampon de
nom sera formé.
Conclusion
Comme nous l'avons vu, la mise en place d'une vérification d'analyseur de code statique de notre projet dans le cloud est une tâche assez simple. Pour ce faire, il vous suffit d'ajouter un fichier au référentiel et de passer le minimum de temps à configurer le système CI. Par conséquent, nous obtenons un outil qui vous permet d'identifier le code problématique au stade de l'écriture et ne permet pas aux erreurs de passer aux étapes suivantes du test, où leur correction prendra plus de temps et de ressources.
Bien sûr, l'utilisation de PVS-Studio avec des plates-formes cloud n'est pas limitée à Travis CI. Par analogie avec la méthode décrite dans l'article, avec des différences minimes, l'analyse PVS-Studio peut être intégrée à d'autres solutions CI populaires basées sur le cloud, telles que CircleCI, GitLab, etc.
Liens utiles
- Des informations supplémentaires sur le lancement de PVS-Studio sur Linux et MacOS peuvent être trouvées ici .
- Vous pouvez lire ici comment créer, configurer et utiliser des conteneurs avec l'analyseur statique PVS-Studio installé.
- Documentation TravisCI .

Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Oleg Andreev.
PVS-Studio dans les nuages ​​- Exécution de l'analyse sur Travis CI