PVS-Studio in den Clouds - Ausführen der Analyse auf Travis CI

Derzeit sind Cloud-CI-Systeme ein sehr gefragter Dienst. In diesem Artikel erfahren Sie, wie Sie die Analyse des Quellcodes in eine CI-Cloud-Plattform mit den Tools integrieren, die bereits in PVS-Studio verfügbar sind. Als Beispiel verwenden wir den Travis CI-Dienst.

Bild 1


Warum betrachten wir Clouds von Drittanbietern und erstellen keine eigenen? Es gibt eine Reihe von Gründen, der Hauptgrund ist, dass die SaaS-Implementierung ein ziemlich teures und schwieriges Verfahren ist. Tatsächlich ist es eine einfache und triviale Aufgabe, die PVS-Studio-Analyse direkt in eine Cloud-Plattform eines Drittanbieters zu integrieren - unabhängig davon, ob es sich um offene Plattformen wie CircleCI, Travis CI, GitLab oder eine bestimmte Unternehmenslösung handelt, die nur in einem bestimmten Unternehmen verwendet wird. Daher können wir sagen, dass PVS-Studio bereits "in den Clouds" verfügbar ist. Ein weiteres Problem ist die Implementierung und Sicherstellung des Zugriffs auf die Infrastruktur rund um die Uhr. Dies ist eine kompliziertere Aufgabe. PVS-Studio wird keine eigene Cloud-Plattform direkt für die Analyse bereitstellen.

Einige Informationen zur verwendeten Software


Travis CI ist ein Dienst zum Erstellen und Testen von Software, die GitHub als Speicher verwendet. Travis CI erfordert keine Änderung des Programmcodes für die Nutzung des Dienstes. Alle Einstellungen werden in der Datei .travis.yml vorgenommen, die sich im Stammverzeichnis des Repositorys befindet.

Wir nehmen LXC (Linux Containers) als Testprojekt für PVS-Studio. Es ist ein Virtualisierungssystem auf Betriebssystemebene zum Starten mehrerer Instanzen des Linux-Betriebssystems auf einem Knoten.

Das Projekt ist klein, aber mehr als genug für Demonstrationen. Ausgabe des Befehls cloc:
Sprache
Dateien
leer
Kommentar
Code
C.
124
11937
6758
50836
C / C ++ - Header
65
1117
3676
3774
Hinweis: LXC-Entwickler verwenden Travis CI bereits, daher nehmen wir ihre Konfigurationsdatei als Grundlage und bearbeiten sie für unsere Zwecke.

Konfiguration


Um mit Travis CI zu arbeiten, folgen wir dem Link und melden uns mit einem GitHub-Konto an.

Bild 17

Im geöffneten Fenster müssen wir uns bei Travis CI anmelden.

Bild 16

Nach der Autorisierung wird auf die Begrüßungsseite "Zum ersten Mal hier?" Weitergeleitet. Lass uns anfangen! “ , wo wir eine kurze Beschreibung finden, was danach getan werden muss, um loszulegen:

  • Aktivieren Sie die Repositorys.
  • Fügen Sie die Datei .travis.yml im Repository hinzu.
  • Starten Sie den ersten Build.

Bild 18

Beginnen wir mit diesen Aktionen.

Um unser Repository in Travis CI hinzuzufügen, gehen wir über den Link zu den Profileinstellungen und klicken auf "Aktivieren".

Bild 19

Nach dem Klicken wird ein Fenster geöffnet, in dem Repositorys ausgewählt werden, auf die die Travis CI-App Zugriff erhält.

Hinweis: Um Zugriff auf das Repository zu erhalten, muss Ihr Konto über Administratorrechte verfügen.

Bild 38

Danach wählen wir das richtige Repository aus, bestätigen die Auswahl mit der Schaltfläche "Genehmigen und installieren" und wir werden zurück zur Seite mit den Profileinstellungen weitergeleitet.

Fügen wir einige Variablen hinzu, mit denen wir die Lizenzdatei des Analysators erstellen und seine Berichte senden. Dazu gehen wir zur Einstellungsseite - der Schaltfläche "Einstellungen" rechts neben dem benötigten Repository.

Bild 39

Das Einstellungsfenster wird geöffnet.

Bild 41

Kurze Beschreibung der Einstellungen;

  • Abschnitt "Allgemein" - Konfigurieren von Autostart-Task-Triggern;
  • Im Abschnitt "Automatische Stornierung" können Sie die automatische Stornierung des Builds konfigurieren.
  • Im Abschnitt „Umgebungsvariablen“ können Umgebungsvariablen definiert werden, die sowohl offene als auch vertrauliche Informationen enthalten, z. B. Anmeldeinformationen und SSH-Schlüssel.
  • Der Abschnitt "Cron-Jobs" ist eine Konfiguration des Zeitplans für die Ausführung von Aufgaben.

