PVS-Studio geht in die Cloud - Starten Sie die Analyse auf Travis CI

Derzeit sind Cloud-CI-Systeme ein sehr beliebter Dienst. In diesem Artikel erfahren Sie, wie Sie mithilfe der in PVS-Studio verfügbaren Tools die Quellcodeanalyse am Beispiel des Travis CI-Dienstes in die Cloud-CI-Plattform integrieren können.

Bild 1


Warum betrachten wir Clouds von Drittanbietern und machen keine eigenen? Es gibt eine Reihe von Gründen, und der Hauptgrund ist, dass SaaS ein ziemlich teures und schwieriges Verfahren ist. Die direkte Integration der PVS-Studio-Analyse in eine Cloud-Plattform eines Drittanbieters (ob es sich um offene Plattformen wie CircleCI, Travis CI, GitLab oder eine spezialisierte Unternehmenslösung handelt, die nur in einem bestimmten Unternehmen verwendet wird) ist eine recht einfache und triviale Aufgabe. Das heißt, wir können sagen, dass PVS-Studio bereits „in den Clouds“ verfügbar ist . Ein völlig anderes Thema ist die Organisation und Bereitstellung der Infrastruktur für solche Arbeiten rund um die Uhr. Dies ist eine völlig andere Aufgabe, und PVS-Studio hat nicht vor, eine eigene Cloud-Plattform bereitzustellen, auf der Analysen durchgeführt werden können.

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, um den Dienst nutzen zu können. Alle Einstellungen erfolgen in der Datei .travis.yml im Stammverzeichnis des Repositorys.

Wir werden LXC (Linux Containers) als Testprojekt zum Testen mit PVS-Studio verwenden. Es ist ein Virtualisierungssystem auf Betriebssystemebene zum Ausführen mehrerer Instanzen des Linux-Betriebssystems auf einem einzelnen Knoten.

Das Projekt ist klein, aber mehr als genug, um es zu demonstrieren. Die 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.

Anpassung


Um mit Travis CI zu beginnen, folgen Sie dem Link und authentifizieren Sie sich mit einem GitHub-Konto.

Bild 17

In dem sich öffnenden Fenster müssen Sie Travis CI autorisieren.

Bild 16

Nach der Autorisierung eine Weiterleitung zur Begrüßungsseite „Zum ersten Mal hier? Lass uns anfangen! “ , die kurz beschreibt, was als nächstes getan werden muss, um loszulegen:

  • Repositorys aktivieren;
  • Fügen Sie die Datei .travis.yml zum Repository hinzu.
  • Führen Sie den ersten Build aus.

Bild 18

Wir werden beginnen, diese Punkte auszuführen.

Um unser Repository zu Travis CI hinzuzufügen, gehen Sie über den Link zu den Profileinstellungen und klicken Sie auf die Schaltfläche "Aktivieren".

Bild 19

Nach dem Klicken wird ein Fenster mit einer Auswahl von Repositorys geöffnet, auf die die Travis CI-Anwendung Zugriff erhält.
Hinweis: Um Zugriff auf das Repository zu erhalten, muss das Konto über Administratorrechte verfügen.

Bild 38

Wir wählen das gewünschte Repository aus, bestätigen die Auswahl mit der Schaltfläche "Genehmigen & Installieren" und werden zurück zur Seite mit den Profileinstellungen weitergeleitet.

Erstellen Sie sofort die Variablen, mit denen wir die Analyzer-Lizenzdatei erstellen und ihre Berichte senden. Gehen Sie dazu zur Einstellungsseite - der Schaltfläche "Einstellungen" rechts neben dem gewünschten Repository.

Bild 39

Das Einstellungsfenster wird geöffnet.

Bild 41

Kurze Beschreibung der Einstellungen:

  • Abschnitt "Allgemein" - Einstellen von Triggern für Autorun-Aufgaben;
  • Abschnitt "Automatische Abbrechen" - Ermöglicht das Konfigurieren der Baugruppe zum automatischen Abbrechen.
  • Abschnitt "Umgebungsvariablen" - Mit dieser Option können Sie Umgebungsvariablen definieren, die sowohl öffentliche als auch vertrauliche Informationen enthalten, z. B. Anmeldeinformationen und SSH-Schlüssel.
  • Abschnitt „Cron-Jobs“ - Festlegen des Zeitplans für den Start der Aufgabe.

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

Bild 5

Erstellen Sie sofort die Variablen MAIL_USER und MAIL_PASSWORD, die den Benutzernamen und das Kennwort der Mailbox enthalten, mit denen wir Berichte senden.

Bild 4

Wenn die Aufgabe gestartet wird, 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 direkt in der virtuellen Maschine ausführen oder dafür einen vorkonfigurierten Container verwenden. Die Ergebnisse dieser Ansätze unterscheiden sich nicht voneinander. Die Verwendung eines vorkonfigurierten Containers kann jedoch nützlich sein, wenn beispielsweise bereits ein Container mit einer bestimmten Umgebung vorhanden ist, in der das Softwareprodukt erstellt und getestet wird, und diese Umgebung in Travis CI nicht wiederhergestellt werden soll .

Erstellen wir eine Konfiguration zum Ausführen des Analysators in einer virtuellen Maschine.

Zum Zusammenbau und Testen verwenden wir eine virtuelle Maschine, die auf Ubuntu Trusty basiert. Die Beschreibung finden Sie hier .

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

language: c compiler: - gcc - clang 

