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.
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.
Nach der Autorisierung der Anwendung (durch Klicken auf die grüne Schaltfläche "Authorize circleci") werden wir zu "Welcome to CircleCI!" Weitergeleitet. Seite:
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.
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.
Wir gehen zur Seite "Umgebungsvariablen". Hier erstellen wir zwei Variablen,
PVS_USERNAME und
PVS_KEY , die den Benutzernamen und den Analysator-Lizenzschlüssel enthalten.
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:
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:
Sobald diese Datei in das Repository hochgeladen wurde, startet CircleCI den Build automatisch.
Nach Abschluss des Auftrags können die Dateien mit den Analyseergebnissen auf der Registerkarte "Artefakte" heruntergeladen werden.
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;
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),
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);
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",
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;
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. :) :)