PVS-Studio in den Wolken: CircleCI

Bild 2

Dies ist ein neuer Teil unserer Artikelserie über die Verwendung des statischen Analysators PVS-Studio mit Cloud-CI-Systemen. Heute schauen wir uns einen anderen Dienst an, CircleCI. Wir werden die Kodi Media Player-Anwendung als Testprojekt verwenden und prüfen, ob wir im Quellcode interessante Fehler finden können.
Hinweis Die vorherigen Artikel zur Integration von PVS-Studio in Cloud-CI-Systeme:


Bevor ich die Arbeitsumgebung einrichte und den Analysebericht prüfe, möchte ich einige Worte zu der Software sagen, die wir verwenden und überprüfen werden.

CircleCI ist ein Cloud-CI-Dienst zum automatisierten Erstellen , Testen und Bereitstellen von Software. Es unterstützt die Projekterstellung sowohl in Containern als auch auf virtuellen Maschinen unter Windows, Linux und macOS.

Kodi ist eine kostenlose und plattformübergreifende Open-Source-Mediaplayer-Anwendung. Benutzer können die meisten Streaming-Medien wie Videos, Musik, Podcasts und Videos aus dem Internet sowie alle gängigen digitalen Mediendateien von lokalen und Netzwerkspeichermedien abspielen und anzeigen. Es unterstützt die Verwendung von Themen und Skins sowie Funktionserweiterungen über Plugins. Kodi ist für Windows, Linux, MacOS und Android verfügbar.

PVS-Studio ist ein statischer Analysator zum Erkennen von Fehlern und potenziellen Schwachstellen im Quellcode von Anwendungen, die in C, C ++, C # und Java geschrieben wurden. Der Analysator läuft unter Windows, Linux und MacOS.

Einrichten


Zuerst müssen wir zur CircleCI -Hauptseite gehen und auf " Anmelden " klicken.

Bild 1

Auf der nächsten Seite wird uns angeboten, mit einem GitHub- oder Bitbucket-Konto zu autorisieren. Wir wählen GitHub und gelangen zur CircleCI-Autorisierungsseite.

Bild 3

Nach der Autorisierung der Anwendung (durch Klicken auf die grüne Schaltfläche "Authorize circleci") werden wir zu "Welcome to CircleCI!" Weitergeleitet. Seite:

Bild 4

Hier können wir sofort angeben, welche Projekte CircleCI erstellen soll. Wir kreuzen unser Repository an und klicken auf "Folgen".

Nach dem Hinzufügen des Repositorys startet CircleCI den Erstellungsprozess automatisch. Da wir jedoch noch keine Konfigurationsdatei in unserem Repository haben, wird der Erstellungsjob mit einer Fehlermeldung abgebrochen.

Bild 5

Vor dem Hinzufügen einer Konfigurationsdatei müssen einige Variablen hinzugefügt werden, die Lizenzdaten für den Analysator enthalten. Dazu klicken wir in der linken Seitenleiste auf "Einstellungen", wählen im Abschnitt "ORGANISATION" die Option "Projekte" und klicken auf die Zahnradschaltfläche rechts neben dem Namen unseres Projekts. Ein Einstellungsfenster wird angezeigt.

Bild 6

Wir gehen zur Seite "Umgebungsvariablen". Hier erstellen wir zwei Variablen, PVS_USERNAME und PVS_KEY , die den Benutzernamen und den Analysator-Lizenzschlüssel enthalten.

Bild 7

Beim Starten des Builds liest CircleCI die Jobkonfiguration aus der Datei, die im Repository unter .circleci / config.yml gespeichert ist. Fügen wir es hinzu.

Zuerst müssen wir das Image der virtuellen Maschine angeben, auf der der Analysator ausgeführt wird. Die vollständige Liste der Bilder finden Sie hier .

version: 2 jobs: build: machine: image: ubuntu-1604:201903-01 

Als Nächstes fügen wir die erforderlichen Repositorys hinzu, um die Abhängigkeiten des Projekts zu aktivieren und zu installieren:

 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget 

Hinzufügen des PVS-Studio-Repositorys und Installieren des Analysators:

 - run: 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 - run: sudo -- sh -c "apt-get update && apt-get install pvs-studio -y" 

Dann bauen wir die Abhängigkeiten auf:

 - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local 

Danach generieren wir Makefiles im Build-Verzeichnis:

 - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 

Der nächste Schritt ist das Einrichten und Starten einer Analyse des Projekts.

Zuerst erstellen wir eine Analyzer-Lizenzdatei. Ein anderer Befehl beginnt mit der Verfolgung des vom Compiler erstellten Projekts.

