PVS-Studio in den Wolken: GitLab CI / CD

Abbildung 2

Dieser Artikel setzt die Veröffentlichungsreihe zur Verwendung von PVS-Studio in Cloud-Systemen fort. Dieses Mal schauen wir uns an, wie der Analyzer mit GitLab CI zusammenarbeitet, einem Produkt von GitLab Inc. Die Integration des Static Analyzers in ein CI-System ermöglicht die Erkennung von Fehlern direkt nach der Projekterstellung und ist eine äußerst effektive Methode, um die Kosten für die Fehlersuche zu senken.

Eine Liste unserer anderen Artikel zur Integration in Cloud-CI-Systeme:


Informationen zur verwendeten Software


GitLab ist ein Onlinedienst zur Verwaltung von Repositorys. Sie können es direkt in einem Browser auf der offiziellen Website verwenden, indem Sie Ihr Konto registrieren, oder es auf Ihrem eigenen Server installieren und bereitstellen.

PVS-Studio ist ein Tool zur Erkennung von Fehlern und potenziellen Schwachstellen im Quellcode von Programmen, die in C, C ++, C # und Java geschrieben wurden. Funktioniert in 64-Bit-Systemen unter Windows, Linux und MacOS und kann Code für 32-Bit-, 64-Bit- und Embedded-ARM-Plattformen analysieren. Wenn Sie den Analyzer zum ersten Mal verwenden, um Ihre Projekte zu überprüfen, empfehlen wir Ihnen, den Artikel zu lesen, in dem beschrieben wird, wie Sie die interessantesten PVS-Studio-Warnungen schnell überprüfen und die Funktionen des Tools bewerten können.

Das OBS-Projekt wird verwendet, um die Fähigkeiten des statischen Analysators in der Cloud zu demonstrieren. Open Broadcaster Software ist eine kostenlose und offene Sammlung von Programmen für die Videoaufnahme und das Streaming. OBS bietet Geräte- und Quellenüberwachung in Echtzeit, Szenenkomposition, Dekodierung, Aufnahme und Ausstrahlung. Die Daten werden hauptsächlich über das Real Time Messaging-Protokoll übertragen und können an jede Quelle gesendet werden, die RTMP unterstützt. Das Programm verfügt über fertige Vorinstallationen für die Live-Übertragung auf den beliebtesten Streaming-Plattformen.

Konfiguration


Um mit GitLab zu arbeiten, gehen Sie auf die Website und klicken Sie auf Registrieren :

Abbildung 6

Sie können sich registrieren, indem Sie Konten anderer Dienste wie GitHub, Twitter, Google, BitBucket, Saleforce verknüpfen oder einfach das offene Formular ausfüllen. Nach der Autorisierung lädt GitLab uns ein, ein Projekt zu erstellen:

Abbildung 7

Eine Liste der verfügbaren Plattformen zum Importieren von Dateien:

Abbildung 33


Lassen Sie uns aus Gründen der Übersichtlichkeit ein leeres Projekt erstellen:

Abbildung 17


Als nächstes müssen wir unser Projekt in das erstellte Repository hochladen. Verwenden Sie dazu die Hinweise, die im Fenster des erstellten Projekts angezeigt werden.

Abbildung 1


Wenn Sie die Aufgabe starten, entnimmt GitLab CI Anweisungen aus der Datei .gitlab-ci.yml . Sie können es hinzufügen, indem Sie entweder auf CI / CD einrichten klicken oder einfach ein lokales Repository erstellen und auf die Site hochladen. Folgen wir der ersten Option:

Abbildung 21



Erstellen Sie jetzt einen minimalen Wrapper für das Skript:

image: debian job: script: 

Laden Sie den Analyzer und das Sendemail-Dienstprogramm herunter, die wir später benötigen werden:

 - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail 

Als Nächstes installieren wir Abhängigkeiten und Dienstprogramme zum Erstellen von OBS:

 - apt-get -y install build-essential cmake make pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev libio-socket-ssl-perl libnet-ssleay-perl ca-certificates 

