Unser Team bietet schnelle und effiziente Kundenbetreuung. Nur Programmierer nehmen an der Unterstützung teil, da Programmierer auch Fragen stellen und wir über viele von ihnen nachdenken müssen. Ich möchte einen der jüngsten Unterstützungsaufrufe zum Thema False Positives beschreiben, der zu einer ganz kleinen Untersuchung des im Brief beschriebenen Problems führte.
Wir arbeiten hart daran, die Anzahl der vom PVS-Studio-Analysegerät erzeugten Fehlalarme zu reduzieren. Leider können statische Analysatoren oft nicht den richtigen Code vom Fehler unterscheiden, da sie einfach nicht genügend Informationen haben. Infolgedessen gibt es sowieso falsch positive Ergebnisse. Dies ist jedoch kein Problem, da nach dem Einstellen des Analysators leicht eine Situation erreicht werden kann, in der
9 von 10 Warnungen auf echte Fehler hinweisen.
Obwohl Fehlalarme kein so großes Problem darstellen, wie es auf den ersten Blick erscheinen mag, kämpfen wir ständig mit ihnen und verbessern die Diagnose. Wir bemerken selbst einige offensichtliche Fehlalarme, von denen einige von Kunden und kostenlosen Benutzern an uns geschrieben wurden.
Kürzlich hat einer unserer Kunden einen Brief mit ungefähr folgendem Inhalt geschrieben:
Aus irgendeinem Grund sagt der Analysator, dass der Zeiger immer null ist, obwohl dies eindeutig nicht der Fall ist. Darüber hinaus verhält sich der Analysator bei einem Testprojekt seltsam und instabil: Er gibt entweder eine Warnung aus oder nicht. Ein synthetisches Beispiel, das ein falsches Positiv reproduziert:#include <windows.h> #include <aclapi.h> #include <tchar.h> int main() { PACL pDACL = NULL; PSECURITY_DESCRIPTOR pSD = NULL; ::GetNamedSecurityInfo(_T("ObjectName"), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSD); auto test = pDACL == NULL; // V547 Expression 'pDACL == 0' is always true. return 0; }
Ich kann mir vorstellen, wie ähnlich die Antworten unserer Benutzer aussehen. Es ist sofort klar, dass die Funktion
GetNamedSecurityInfo den Wert der Variablen
pDACL ändert. Könnten die Entwickler wirklich nicht in der Lage sein, einen Handler für solch einfache Situationen zu schreiben? Darüber hinaus ist nicht klar, warum der Analysator entweder eine Nachricht ausgibt oder nicht. Vielleicht haben sie selbst einen Fehler im Tool, z. B. eine nicht initialisierte Variable?
Eh ... Es ist keine leichte Aufgabe, einen statischen Code-Analysator zu warten. Aber was zu tun ist, ich selbst habe ein solches Schicksal gewählt. Ich krempelte die Ärmel hoch und untersuchte die Ursache des falschen Positivs.
Zunächst habe ich die Beschreibung der Funktion
GetNamedSecurityInfo untersucht und sichergestellt, dass ihr Aufruf tatsächlich zu einer Änderung des Werts der Variablen
pDACL führen sollte . Hier ist eine Beschreibung des 6. Funktionsarguments:
ppdacl
Ein Zeiger auf eine Variable, die einen Zeiger auf die DACL im zurückgegebenen Sicherheitsdeskriptor oder NULL empfängt, wenn der Sicherheitsdeskriptor keine DACL hat. Der zurückgegebene Zeiger ist nur gültig, wenn Sie das Flag DACL_SECURITY_INFORMATION setzen. Dieser Parameter kann auch NULL sein, wenn Sie die DACL nicht benötigen.
|
Ich weiß, dass der PVS-Studio-Analysator solch einfachen Code auf jeden Fall korrekt verarbeiten und keine bedeutungslose Warnung geben sollte. Bereits in diesem Moment sagte mir meine Intuition, dass dies ein ungewöhnlicher Fall sein würde, der Zeit verbringen muss.
Ich bestätigte meine Befürchtungen, dass ich weder in der aktuellen Alpha-Version des Analysators noch in genau der auf dem Client installierten Version ein falsches Positiv reproduzieren konnte. So und so, aber der Analysator ist still.
Ich bat den Client, mir eine
vorverarbeitete i-Datei zu senden, die für ein Programm mit einem synthetischen Beispiel generiert wurde. Er hat die Datei erstellt, gesendet, und ich habe begonnen, sie im Detail zu untersuchen.
Bei der gesendeten Datei gab der Analysator sofort ein falsches Positiv aus. Einerseits ist dies gut, da der Fehler reproduziert wird. Andererseits habe ich die Gefühle erlebt, die dieses Bild am besten beschreibt.
Warum genau diese? Ich weiß sehr gut, wie der Analysator und die Diagnose des
V547 funktionieren . Nun, es kann keine solche Betätigung geben!
Ok, mach Tee und mach weiter.
Der Aufruf der Funktion
GetNamedSecurityInfo wird
erweitert auf:
::GetNamedSecurityInfoW(L"ObjectName", SE_FILE_OBJECT, (0x00000004L), 0, 0, &pDACL, 0, &pSD);
Dieser Code sieht sowohl in meiner eigenen vorverarbeiteten i-Datei als auch in der vom Client gesendeten Datei gleich aus.
Hmm ... Okay, jetzt werden wir untersuchen, wie diese Funktion deklariert wird. In meiner Datei sehe ich:
__declspec(dllimport) DWORD __stdcall GetNamedSecurityInfoW( LPCWSTR pObjectName, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID * ppsidOwner, PSID * ppsidGroup, PACL * ppDacl, PACL * ppSacl, PSECURITY_DESCRIPTOR * ppSecurityDescriptor );
Alles ist logisch, alles ist klar. Nichts Unerwartetes.
Als nächstes schaue ich mir die Client-Datei an und ...
Dort sehe ich etwas aus der parallelen Realität:
__declspec(dllimport) DWORD __stdcall GetNamedSecurityInfoW( LPCWSTR pObjectName, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, const PSID * ppsidOwner, const PSID * ppsidGroup, const PACL * ppDacl, const PACL * ppSacl, PSECURITY_DESCRIPTOR * ppSecurityDescriptor );
Beachten Sie, dass das formale Argument für
ppDacl als
const markiert ist.
Wat? WTF? Wat? WTF?Was für eine
Konstante !? Woher kommt er?
Zumindest ist sofort klar, dass der Analysator hier nicht schuld ist und ich seine Ehre verteidigen kann.
Das Argument ist ein Zeiger auf ein konstantes Objekt. Es stellt sich heraus, dass die Funktion
GetNamedSecurityInfoW aus Sicht des Analysators das Objekt, auf das sich der Zeiger bezieht, nicht ändern kann. Daher hier:
PACL pDACL = NULL; PSECURITY_DESCRIPTOR pSD = NULL; ::GetNamedSecurityInfo(_T("ObjectName"), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSD); auto test = pDACL == NULL;
Die Variable
pDACL kann sich nicht ändern und der Analysator generiert eine angemessene Warnung (Ausdruck 'pDACL == 0' ist immer wahr.).
Warum eine Warnung ausgegeben wird, ist verständlich. Aber es ist nicht klar, woher diese
Konstante kam. Es kann einfach nicht da sein!
Es gibt jedoch eine Vermutung, die durch Suchanfragen im Internet bestätigt wird. Es stellt sich heraus, dass es eine alte ungültige Datei aclapi.h mit einer falschen Funktionsbeschreibung gibt. Ich habe auch zwei interessante Links im Internet gefunden:
Es war also einmal eine Funktionsbeschreibung in der Datei aclapi.h (6.0.6002.18005-Windows 6.0):
WINADVAPI DWORD WINAPI GetNamedSecurityInfoW( __in LPWSTR pObjectName, __in SE_OBJECT_TYPE ObjectType, __in SECURITY_INFORMATION SecurityInfo, __out_opt PSID * ppsidOwner, __out_opt PSID * ppsidGroup, __out_opt PACL * ppDacl, __out_opt PACL * ppSacl, __out_opt PSECURITY_DESCRIPTOR * ppSecurityDescriptor );
Dann wollte jemand den Typ des formalen Arguments
pObjectName korrigieren , aber auf dem Weg dorthin wurden die
Zeigertypen durch das Schreiben von
const durcheinander gebracht. Und aclapi.h (6.1.7601.23418-Windows 7.0) wurde so:
WINADVAPI DWORD WINAPI GetNamedSecurityInfoW( __in LPCWSTR pObjectName, __in SE_OBJECT_TYPE ObjectType, __in SECURITY_INFORMATION SecurityInfo, __out_opt const PSID * ppsidOwner, __out_opt const PSID * ppsidGroup, __out_opt const PACL * ppDacl, __out_opt const PACL * ppSacl, __out PSECURITY_DESCRIPTOR * ppSecurityDescriptor );
Es wird deutlich, dass genau die falsche falsche aclapi.h-Datei vom Client verwendet wird. Er bestätigte diese Hypothese später in der Korrespondenz. Ich verwende eine neuere Version, daher wurde der Fehler nicht reproduziert.
So sieht die bereits korrigierte Funktionsbeschreibung in aclapi.h (6.3.9600.17415-Windows_8.1) aus.
WINADVAPI DWORD WINAPI GetNamedSecurityInfoW( _In_ LPCWSTR pObjectName, _In_ SE_OBJECT_TYPE ObjectType, _In_ SECURITY_INFORMATION SecurityInfo, _Out_opt_ PSID * ppsidOwner, _Out_opt_ PSID * ppsidGroup, _Out_opt_ PACL * ppDacl, _Out_opt_ PACL * ppSacl, _Out_ PSECURITY_DESCRIPTOR * ppSecurityDescriptor );
Der Argumenttyp
pObjectName blieb gleich, aber die zusätzliche
Konstante wurde entfernt. Alles ist an seinen Platz zurückgekehrt, aber Header-Dateien mit falschen Funktionsdeklarationen leben weiterhin in der Welt.
All das erzähle ich dem Kunden. Er ist erfreut und erfreut, dass sich die Situation geklärt hat. Außerdem findet er den Grund, warum er entweder ein falsches Positiv sieht oder nicht:
Ich habe vergessen, dass ich bei diesem Testprojekt einmal mit Toolsets experimentiert habe. Im Debug-Testprojekt ist die Konfiguration standardmäßig im Platform Toolset für Visual Studio 2017 - "Visual Studio 2017 (v141)" konfiguriert, die Release-Konfiguration ist jedoch in "Visual Studio 2015 - Windows XP (v140_xp)" konfiguriert. Gestern habe ich irgendwann die Konfiguration geändert und die Warnung erschien und verschwand.Das ist alles. Sie können die Untersuchung beenden. Wir entscheiden mit dem Client, dass wir keine spezielle Sicherung im Analysator durchführen, damit dieser Fehler in der Header-Datei berücksichtigt wird. Die Hauptsache ist, dass die Situation jetzt klar ist. Wie das Sprichwort sagt: "Der Fall ist abgeschlossen."
FazitPVS-Studio Analyzer ist ein komplexes Softwareprodukt, das viele Informationen aus dem Programmcode sammelt und für verschiedene
Analysetechnologien verwendet . Insbesondere in diesem Fall hat die übermäßige Intelligenz des Analysators dazu geführt, dass er aufgrund einer falschen Beschreibung der Funktion ein falsches Positiv zu erzeugen begann.
Werden Sie unser Kunde und Sie erhalten schnelle und qualitativ hochwertige Unterstützung von mir und meinen Kollegen.
Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Andrey Karpov.
False Positives in PVS-Studio: Wie tief das Kaninchenloch geht .