Der nächste Befehl nach der Ablaufverfolgung führt die Analyse als solche aus. Wenn Sie eine Demoversion von PVS-Studio verwenden, starten Sie diese mit dem Parameter:

--disableLicenseExpirationCheck .

Der letzte Befehl konvertiert die Berichtsdatei des Analysators in einen HTML-Bericht:

 - run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log 

Sobald die Tests beendet sind, speichern wir die Berichte:

 - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 

Hier ist der vollständige Text der Datei .circleci / config.yml:

 version: 2.1 jobs: build: machine: image: ubuntu-1604:201903-01 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget - run: 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 - run: sudo -- sh -c "apt-get update && apt-get install pvs-studio -y" - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. - run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 

Sobald diese Datei in das Repository hochgeladen wurde, startet CircleCI den Build automatisch.

Bild 12

Nach Abschluss des Auftrags können die Dateien mit den Analyseergebnissen auf der Registerkarte "Artefakte" heruntergeladen werden.

Bild 11

Analyseergebnisse


OK, jetzt schauen wir uns einige der vom Analysegerät ausgegebenen Warnungen an.

PVS-Studio-Warnung: V504 Es ist sehr wahrscheinlich, dass das Semikolon ';' fehlt nach dem Schlüsselwort 'return'. AdvancedSettings.cpp: 1476

 void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap) { if (!arttypes) return artworkMap.clear(); const TiXmlNode* arttype = arttypes->FirstChild("arttype"); .... } 

Die Code-Formatierung schlägt die folgende Ausführungslogik vor:

  • Wenn arttypes ein Nullzeiger ist, gibt die Methode zurück.
  • Wenn arttypes ein Nicht-Null-Zeiger ist, wird der ArtworkMap- Vektor gelöscht und einige Aktionen werden ausgeführt.

Aber das fehlende ';' Zeichen bricht alles und die tatsächliche Ausführungslogik ist wie folgt:

  • Wenn arttypes ein Nullzeiger ist, wird der ArtworkMap- Vektor gelöscht und die Methode wird zurückgegeben.
  • Wenn arttypes ein Nicht-Null-Zeiger ist, führt das Programm alle Aktionen aus, die als Nächstes ausgeführt werden, aber der ArtworkMap- Vektor wird nicht gelöscht.

Um es kurz zu machen, diese Situation sieht aus wie ein Fehler. Schließlich erwarten Sie kaum, dass jemand Ausdrücke wie return artistMap.clear () schreibt; :).

PVS-Studio-Warnungen:

  • V547 Der Ausdruck 'letzter Sektor' ist immer falsch. udf25.cpp: 636
  • V547 Der Ausdruck 'letzter Sektor' ist immer falsch. udf25.cpp: 644
  • V571 Wiederkehrende Prüfung. Die Bedingung 'if (letzter Sektor)' wurde bereits in Zeile 636 überprüft. Udf25.cpp: 644

 int udf25::UDFGetAVDP( struct avdp_t *avdp) { .... uint32_t lastsector; .... lastsector = 0; // <= .... for(;;) { .... if( lastsector ) { // <= V547 lbnum = lastsector; terminate = 1; } else { //! @todo Find last sector of the disc (this is optional). if( lastsector ) // <= V547 lbnum = lastsector - 256; else return 0; } } .... } 

Beachten Sie die mit // <= markierten Stellen. Der letzten Sektorvariablen wird der Wert 0 zugewiesen und dann in zwei if- Anweisungen als bedingter Ausdruck verwendet. Da sich der Wert weder in der Schleife noch zwischen den Zuweisungen ändert, gelangt die Steuerung niemals in die else- Zweige beider if- Anweisungen.

Dies kann jedoch auch bedeuten, dass die Entwickler die beabsichtigte Funktionalität einfach noch nicht implementiert haben (beachten Sie die ToDo- Bemerkung).

Übrigens, wie Sie wahrscheinlich bemerkt haben, hat dieses Snippet drei Warnungen gleichzeitig ausgelöst. Aber selbst so viele Warnungen für einen Code würden für einige Benutzer nicht überzeugend genug aussehen, und sie würden weiterhin glauben, dass der Analysator falsch ist ... Dieser Aspekt wird in einem Beitrag von einem meiner Teamkollegen ausführlich besprochen: " One Day from PVS-Studio Benutzerunterstützung ":).