Jetzt müssen wir die Datei mit der Analyzer-Lizenz erstellen (Standardmäßig wird die Datei PVS-Studio.lic im Verzeichnis ../.config/PVS-Studio erstellt). Dabei muss die Lizenzdatei nicht in den laufenden Parametern des Analysators angegeben werden, sie wird automatisch abgerufen.):

 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY 

Hier sind PVS_NAME und PVS_KEY die Namen von Variablen, deren Werte wir in den Einstellungen angeben. Sie speichern den PVS-Studio-Login und den Lizenzschlüssel. Gehen Sie wie folgt vor, um die Werte festzulegen: Einstellungen> CI / CD> Variablen.

Abbildung 23


Erstellen Sie das Projekt mit cmake:

 - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 

Danach führen Sie den Analyzer aus:

 - pvs-studio-analyzer analyze -o PVS-Studio.log 

PVS-Studio.log speichert die Analyseergebnisse. Die resultierende Datei mit dem Bericht ist nicht zum Lesen vorgesehen. Um es für ein menschliches Auge zugänglich zu machen, benötigen wir das Dienstprogramm plog-converter. Dieses Programm konvertiert das Protokoll des Analysators in verschiedene Formate. Zum einfachen Lesen konvertieren wir das Protokoll in das HTML-Format:

 - plog-converter -t html PVS-Studio.log -o PVS-Studio.html 

Sie können den Bericht mit Artefakten exportieren, aber wir ändern den Kurs und senden die Datei mit den Analyseergebnissen per E-Mail mit dem Dienstprogramm sendemail:

 - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

Vollständige .gitlab-ci.yml:

 image: debian job: script: - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail - apt-get -y install build-essential cmake pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev make libio-socket-ssl-perl libnet-ssleay-perl ca-certificates - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 - pvs-studio-analyzer analyze -o PVS-Studio.log - plog-converter -t html PVS-Studio.log -o PVS-Studio.html - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

Klicken Sie auf Änderungen festschreiben. Wenn wir alles richtig gemacht haben, sehen wir die Ausgabe: Diese GitLab CI-Konfiguration ist gültig. Um den Fortschritt zu verfolgen, gehen wir zur Registerkarte CI / CD> Pipelines .

Abbildung 5


Klicken Sie auf " Laufen" . Es wird das Terminalfenster der virtuellen Maschine angezeigt, in dem unsere Konfigurationsdatei ausgeführt wird. Nach einer Weile bekommen wir eine Nachricht: job succeeded.

Abbildung 29


Es ist also Zeit, die HTML-Datei mit Warnungen zu öffnen, die per E-Mail gesendet werden.

Analyseergebnisse


Werfen wir einen Blick auf einige Warnungen aus dem Bericht, in denen Fehler im Open Broadcaster-Softwareprojekt aufgedeckt werden, um das Wesentliche der statischen Codeanalyse zu erfahren. Da der Hauptzweck des Artikels darin besteht, die Prinzipien der Interaktion zwischen PVS-Studio und GitLab CI / CD zu beschreiben, wurden nur einige nicht triviale Beispiele herausgegriffen. Wir sind bereit, den Autoren des Projekts eine zeitlich begrenzte Lizenz zu erteilen. Auf Wunsch können sie auch eine gründlichere Projektanalyse durchführen. Darüber hinaus können sie eine der Möglichkeiten nutzen, um eine kostenlose PVS-Studio-Lizenz zu erhalten .

Außerdem kann jeder einen Testschlüssel erhalten , um die PVS-Studio-Funktionen zu erkunden und seine Projekte zu überprüfen.

Schauen wir uns also einige Beispiele für gefundene Fehler in der Open Broadcaster-Software an.

Warnung N1