Im Abschnitt "Umgebungsvariablen" erstellen wir Variablen PVS_USERNAME und PVS_KEY, die einen Benutzernamen und einen Lizenzschlüssel für den statischen Analysator enthalten. Wenn Sie keine permanente PVS-Studio-Lizenz haben, können Sie eine Testlizenz anfordern .

Bild 5

Hier erstellen wir die Variablen MAIL_USER und MAIL_PASSWORD , die einen Benutzernamen und ein E-Mail-Passwort enthalten, die wir zum Senden von Berichten verwenden.

Bild 4

Beim Ausführen von Aufgaben nimmt Travis CI Anweisungen aus der Datei .travis.yml entgegen, die sich im Stammverzeichnis des Repositorys befindet.

Mit Travis CI können wir statische Analysen sowohl direkt auf der virtuellen Maschine ausführen als auch einen vorkonfigurierten Container verwenden. Die Ergebnisse dieser Ansätze unterscheiden sich nicht voneinander. Die Verwendung eines vorkonfigurierten Containers kann jedoch hilfreich sein. Wenn wir beispielsweise bereits einen Container mit einer bestimmten Umgebung haben, in dem ein Softwareprodukt erstellt und getestet wird, und wir diese Umgebung in Travis CI nicht wiederherstellen möchten.

Erstellen Sie eine Konfiguration, um den Analysator auf einer virtuellen Maschine auszuführen.

Zum Erstellen und Testen verwenden wir eine virtuelle Maschine unter Ubuntu Trusty. Die Beschreibung finden Sie unter dem Link .

Zunächst geben wir an, dass das Projekt in C geschrieben ist, und listen Compiler auf, die wir für den Build verwenden werden:

language: c compiler: - gcc - clang 

Hinweis: Wenn Sie mehr als einen Compiler angeben, werden die Aufgaben für jeden von ihnen gleichzeitig ausgeführt. Lesen Sie hier mehr.

Vor dem Build müssen wir das Analyzer-Repository hinzufügen, Abhängigkeiten und zusätzliche Pakete festlegen:

 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 

Bevor wir ein Projekt erstellen, müssen wir Ihre Umgebung vorbereiten:

 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 

Als nächstes müssen wir eine Lizenzdatei erstellen und mit der Analyse des Projekts beginnen.

Dann erstellen wir mit dem ersten Befehl eine Lizenzdatei für den Analysator. Die Daten für die Variablen $ PVS_USERNAME und $ PVS_KEY werden aus den Projekteinstellungen übernommen.

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

Mit dem nächsten Befehl beginnen wir mit der Verfolgung des Projektaufbaus.

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

Danach führen wir eine statische Analyse durch.
Hinweis: Wenn Sie eine Testlizenz verwenden , müssen Sie den Parameter --disableLicenseExpirationCheck angeben.
  - pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-${CC}.log --disableLicenseExpirationCheck 

Die Datei mit den Analyseergebnissen wird mit dem letzten Befehl in den HTML-Bericht konvertiert.

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

Da Sie mit TravisCI das Format von E-Mail-Benachrichtigungen nicht ändern können, verwenden wir im letzten Schritt das Sendemail-Paket zum Senden von Berichten:

 - 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 

Hier ist der vollständige Text der Konfigurationsdatei zum Ausführen des Analysators auf der virtuellen Maschine:

 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 

