PVS-Studio geht in die Wolken: CircleCI

Bild 2

Wir setzen die Artikelserie über die Verwendung des statischen Analysators PVS-Studio in Cloud-CI-Systemen fort. Heute erwägen wir einen anderen Service - CircleCI. Diesmal fungiert der Kodi Media Player als Analyseprojekt, in dessen Quellcode wir versuchen werden, interessante Orte zu finden.

Hinweis Weitere Artikel zur Integration von PVS-Studio in Cloud-CI-Systeme finden Sie hier:


Bevor wir direkt mit dem Einrichten und Analysieren von Analysatorwarnungen fortfahren, lassen Sie uns einige Worte über die verwendete und analysierte Software sagen.

CircleCI ist ein Cloud-basierter CI-Service zur Automatisierung des Zusammenbaus, Testens und Publizierens von Software. Es unterstützt die Zusammenstellung von Projekten sowohl in Containern als auch in virtuellen Maschinen unter Windows, Linux und MacOS.

Kodi ist ein kostenloser plattformübergreifender Open Source-Mediaplayer. Ermöglicht die Wiedergabe von Audio- und Videodateien, die sich sowohl auf Ihrem Heimcomputer als auch in einem lokalen Netzwerk oder im Internet befinden. Es unterstützt Themen und Funktionen durch die Installation von Plugins. Verfügbar für Windows, Linux, MacOS und Android.

PVS-Studio ist ein statischer Code-Analysator für die Suche nach Fehlern und potenziellen Schwachstellen in Code, der in C, C ++, C # und Java geschrieben wurde. Funktioniert unter Windows, Linux und MacOS.

Anpassung


Gehen Sie zur CircleCI -Homepage und klicken Sie auf die Schaltfläche " Anmelden "

Bild 1

Auf der nächsten Seite werden wir aufgefordert, uns bei einem GitHub- oder Bitbucket-Konto zu authentifizieren. Wählen Sie GitHub aus und rufen Sie die Autorisierungsseite der CircleCI-Anwendung auf.

Bild 3

Wir autorisieren die Anwendung (grüner Button "Authorize circleci") und leiten uns auf die Begrüßungsseite "Welcome to CircleCI!" Weiter.

Bild 4

Auf dieser Seite können wir sofort konfigurieren, welche Projekte in CircleCI zusammengestellt werden. Wir markieren unser Repository und klicken auf "Folgen".

Nach dem Hinzufügen des Repositorys startet CircleCI den Build automatisch, jedoch seitdem Es befindet sich noch keine Konfigurationsdatei im Repository. Die Build-Task schlägt fehl.

Bild 5

Vor dem Hinzufügen der Konfigurationsdatei fügen wir den Projektvariablen Lizenzdaten für den Analysator hinzu. Klicken Sie dazu im linken Bereich auf „Einstellungen“, wählen Sie dann in der Gruppe „ORGANISATION“ das Element „Projekte“ aus und klicken Sie auf das Zahnrad rechts neben dem gewünschten Projekt. Ein Einstellungsfenster wird geöffnet.

Bild 6

Wir interessieren uns für den Abschnitt "Umgebungsvariablen". Wir gehen darauf ein und erstellen die Variablen PVS_USERNAME und PVS_KEY , die den Benutzernamen und den Lizenzschlüssel für den Analysator enthalten.

Bild 7

Beim Starten eines Projektaufbaus liest CircleCI die Aufgabenkonfiguration aus einer Datei im Repository entlang des Pfads .circleci / config.yml. Füge es hinzu.

Zunächst geben wir das Image der virtuellen Maschine an, auf der der Analysator gestartet wird. Eine vollständige Liste der Bilder finden Sie hier .

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

Fügen Sie als Nächstes die erforderlichen Repositorys hinzu, um die Projektabhängigkeiten 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 

Fügen Sie das PVS-Studio-Repository hinzu und installieren Sie den Analysator:

 - 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" 

Sammeln wir die Projektabhängigkeiten:

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

Generieren Sie Makefiles im Assembly-Verzeichnis:

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

Der nächste Schritt besteht darin, eine statische Analyse unseres Projekts zu konfigurieren und auszuführen.

Erstellen Sie zunächst eine Datei mit der Analysatorlizenz. Der zweite Befehl startet die Kompilierung des Projektassembly-Trace.

Nach dem Tracing starten wir direkt die statische Analyse. Bei Verwendung einer Testlizenz muss der Analysator mit dem Parameter gestartet werden:

--disableLicenseExpirationCheck .

Der letzte Befehl konvertiert die Datei mit den Ergebnissen 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 

Speichern Sie nach Abschluss der Tests die Analysatorberichte:

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

Volltext 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 

Wir laden die Datei in das Repository hoch und CircleCI beginnt automatisch mit der Montage des Projekts.

Bild 12

Nach Beendigung der Aufgabe können die Dateien mit den Ergebnissen des Analysators auf der Registerkarte „Artefakte“ heruntergeladen werden.

Bild 11

Analyseergebnisse


Schauen wir uns nun einige Warnungen an, die der Analysator während der Arbeit ausgegeben hat.

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"); .... } 

Gemessen an der Formatierung des Codes wurde die folgende Ausführungslogik angenommen:

  • Wenn arttypes ein Nullzeiger ist, schließen Sie die Ausführung der Methode ab.
  • Wenn arttypes ein Zeiger ungleich Null ist, löschen Sie den vectorMap-Vektor und führen Sie dann einige andere Aktionen aus.