PVS-Studio-Warnung: V547 Ausdruck 'values.size ()! = 2' ist immer falsch. GUIControlSettings.cpp: 1174

 bool CGUIControlRangeSetting::OnClick() { .... std::vector<CVariant> values; SettingConstPtr listDefintion = settingList->GetDefinition(); switch (listDefintion->GetType()) { case SettingType::Integer: values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorUpper)); break; case SettingType::Number: values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorUpper)); break; default: return false; } if (values.size() != 2) return false; SetValid(CSettingUtils::SetList(settingList, values)); return IsValid(); } 

Die Prüfung values.size ()! = 2 ist hier redundant, da dieser bedingte Ausdruck immer als falsch ausgewertet wird. Wenn die Ausführung in einen der Fallzweige der switch- Anweisung eintritt, werden dem Vektor zwei Elemente hinzugefügt, und da er ursprünglich leer war, wird seine Größe natürlich gleich 2. Andernfalls (dh wenn der Standardzweig ausgeführt wird) wird die Methode zurückgegeben.

PVS-Studio-Warnung: Der V547- Ausdruck 'prio == 0x7fffffff' ist immer wahr. DBusReserve.cpp: 57

 bool CDBusReserve::AcquireDevice(const std::string& device) { .... int prio = INT_MAX; .... res = dbus_bus_request_name( m_conn, service.c_str(), DBUS_NAME_FLAG_DO_NOT_QUEUE | (prio == INT_MAX ? 0 : DBUS_NAME_FLAG_ALLOW_REPLACEMENT), // <= error); .... } 

Die Prio- Variable wird auf den Wert INT_MAX initialisiert und dann als Operand des ternären Operators im Vergleich prio == INT_MAX verwendet , obwohl sich ihr Wert nach der Initialisierung nicht ändert. Dies bedeutet, dass der Ausdruck prio == INT_MAX wahr ist und der ternäre Operator immer 0 zurückgibt .

PVS-Studio-Warnungen:

  • V575 Der potenzielle Nullzeiger wird an die Funktion 'memcpy' übergeben. Überprüfen Sie das erste Argument. Überprüfen Sie die Zeilen: 39, 38. DVDOverlayImage.h: 39
  • V575 Der potenzielle Nullzeiger wird an die Funktion 'memcpy' übergeben. Überprüfen Sie das erste Argument. Überprüfen Sie die Zeilen: 44, 43. DVDOverlayImage.h: 44

 CDVDOverlayImage(const CDVDOverlayImage& src) : CDVDOverlay(src) { Data = (uint8_t*)malloc(src.linesize * src.height); memcpy(data, src.data, src.linesize * src.height); // <= if(src.palette) { palette = (uint32_t*)malloc(src.palette_colors * 4); memcpy(palette, src.palette, src.palette_colors * 4); // <= } .... } 

Beide Warnungen haben dasselbe Muster: Ein von der malloc- Funktion zurückgegebener Zeiger wird in der memcpy- Funktion weiter verwendet, ohne zuvor auf NULL überprüft zu werden.

Einige argumentieren möglicherweise, dass malloc niemals einen Nullzeiger zurückgibt, und wenn dies der Fall ist, ist es besser, wenn die Anwendung abstürzt. Es ist ein Thema einer separaten Diskussion, aber was auch immer Ihre Meinung ist, ich empfehle, diesen Beitrag meines Teamkollegen zu lesen: " Warum es wichtig ist, zu überprüfen, was die Malloc-Funktion zurückgegeben hat ".

Wenn Sie möchten, können Sie den Analysator so anpassen, dass nicht davon ausgegangen wird, dass malloc einen Nullzeiger zurückgeben kann. Dadurch wird verhindert, dass diese Art von Warnungen ausgegeben wird. Weitere Details finden Sie hier .

PVS-Studio-Warnung: V522 Möglicherweise wird ein potenzieller Nullzeiger-Eintrag dereferenziert. Überprüfen Sie die Zeilen: 985, 981. emu_msvcrt.cpp: 985

 struct dirent *dll_readdir(DIR *dirp) { .... struct dirent *entry = NULL; entry = (dirent*) malloc(sizeof(*entry)); if (dirData->curr_index < dirData->items.Size() + 2) { if (dirData->curr_index == 0) strncpy(entry->d_name, ".\0", 2); .... } 

Dieses Beispiel ähnelt dem vorherigen. Der von der malloc- Funktion zurückgegebene Zeiger wird in der Eingabevariablen gespeichert, und diese Variable wird dann ohne vorherige Nullprüfung ( entry-> d_name ) verwendet.

PVS-Studio-Warnung: V773 Der Sichtbarkeitsbereich des Zeigers 'progressHandler' wurde beendet, ohne den Speicher freizugeben. Ein Speicherverlust ist möglich. PVRGUIChannelIconUpdater.cpp: 94

 void CPVRGUIChannelIconUpdater::SearchAndUpdateMissingChannelIcons() const { .... CPVRGUIProgressHandler* progressHandler = new CPVRGUIProgressHandler(g_localizeStrings.Get(19286)); for (const auto& group : m_groups) { const std::vector<PVRChannelGroupMember> members = group->GetMembers(); int channelIndex = 0; for (const auto& member : members) { progressHandler->UpdateProgress(member.channel->ChannelName(), channelIndex++, members.size()); .... } progressHandler->DestroyProgress(); } 

Der Wert des progressHandler- Zeigers wurde vom neuen Operator zurückgegeben. Es gibt jedoch keinen Löschoperator für diesen Zeiger. Dies bedeutet einen Speicherverlust.

PVS-Studio-Warnung: V557- Array-Überlauf ist möglich. Der 'idx'-Index zeigt über die Array-Grenze hinaus. PlayerCoreFactory.cpp: 240

 std::vector<CPlayerCoreConfig *> m_vecPlayerConfigs; bool CPlayerCoreFactory::PlaysVideo(const std::string& player) const { CSingleLock lock(m_section); size_t idx = GetPlayerIndex(player); if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size()) return false; return m_vecPlayerConfigs[idx]->m_bPlaysVideo; } 

Die if- Anweisung beschränkt die Größe des Vektors m_vecPlayerConfigs auf einen bestimmten Bereich, indem die Methode zurückgegeben wird, wenn die Bedingung für die Größenprüfung erfüllt ist. Wenn die Ausführung die letzte return- Anweisung erreicht, liegt die Größe des Vektors m_vecPlayerConfigs daher innerhalb des angegebenen Bereichs [1; idx]. Ein paar Zeilen später indiziert das Programm den Vektor unter idx : m_vecPlayerConfigs [idx] -> m_bPlaysVideo . Dies bedeutet, dass wenn idx gleich der Größe des Vektors ist, wir über den gültigen Bereich hinaus indizieren.

Lassen Sie uns diesen Artikel mit einigen Beispielen aus dem Code der Platinum- Bibliothek abschließen .

PVS-Studio-Warnung: V542 Überprüfen Sie einen ungeraden Typ: 'bool' bis 'char *'. PltCtrlPoint.cpp: 1617

 NPT_Result PLT_CtrlPoint::ProcessSubscribeResponse(...) { .... bool subscription = (request.GetMethod().ToUppercase() == "SUBSCRIBE"); .... NPT_String prefix = NPT_String::Format(" PLT_CtrlPoint::ProcessSubscribeResponse %ubscribe for service \"%s\" (result = %d, status code = %d)", (const char*)subscription?"S":"Uns", // <= (const char*)service->GetServiceID(), res, response?response->GetStatusCode():0); .... } 

Die Entwickler hatten falsche Annahmen über die Priorität der Operationen. Was in const char * umgewandelt wird, ist nicht das vom ternären Operator zurückgegebene Ergebnis ( Abonnement? "S": "Uns" ), sondern die Abonnementvariable . Das sieht zumindest seltsam aus.

PVS-Studio-Warnung: V560 Ein Teil des bedingten Ausdrucks ist immer falsch: c == '\ t'. NptUtils.cpp: 863

 NPT_Result NPT_ParseMimeParameters(....) { .... case NPT_MIME_PARAMETER_PARSER_STATE_NEED_EQUALS: if (c < ' ') return NPT_ERROR_INVALID_SYNTAX; // END or CTLs are invalid if (c == ' ' || c == '\t') continue; // ignore leading whitespace .... } 

Der Code des Leerzeichens ist 0x20 und der Code des Tabulatorzeichens ist 0x09. Daher wird der Unterausdruck c == '\ t' immer als falsch ausgewertet, da dieser Fall bereits von der Prüfung c <'' abgedeckt wird (was, wenn true, dazu führt, dass die Funktion zurückkehrt).

Fazit


Wie dieser Artikel zeigt, haben wir erfolgreich eine Analyse von PVS-Studio auf einem weiteren CI-System (CircleCI) eingerichtet. Ich lade Sie ein, den Analysator für Ihr eigenes Projekt herunterzuladen und zu testen. Wenn Sie Fragen zur Einrichtung oder Verwendung von PVS-Studio haben, zögern Sie nicht, uns zu kontaktieren - wir helfen Ihnen gerne weiter.

Und natürlich wünschen wir Ihnen fehlerfreien Code. :) :)

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


All Articles