V547 Ausdruck 'back_size' ist immer wahr. circlebuf.h (138)

 struct circlebuf { .... size_t capacity; }; static inline void circlebuf_place(struct circlebuf *cb, size_t position,....,const void *data, size_t size) { .... size_t data_end_pos; data_end_pos = position + size; if (data_end_pos > cb->capacity) { size_t back_size = data_end_pos - cb->capacity; if (back_size) { memcpy((uint8_t *)cb->data + position, data, loop_size); } .... } 

Die Zeile if (data_end_pos> cb-> capacity) ist auf jeden Fall einen Blick wert. Wenn die Bedingung wahr ist, ist die in der folgenden Zeile definierte Variable back_size immer größer als Null, da hier die Subtraktion des notorisch kleineren Werts von dem größeren Wert behandelt wird. Am Ende ist die Bedingung, die zwei Zeilen weiter unten steht, immer wahr . Die redundante Bedingung ist nicht so harmlos, wenn der Code folgt und Daten ändert.

Warnungen N2, N3

V629 Betrachten Sie den Ausdruck '1 << Einzug'. Bitverschiebung des 32-Bit-Wertes mit anschließender Erweiterung auf den 64-Bit-Typ. profiler.c (610)

 static void profile_print_entry(uint64_t active, unsigned indent, ....) { .... active &= (1 << indent) - 1; .... } 

Verwirrte Operationen über 32-Bit- und 64-Bit-Typen sehen hier verdächtig aus. Zuerst wertet der Programmierer die Maske unter Verwendung von 32-Bit-Typen aus (Ausdruck (1 << Einzug) - 1 ), danach wird sie implizit in den 64-Bit-Typ im Ausdruck active & = ... erweitert. Höchstwahrscheinlich war bei der Auswertung der Maske auch die Verwendung von 64-Bit-Typen erforderlich.

Richtige Codeversion:

 active &= ((uint64_t)(1) << indent) - 1; 

Oder:

 active &= (1ull << indent) - 1; 

Übrigens, die Copy-Paste-Version dieses Blocks ist unten, der Analysator hat auch die Warnung dafür ausgegeben: V629 Betrachten Sie den Ausdruck '1 << Indent'. Bitverschiebung des 32-Bit-Wertes mit anschließender Erweiterung auf den 64-Bit-Typ. profiler.c (719)

Warnung N4

V761 Es wurden vier identische Textblöcke gefunden. 'obs-audio-controls.c' (353)

 static float get_true_peak(....) { .... peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); .... } 

Vier identische Blöcke. In fast allen Fällen weist ein solcher Code auf einen Fehler beim Kopieren und Einfügen hin . Diese Funktionen müssen höchstwahrscheinlich mit unterschiedlichen Argumenten aufgerufen worden sein. Auch wenn nicht, sieht dieser Code seltsam aus. Eine gute Lösung wäre, den Block nur einmal zu schreiben und ihn in eine Schleife zu packen:

 for(size_t i = 0; i < 3; i++) { peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); } 

Warnung n5

V560 Ein Teil eines bedingten Ausdrucks ist immer falsch: '! Modifikatoren'. obs-hotkey.c (662)

 typedef struct obs_key_combination obs_key_combination_t; struct obs_key_combination { uint32_t modifiers; obs_key_t key; }; static inline void load_binding(....) { obs_key_combination_t combo = {0}; uint32_t *modifiers = &combo.modifiers; load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY); load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY); load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY); load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { .... } .... } 

Definition der load_modifier Funktion:

 static inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag) { if (obs_data_get_bool(data, name)) *modifiers |= flag; } 