Hinweis : Wenn Sie mehr als einen Compiler angeben, werden die Aufgaben für jeden von ihnen parallel gestartet. Lesen Sie mehr in der Dokumentation .

Bevor wir mit dem Build beginnen, müssen wir das Analyzer-Repository hinzufügen, die Abhängigkeiten und zusätzliche Pakete installieren:

 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 Sie das Projekt erstellen, müssen Sie die 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 Datei mit einer Lizenz erstellen und die Projektanalyse ausführen.

Der erste Befehl erstellt eine Lizenzdatei für den Analysator. Die Daten für die Variablen $ PVS_USERNAME und $ PVS_KEY stammen aus den Projekteinstellungen.

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

Der folgende Befehl startet die Projektassembly-Ablaufverfolgung:

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

Nachdem wir die statische Analyse gestartet haben.
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 

Mit dem letzten Befehl wird die Analyser-Ergebnisdatei in einen HTML-Bericht konvertiert.

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

Da TravisCI das Ändern des Formats von E-Mail-Benachrichtigungen nicht zulässt, verwenden wir das Sendemail-Paket, um im letzten Schritt Berichte zu senden:

 - 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 

Volltext der Konfigurationsdatei zum Ausführen des Analysators in einer 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 einen statischen Analysator in einem Container auszuführen, erstellen Sie ihn zunächst mit der folgenden Docker-Datei:

 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, führen wir in diesem Fall nichts in der virtuellen Maschine aus, und absolut alle Aktionen zum Zusammenstellen und Testen des Projekts finden im Container statt.

Hinweis : Beim Starten des Containers müssen Sie den Parameter --cap-add SYS_PTRACE oder --security-opt seccomp: uncfined angeben , da der Systemaufruf ptrace zum Kompilieren des Trace verwendet wird.

Wir laden die Konfigurationsdatei in das Stammverzeichnis des Repositorys und stellen fest, dass Travis CI eine Benachrichtigung über das Vorhandensein von Änderungen im Projekt erhalten und die Assembly automatisch gestartet hat.

Detaillierte Informationen zum Fortschritt der Montage und zur Überprüfung durch den Analysator finden Sie in der Konsole.

Bild 2

Nach Abschluss der Tests erhalten wir zwei Briefe per Post: einen mit den Ergebnissen der statischen Analyse für die Erstellung des Projekts mit gcc und einen mit clang.

Kurz zu den Testergebnissen


Im Allgemeinen ist das Projekt ziemlich sauber, der Analysator gab nur 24 kritische und 46 durchschnittliche Warnungen aus. Betrachten Sie einige interessante Benachrichtigungen, um die Arbeit zu demonstrieren:

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). Übermäßige Validierung, ret! = EOF kann entfernt werden.

Zwei weitere Warnungen wurden 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 der 32 höchstwertigen Bits. Das implizite Umwandeln der Bitmaske in eine 64-Bit-Ganzzahlvariable nach bitweiser Inversion wird durchgeführt. Die hohen Bits dieser Maske sind Null.

Demonstrieren Sie mit einem Beispiel:

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

Bild 3


Der richtige Code lautet:

 *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ächtiger Zyklus


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; } 

Der Zyklus beginnt und wird bei der ersten Iteration unterbrochen. Vielleicht war dies beabsichtigt, aber dann kann der Zyklus weggelassen werden.

Über die Grenzen eines Arrays hinausgehen


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 aus der Pipe in den Puffer eingelesen. Im Fehlerfall gibt die Funktion lxc_read_nointr einen negativen Wert zurück. Wenn alles gut gegangen ist, wird das Null-Terminal als letztes Element geschrieben. Wenn jedoch 0 Bytes gelesen werden, überschreitet der Puffer die Grenzen, 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; } .... } .... } 

Die Verwendung von sscanf kann in diesem Fall gefährlich sein, da die Länge des oparq- Puffers größer als die Länge des Namenspuffers ist und bei der Bildung des Namenspuffers ins Ausland geht.

Fazit


Wie wir gesehen haben, ist das Einrichten einer statischen Code-Analyse-Überprüfung unseres Projekts in der Cloud eine ziemlich einfache Aufgabe. Dazu müssen Sie nur eine Datei zum Repository hinzufügen und die minimale Zeit für die Einrichtung des CI-Systems aufwenden. Als Ergebnis erhalten wir ein Tool, mit dem Sie problematischen Code in der Schreibphase identifizieren können und das es Fehlern nicht ermöglicht, in die nächsten Testphasen zu gelangen, in denen ihre Korrektur mehr Zeit und Ressourcen in Anspruch nimmt.

Die Verwendung von PVS-Studio in Verbindung mit Cloud-Plattformen ist natürlich nicht auf Travis CI beschränkt. In Analogie zu der im Artikel beschriebenen Methode kann die PVS-Studio-Analyse mit minimalen Unterschieden in andere gängige Cloud-basierte CI-Lösungen wie CircleCI, GitLab usw. integriert werden.

Nützliche Links


  • Weitere Informationen zum Starten von PVS-Studio unter Linux und MacOS finden Sie hier .
  • Informationen zum Erstellen, Konfigurieren und Verwenden von Containern mit dem installierten statischen PVS-Studio-Analysegerät finden Sie hier .
  • TravisCI-Dokumentation .



Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Oleg Andreev. PVS-Studio in den Clouds - Ausführen der Analyse auf Travis CI

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


All Articles