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.
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:
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.
In dem sich öffnenden Fenster müssen Sie Travis CI autorisieren.
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.
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".
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.
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.
Das Einstellungsfenster wird geöffnet.
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 .
Erstellen Sie sofort die Variablen
MAIL_USER und
MAIL_PASSWORD, die den Benutzernamen und das Kennwort der Mailbox enthalten, mit denen wir Berichte senden.
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:
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:
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:
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.
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)
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; for (mo = &mount_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { if (mo->clear) { *flags &= ~mo->flag;
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;
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) { if (sscanf(oparg, "%s", name) < 1)
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