Um PVS-Studio in einem Container auszuführen, erstellen wir es mit der folgenden Docker-Datei vor:

 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/* 

In diesem Fall sieht die Konfigurationsdatei möglicherweise folgendermaßen aus:

 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" 

Wie Sie sehen können, tun wir in diesem Fall nichts in der virtuellen Maschine, und alle Aktionen zum Erstellen und Testen des Projekts finden im Container statt.

Hinweis : Wenn Sie den Container starten, müssen Sie den Parameter --cap-add SYS_PTRACE oder --security-opt seccomp: nicht begrenzt angeben , da ein ptrace-Systemaufruf für die Compiler-Ablaufverfolgung verwendet wird.

Als Nächstes laden wir die Konfigurationsdatei in das Stammverzeichnis des Repositorys und stellen fest, dass Travis CI über Änderungen im Projekt informiert wurde und den Build automatisch gestartet hat.

Details zum Build-Fortschritt und zur Analyse des Analysators finden Sie in der Konsole.

Bild 2

Nach Abschluss der Tests erhalten wir zwei E-Mails: die erste mit statischen Analyseergebnissen zum Erstellen eines Projekts mit gcc und die zweite mit clang.

Kurz über die Prüfergebnisse


Im Allgemeinen ist das Projekt ziemlich sauber, der Analysator gab nur 24 Warnungen mit hoher und 46 Warnungen mit mittlerer Sicherheit aus. Schauen wir uns einige interessante Benachrichtigungen an:

Redundante Bedingungen in if


V590 Überprüfen Sie den Ausdruck 'ret! = (- 1) && ret == 1'. Der Ausdruck ist übertrieben oder enthält einen Druckfehler. Anhang.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; } } .... } 

Wenn ret == 1 ist , ist es definitiv nicht gleich -1 (EOF). Redundante Prüfung, ret! = EOF kann entfernt werden.

Es wurden zwei ähnliche Warnungen ausgegeben:

  • V590 Überprüfen Sie den Ausdruck 'ret! = (- 1) && ret == 1'. Der Ausdruck ist übertrieben oder enthält einen Druckfehler. Anhang.c 579
  • V590 Überprüfen Sie den Ausdruck 'ret! = (- 1) && ret == 1'. Der Ausdruck ist übertrieben oder enthält einen Druckfehler. Anhang.c 583

Verlust von hohen Bits


V784 Die Größe der Bitmaske ist kleiner als die Größe des ersten Operanden. Dies führt zum Verlust höherer Bits. 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; } } .... } 

Unter Linux ist long eine 64-Bit-Ganzzahlvariable, mo-> flag eine 32-Bit-Ganzzahlvariable. Die Verwendung von mo-> flag als Bitmaske führt zum Verlust von 32 hohen Bits. Eine Bitmaske wird nach bitweiser Inversion implizit in eine 64-Bit-Ganzzahlvariable umgewandelt. Hohe Bits dieser Maske können verloren gehen.

Ich werde es anhand eines Beispiels zeigen:

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

Bild 3


Hier ist die richtige Version des Codes:

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

Der Analysator gab eine weitere ähnliche Warnung aus:

  • V784 Die Größe der Bitmaske ist kleiner als die Größe des ersten Operanden. Dies führt zum Verlust höherer Bits. conf.c 1933

Verdächtige Schleife


V612 Eine bedingungslose 'Rückkehr' innerhalb einer Schleife. 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; } 

Die Schleife wird bei der ersten Iteration gestartet und unterbrochen. Dies könnte absichtlich gemacht worden sein, aber in diesem Fall könnte die Schleife weggelassen worden sein.

Array-Index außerhalb der Grenzen


V557 Array-Unterlauf ist möglich. Der Wert des Index 'Bytes - 1' könnte -1 erreichen. 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'; } .... } 

Bytes werden im Puffer aus der Pipe gelesen. Im Fehlerfall gibt die Funktion lxc_read_nointr einen negativen Wert zurück. Wenn alles erfolgreich ist, wird vom letzten Element ein Terminal null geschrieben. Wenn jedoch 0 Bytes gelesen werden, liegt der Index außerhalb der Puffergrenzen, was zu undefiniertem Verhalten führt.

Der Analysator gab eine weitere ähnliche Warnung aus:

  • V557 Array-Unterlauf ist möglich. Der Wert des Index 'Bytes - 1' könnte -1 erreichen. network.c 2725

Pufferüberlauf


V576 Falsches Format. Überprüfen Sie das dritte tatsächliche Argument der Funktion 'sscanf'. Es ist gefährlich, einen String-Bezeichner ohne Breitenangabe zu verwenden. Pufferüberlauf ist möglich. 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; } .... } .... } 

In diesem Fall kann die Verwendung von sscanf gefährlich sein, da der Index beim Bilden des Namenspuffers außerhalb der Grenzen liegt, wenn der oparq- Puffer größer als der Namenspuffer ist.

Fazit


Wie wir sehen, ist es eine recht einfache Aufgabe, eine Überprüfung des statischen Code-Analysators in einer Cloud zu konfigurieren. Dazu müssen wir nur eine Datei in ein Repository einfügen und wenig Zeit für die Einrichtung des CI-Systems aufwenden. Als Ergebnis erhalten wir ein Tool, mit dem Probleme beim Schreiben von Code erkannt werden können. Mit diesem Tool können wir verhindern, dass Fehler in die nächsten Testphasen gelangen, in denen ihre Behebung viel Zeit und Mühe erfordert.

Natürlich ist die Nutzung von PVS-Studio mit Cloud-Plattformen nicht nur auf Travis CI beschränkt. Ähnlich wie bei der im Artikel beschriebenen Methode kann die PVS-Studio-Analyse mit kleinen Unterschieden in andere gängige Cloud-CI-Lösungen wie CircleCI, GitLab usw. integriert werden.

Nützliche Links


  • Weitere Informationen zum Ausführen von PVS-Studio unter Linux und MacOS finden Sie unter dem Link .
  • Über den Link können Sie auch Informationen zum Erstellen, Einstellen und Verwenden von Containern mit installiertem PVS-Studio Static Code Analyzer lesen.
  • TravisCI-Dokumentation .

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


All Articles