Das fehlende Zeichen ';' Anpassungen vorgenommen; Infolgedessen entspricht die Ausführungslogik überhaupt nicht der Formatierung. Infolgedessen wird es das Folgende:

  • Wenn arttypes ein Nullzeiger ist, wird der ArtworkMap- Vektor gelöscht und die Methode wird beendet.
  • Wenn arttypes ein Zeiger ungleich Null ist, werden weitere Aktionen ausgeführt, aber der ArtworkMap- Vektor wird nicht bereinigt.

Im Allgemeinen ist es sehr unwahrscheinlich, dass kein Fehler vorliegt. Und kaum jemand würde Ausdrücke im Geiste der Rückkehr schreiben. ArtworkMap.clear (); :).

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

Achten Sie auf die mit // <= gekennzeichneten Stellen. Der Wert 0 wird in die Lastsector- Variable geschrieben und dann zweimal als bedingter Ausdruck der if-Anweisung verwendet . Da sich der Wert der Variablen weder in der Schleife noch zwischen diesen Zuweisungen ändert, werden die Zweige beider if-Anweisungen nicht ausgeführt.

Es kann sein, dass die erforderliche Funktionalität noch nicht implementiert wurde (beachten Sie die Aufgaben).

Wie Sie sehen, hat der Analysator übrigens sofort 3 Warnungen für diesen Code ausgegeben. Manchmal reichen jedoch auch nur ein paar Warnungen nicht aus, und Benutzer glauben weiterhin, dass der Analysator falsch ist ... Ein Kollege schrieb mehr darüber im Artikel " Ein Tag vom PVS-Studio-Benutzer-Support " :).

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

In diesem Fall ist das Überprüfen von values.size ()! = 2 redundant, da das Ergebnis des bedingten Ausdrucks immer falsch ist . Wenn die Ausführung in einen der Fallzweige der switch-Anweisung geht , werden dem Vektor zwei Elemente hinzugefügt, und da er leer war, wird seine Größe gleich zwei. Andernfalls (beim Ausführen des Standardzweigs ) wird die Methode beendet.

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 Variable prio wird mit dem Wert INT_MAX initialisiert und anschließend im Vergleich prio == INT_MAX auch im ternären Operator verwendet . Zwischen dem Ort der Initialisierung und der Verwendung ändert sich sein Wert jedoch nicht. Daher ist der Wert des Ausdrucks prio == INT_MAX wahr , und der ternäre Operator gibt immer 0 zurück.

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 das gleiche Muster - der Zeiger, der durch Aufrufen der Malloc- Funktion erhalten wird, wird in der memcpy- Funktion weiter verwendet, ohne zuvor nach NULL zu suchen .

Jemand möchte diesen Warnungen möglicherweise im folgenden Schlüssel widersprechen: malloc gibt niemals einen Nullzeiger zurück. Wenn dies der Fall ist, lassen Sie die Anwendung besser fallen. Dies ist ein Thema für eine lange Diskussion, aber auf die eine oder andere Weise schlage ich vor, die Notiz meines Kollegen zu lesen: " Warum ist es wichtig zu überprüfen, ob die Malloc-Funktion zurückgegeben wurde? ".

Falls gewünscht, können Sie den Analysator so konfigurieren, dass er sich so verhält, dass Malloc keinen Nullzeiger zurückgeben kann. Dann werden keine derartigen Warnungen angezeigt. Lesen Sie hier mehr darüber.

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

Die Situation ist ähnlich wie oben beschrieben. Der als Ergebnis des Aufrufs von malloc erhaltene Zeiger wird in die Eingabevariable geschrieben und anschließend verwendet, ohne nach NULL zu suchen ( entry-> d_name ).

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 progressHandler- Zeiger enthält den Wert, der durch Aufrufen des Operators new erhalten wird . Für diesen Zeiger wird jedoch kein Operatorlöschen aufgerufen, was zu einem Speicherverlust führt.

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 schränkt die Größe des Vektors m_vecPlayerConfigs aufgrund des bedingten Ausdrucks ein und verlässt die Methode, wenn sie wahr ist. Wenn die Codeausführung die letzte return-Anweisung erreicht hat , liegt die Größe des Vektors m_vecPlayerConfigs daher im angegebenen Bereich: [1; idx]. Ein paar Zeilen weiter unten ist jedoch der idx- Aufruf : m_vecPlayerConfigs [idx] -> m_bPlaysVideo . Wenn idx gleich der Größe des Vektors ist, geht die Anwendung daher über den zulässigen Bereich hinaus.

Schauen Sie sich zum Schluss einige Warnungen zum Platinum- Bibliothekscode an.

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

In diesem Fall ist die Priorität von Operationen verwirrt. Const char * ist nicht das Ergebnis der Berechnung des ternären Operators ( Abonnement? "S": "Uns" ), sondern das variable Abonnement . Zumindest sieht es komisch 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 Leerzeichencode ist 0x20, der Registerkartencode ist 0x09. Daher ist der Unterausdruck c == '\ t' immer falsch , da dieser Fall bereits durch den Ausdruck c <'' abgedeckt ist (wenn true, wird die Funktion beendet).

Fazit


Wie Sie diesem Artikel entnehmen können, ist es uns beim nächsten CI-System (CircleCI) gelungen, die Projektüberprüfung mit PVS-Studio zu konfigurieren. Ich schlage vor, dass Sie den Analysator für Ihr Projekt herunterladen und ausprobieren . Wenn Sie Fragen zur Konfiguration oder Verwendung des Analysators haben, schreiben Sie uns bitte, wir helfen Ihnen gerne weiter.

Und natürlich der Code der Nachlässigkeit für Sie, Freunde. :) :)



Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Sergey Vasiliev, Ilya Gainulin. PVS-Studio in den Wolken: CircleCI .

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


All Articles