Unser Team bietet schnelle und effektive Kundenbetreuung. Benutzeranfragen werden ausschließlich von Programmierern bearbeitet, da unsere Kunden selbst Programmierer sind und häufig knifflige Fragen stellen. Heute werde ich Ihnen von einer kürzlich erfolgten Anfrage zu einem falschen Positiv erzählen, die mich sogar gezwungen hat, eine kleine Untersuchung durchzuführen, um das Problem zu lösen.
Wir arbeiten hart daran, die Anzahl der von PVS-Studio generierten Fehlalarme auf ein Minimum zu reduzieren. Leider können statische Analysatoren häufig keinen korrekten Code von einem Fehler unterscheiden, da sie einfach nicht über genügend Informationen verfügen. False Positives sind daher unvermeidlich. Dies ist jedoch kein Problem, da Sie den Analysator einfach so anpassen können, dass
9 von 10 Warnungen auf echte Fehler hinweisen.
Obwohl falsch positive Ergebnisse keine große Sache zu sein scheinen, hören wir nie auf, sie zu bekämpfen, indem wir unsere Diagnose verbessern. Einige offensichtliche Fehlalarme werden von unserem Team abgefangen. andere werden von unseren Kunden und Benutzern der kostenlosen Version gemeldet.
Einer unserer Kunden hat uns kürzlich eine E-Mail mit folgendem Text gesendet:
Aus irgendeinem Grund sagt der Analysator, dass ein bestimmter Zeiger immer null ist, während dies nicht der Fall ist. Darüber hinaus ist das Verhalten bei einem Testprojekt seltsam und instabil: Manchmal wird eine Warnung ausgegeben, manchmal nicht. Hier ist ein synthetisches Beispiel, das dieses falsch positive Ergebnis 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; }
Es ist nicht schwer zu erraten, wie unsere Benutzer solche Fehlalarme sehen. Die Funktion
GetNamedSecurityInfo ändert offensichtlich den Wert der Variablen
pDACL . Was hat die Entwickler daran gehindert, einen Handler für solche einfachen Fälle zu erstellen? Und warum wird die Warnung nicht in jeder Sitzung ausgegeben? Vielleicht ist es ein Fehler im Analysator selbst, beispielsweise eine nicht initialisierte Variable?
Leider ... Die Unterstützung von Benutzern eines statischen Code-Analysators ist keine leichte Aufgabe, aber ich habe mich dafür entschieden. Also krempelte ich die Ärmel hoch und machte mich daran, das Problem zu untersuchen.
Zunächst überprüfte ich die Beschreibung der Funktion
GetNamedSecurityInfo und stellte sicher, dass der Aufruf tatsächlich das Ändern des Werts der Variablen
pDACL implizierte. Hier ist die Beschreibung des 6. Arguments:
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 PVS-Studio offensichtlich in der Lage sein sollte, solch einfachen Code zu verarbeiten, ohne eine falsche Warnung zu generieren. Zu diesem Zeitpunkt sagte mir meine Intuition bereits, dass der Fall nicht trivial war und dass die Lösung eine ganze Weile dauern würde.
Meine Bedenken wurden bestätigt, als ich das falsch positive Ergebnis weder mit unserer aktuellen Alpha-Version des Analysators noch mit der auf dem Computer des Benutzers installierten Version reproduzieren konnte. Egal was ich tat, der Analysator schwieg.
Ich habe den Client gebeten, mir die für das Beispielprogramm generierte
vorverarbeitete i-Datei zu senden. Er hat das getan, und ich habe meine Ermittlungen fortgesetzt.
Der Analysator hat sofort das falsch positive Ergebnis in dieser Datei erzeugt. Einerseits war es gut, dass ich es endlich geschafft hatte, es zu reproduzieren. Andererseits hatte ich ein Gefühl, das sich am besten durch dieses Bild veranschaulichen lässt:
Warum dieses Gefühl? Sie sehen, ich weiß genau, wie sowohl der Analysator als auch die
V547- Diagnose funktionieren. Es gibt einfach keine Möglichkeit, jemals so ein falsches Positiv zu erzeugen!
OK, lass uns etwas Tee machen und weitermachen.
Der Aufruf der Funktion
GetNamedSecurityInfo wird in den folgenden Code erweitert:
::GetNamedSecurityInfoW(L"ObjectName", SE_FILE_OBJECT, (0x00000004L), 0, 0, &pDACL, 0, &pSD);
Dieser Code sieht sowohl in der auf meinem Computer vorverarbeiteten i-Datei als auch in der vom Benutzer gesendeten Datei gleich aus.
Hmm ... OK, schauen wir uns die Deklaration dieser Funktion an. Folgendes habe ich in meiner Datei:
__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 und klar. Nichts ungewöhnliches.
Dann schaue ich in die Datei des Benutzers und ...
Was ich dort sehe, gehört nicht zu unserer 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 der formale Parameter
pp Dacl als
const markiert ist.
Wat? WTF? Wat? WTF?Was ist das für eine
Konstante ? Was macht es hier?
Zumindest weiß ich mit Sicherheit, dass der Analysator unschuldig ist und ich seine Ehre verteidigen kann.
Das Argument ist ein Zeiger auf ein konstantes Objekt. Es stellt sich heraus, dass die
GetNamedSecurityInfoW- Funktion aus Sicht des Analysators das Objekt, auf das der Zeiger
verweist , nicht ändern kann. Daher im folgenden Code:
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
pDACL- Variable kann sich nicht ändern, worüber der Analysator uns zu Recht warnt (Ausdruck 'pDACL == 0' ist immer wahr.).
OK, jetzt wissen wir, was die Warnung auslöst. Was wir immer noch nicht wissen, ist, woher dieses
const- Schlüsselwort stammt. Es kann einfach nicht da sein!
Nun, ich habe eine Vermutung, die durch das, was ich im Internet finde, bestätigt wird. Es stellt sich heraus, dass es eine alte Version der Datei aclapi.h mit einer falschen Funktionsbeschreibung gibt. Ich bin auch auf ein paar interessante Links gestoßen:
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 hat jemand den Typ des Parameters
pObjectName geändert , aber die Typen der Zeiger auf dem Weg durch Hinzufügen des Schlüsselworts
const durcheinander gebracht. Die Datei aclapi.h (6.1.7601.23418-Windows 7.0) wurde wie folgt erstellt:
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 );
Jetzt war klar, dass unser Benutzer mit dieser sehr falschen Version von aclapi.h gearbeitet hatte, die er dann in seiner E-Mail bestätigte. Ich konnte den Fehler nicht reproduzieren, da ich eine neuere Version verwendet habe.
So sieht die Beschreibung der festen Funktion in der neuesten Datei 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 Typ des
pObjectName- Arguments ist immer noch derselbe, aber die zusätzlichen
Konstanten sind weg. Alles ist wieder gut, aber irgendwo da draußen werden immer noch kaputte Header verwendet.
Ich erkläre dem Kunden das alles und er freut sich, dass das Problem gelöst ist. Außerdem hat er herausgefunden, warum das falsche Positiv nicht regelmäßig auftrat:
Ich erinnere mich jetzt, dass ich vor einiger Zeit mit Toolsets für dieses Testprojekt experimentiert habe. Die Debug-Konfiguration wurde für Visual Studio 2017 - "Visual Studio 2017 (v141)" standardmäßig auf Platform Toolset festgelegt, während die Release-Konfiguration auf "Visual Studio 2015 - Windows XP (v140_xp)" festgelegt wurde. Ich habe gestern einfach zwischen den Konfigurationen gewechselt, und die Warnung wurde entsprechend angezeigt und ausgeblendet.Das ist alles Die Untersuchung ist beendet. Wir besprechen das Problem mit dem Client und beschließen, dem Analysator keinen Kludge hinzuzufügen, damit dieser Header-Datei-Fehler behoben werden kann. Das Wichtigste ist, dass wir das Problem herausgefunden haben. "Fall abgewiesen", wie sie sagen.
FazitPVS-Studio ist ein komplexes Softwareprodukt, das große Mengen an Informationen aus dem Programmcode sammelt und in verschiedenen
Analysetechniken verwendet . In diesem speziellen Fall stellte sich heraus, dass es zu intelligent war und aufgrund einer falschen Funktionsbeschreibung ein falsches Positiv ergab.
Werden Sie zu unseren Kunden und Sie erhalten garantiert umgehend professionelle Unterstützung von mir und meinen Teamkollegen.