Wie wir sehen können, ist Modifiers ein Zeiger, der durch die Adresse des Modifiers- Feldes der Combo- Struktur initialisiert wird. Da sich sein Wert bis zur Überprüfung nicht ändert, bleibt er ungleich Null. Darüber hinaus wird der Zeiger nach der Initialisierung vor der Prüfung beim Aufrufen der Funktion load_modifier verwendet, wo er dereferenziert wird. Dementsprechend ist die Prüfung von ! Modifiers sinnlos, da der Operator && bei der Auswertung des logischen Ausdrucks immer den Wert false liefert. Ich denke, der Programmierer wollte einen ganzzahligen Wert anhand der Adresse überprüfen, die im Modifikatorzeiger gespeichert ist, vergaß jedoch, diesen Zeiger dereferenzieren.

Aus diesem Grund sollte die Prüfung meines Erachtens wie folgt aussehen:

 if (!*modifiers && ....) Or like this: if (!combo.modifiers && ....) 

Warnung N6

V575 Der potenzielle Nullzeiger wird an die Funktion 'strncpy' übergeben. Untersuche das erste Argument. Zeilen prüfen: 2904, 2903. rtmp.c (2904)

 static int PublisherAuth(....) { .... ptr = malloc(r->Link.app.av_len + pubToken.av_len); strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); .... } 

In den meisten Fällen ist ein solcher Code nicht sicher, da er ignoriert, dass malloc einen Nullzeiger zurückgeben kann. Wenn malloc NULL zurückgibt, tritt in diesem Fall ein undefiniertes Verhalten auf, da das erste Argument der Funktion strncpy den Wert NULL hat.

Weitere Informationen dazu, warum es wichtig ist, den Rückgabewert der Malloc- Funktion zu überprüfen, finden Sie im entsprechenden Artikel .

Warnhinweise N7, N8, N9

Ratet mal, welche Fälle falsche Berechnungen enthalten:

 class OBSProjector : public OBSQTDisplay { .... float sourceX, sourceY, ....; .... } .... void OBSProjector::OBSRenderMultiview(....) { OBSProjector *window = (OBSProjector *)data; .... auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX += window->scenesCX; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX = window->scenesCX; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default:// MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } } .... } 

Analyzer-Warnungen:

  • V636 Der Ausdruck 'i / 6' wurde implizit vom Typ 'size_t' in den Typ 'float' umgewandelt. Erwägen Sie die Verwendung einer expliziten Typumwandlung, um den Verlust eines Bruchteils zu vermeiden. Ein Beispiel: double A = (double) (X) / Y; Fensterprojektor.cpp (330)
  • V636 Der Ausdruck 'i / 2' wurde implizit vom Typ 'size_t' in den Typ 'float' umgewandelt. Erwägen Sie die Verwendung einer expliziten Typumwandlung, um den Verlust eines Bruchteils zu vermeiden. Ein Beispiel: double A = (double) (X) / Y; Fensterprojektor.cpp (334)
  • V636 Der Ausdruck 'i / 2' wurde implizit vom Typ 'size_t' in den Typ 'float' umgewandelt. Erwägen Sie die Verwendung einer expliziten Typumwandlung, um den Verlust eines Bruchteils zu vermeiden. Ein Beispiel: double A = (double) (X) / Y; Fensterprojektor.cpp (340)

Hier ist die richtige Antwort: In denen, in denen ich nicht zum Schweben gezwungen bin. Der Analysator zeigt uns Fragmente mit ganzzahliger Teilung. Ein solcher Code funktioniert möglicherweise nicht so, wie es sich der Programmierer erhofft hatte.

Fazit


Wie wir sehen können, ist die Integration des PVS-Studio-Analysators in Ihr Projekt auf GitLab ein recht einfacher Vorgang. Dazu müssen Sie nur eine Konfigurationsdatei schreiben und in Ihrem Cloud-Repository ablegen. Da GitLab über eine eigene integrierte virtuelle Maschine verfügt, müssen wir nicht einmal viel Zeit für die Konfiguration des CI-Systems aufwenden. Mit der Codeüberprüfung können Sie Probleme direkt nach dem Build feststellen. Dies hilft, Probleme in der Phase zu beseitigen, in der ihre Komplexität und Kosten noch gering sind.

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


All Articles