Notre équipe fournit un support client rapide et efficace. Seuls les programmeurs participent au support, car les programmeurs posent également des questions et nous devons penser à beaucoup d'entre eux. Je voudrais décrire l'une des demandes d'assistance récentes au sujet des faux positifs, qui a conduit à une toute petite étude du problème décrit dans la lettre.
Nous travaillons dur pour réduire le nombre de faux positifs générés par l'analyseur PVS-Studio. Malheureusement, les analyseurs statiques ne peuvent souvent pas distinguer le code correct de l'erreur, car ils n'ont tout simplement pas assez d'informations. En conséquence, il y a quand même des faux positifs. Cependant, ce n'est pas un problème, car après avoir réglé l'analyseur, il est facile de parvenir à une situation où
9 avertissements
sur 10 indiqueront de vraies erreurs.
Bien que les fausses alarmes ne soient pas un problème aussi important que cela puisse paraître à première vue, nous les combattons constamment, améliorant les diagnostics. Nous remarquons nous-mêmes certains faux positifs flagrants, dont certains nous sont écrits par des clients et des utilisateurs gratuits.
Récemment, un de nos clients a écrit une lettre d'environ le contenu suivant:
Pour une raison quelconque, l'analyseur dit que le pointeur est toujours nul, bien que ce ne soit clairement pas le cas. De plus, l'analyseur se comporte étrangement et de manière instable sur un projet de test: soit il donne un avertissement, soit il ne le fait pas. Un exemple synthétique qui reproduit un faux positif:#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; }
Je peux imaginer à quoi ressemblent les réponses de nos utilisateurs. Il est immédiatement clair que la fonction
GetNamedSecurityInfo change la valeur de la variable
pDACL . Les développeurs ne pourraient-ils vraiment pas écrire un gestionnaire pour des situations aussi simples? De plus, il n'est pas clair pourquoi l'analyseur émet ou non un message. Peut-être qu'ils ont eux-mêmes une sorte de bogue dans l'outil, comme une variable non initialisée?
Eh ... Ce n'est pas une tâche facile de maintenir un analyseur de code statique. Mais que faire, j'ai moi-même choisi un tel sort. Retroussant mes manches, j'ai procédé à une enquête sur la cause du faux positif.
Pour commencer, j'ai étudié la description de la fonction
GetNamedSecurityInfo et je me suis assuré que son appel devait vraiment conduire à une modification de la valeur de la variable
pDACL . Voici une description du sixième argument de la fonction:
ppdacl
Un pointeur vers une variable qui reçoit un pointeur vers la DACL dans le descripteur de sécurité renvoyé ou NULL si le descripteur de sécurité n'a pas de DACL. Le pointeur renvoyé n'est valide que si vous définissez l'indicateur DACL_SECURITY_INFORMATION. En outre, ce paramètre peut être NULL si vous n'avez pas besoin de la DACL.
|
Je sais que l'analyseur PVS-Studio devrait certainement traiter correctement ce code simple et ne pas donner un avertissement insignifiant. Déjà à ce moment, mon intuition m'a dit que ce serait un cas inhabituel, qui devra passer du temps.
J'ai confirmé mes craintes lorsque je ne pouvais reproduire un faux positif ni sur la version alpha actuelle de l'analyseur, ni sur la version installée sur le client. Telle et telle, mais l'analyseur est silencieux.
J'ai demandé au client de m'envoyer un
fichier i prétraité généré pour un programme avec un exemple synthétique. Il a généré, envoyé le fichier et j'ai commencé à l'examiner en détail.
Sur le fichier envoyé, l'analyseur a immédiatement émis un faux positif. D'une part, c'est bien, car le bug est reproduit. D'un autre côté, j'ai ressenti les sentiments que cette image décrit le mieux.
Pourquoi exactement ça? Je sais très bien comment fonctionnent l'analyseur et les diagnostics du
V547 . Eh bien, il ne peut y avoir un tel actionnement!
Ok, fais du thé et continue.
L'appel à la fonction
GetNamedSecurityInfo se développe comme
suit :
::GetNamedSecurityInfoW(L"ObjectName", SE_FILE_OBJECT, (0x00000004L), 0, 0, &pDACL, 0, &pSD);
Ce code est identique à la fois dans mon propre fichier i prétraité et dans le fichier envoyé par le client.
Hmm ... D'accord, nous allons maintenant examiner comment cette fonction est déclarée. Dans mon dossier je vois:
__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 );
Tout est logique, tout est clair. Rien d'inattendu.
Ensuite, je regarde le fichier client et ...
Là, je vois quelque chose de la réalité parallèle:
__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 );
Notez que l'argument formel de
ppDacl est marqué comme
const .
Wat? WTF? Wat? WTF?Quel
const !? D'où vient-il!?
Au moins, il est immédiatement clair que l'analyseur n'est pas à blâmer ici et je peux défendre son honneur.
L'argument est un pointeur sur un objet constant. Il s'avère que, du point de vue de l'analyseur, la fonction
GetNamedSecurityInfoW ne peut pas modifier l'objet auquel le pointeur fait référence. D'où ici:
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;
la variable
pDACL ne peut pas changer et l'analyseur génère un avertissement raisonnable (l'expression 'pDACL == 0' est toujours vraie.).
La raison pour laquelle un avertissement est émis est compréhensible. Mais on ne sait pas d'où vient ce
const . Ça ne peut tout simplement pas être là!
Cependant, il y a une intuition, et cela est confirmé par des recherches sur Internet. Il s'avère qu'il existe un ancien fichier aclapi.h invalide avec une description de fonction incorrecte. J'ai également trouvé deux liens intéressants sur Internet:
Donc, il était une fois une description de fonction dans le fichier 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 );
Ensuite, quelqu'un a voulu corriger le type de l'argument formel
pObjectName , mais en cours de route, il a gâché les types de pointeurs en écrivant
const . Et aclapi.h (6.1.7601.23418-Windows 7.0) est devenu comme ceci:
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 );
Il devient clair que c'est précisément le mauvais mauvais fichier aclapi.h qui est utilisé par le client. Il a confirmé plus tard cette hypothèse par correspondance. J'utilise une version plus récente, donc l'erreur ne s'est pas reproduite.
Voici à quoi ressemble la description de fonction déjà corrigée dans aclapi.h (6.3.9600.17415-Windows_8.1).
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 );
Le type d'argument
pObjectName est resté le même, mais les
const supplémentaires
ont été supprimés. Tout est revenu à leur place, mais les fichiers d'en-tête avec des déclarations de fonction incorrectes continuent de vivre dans le monde.
Tout cela, je le dis au client. Il est heureux et heureux que la situation se soit éclaircie. De plus, il trouve la raison pour laquelle il voit un faux positif ou non:
J'ai oublié qu'une fois j'ai expérimenté avec des jeux d'outils sur ce projet de test. Dans le projet de test de débogage, la configuration est configurée par défaut sur le Platform Toolset pour Visual Studio 2017 - "Visual Studio 2017 (v141)", mais la configuration Release est configurée sur "Visual Studio 2015 - Windows XP (v140_xp)". Hier, j'ai juste à un moment donné changé la configuration, et l'avertissement est apparu et a disparu.C’est tout. Vous pouvez mettre fin à l'enquête. Nous décidons avec le client que nous n'effectuerons aucune sauvegarde spéciale dans l'analyseur afin qu'il tienne compte de ce bug dans le fichier d'en-tête. L'essentiel est que la situation soit maintenant claire. Comme le dit le proverbe, "l'affaire est close".
ConclusionL'analyseur PVS-Studio est un produit logiciel complexe qui recueille beaucoup d'informations à partir du code du programme et l'utilise pour diverses
technologies d'analyse . Plus précisément, dans ce cas, l'intelligence excessive de l'analyseur a conduit au fait que, en raison d'une description incorrecte de la fonction, il a commencé à produire un faux positif.
Devenez notre client et vous recevrez un soutien rapide et de qualité de moi et de mes collègues.
Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Andrey Karpov.
Faux positifs dans PVS-Studio: Profondeur du trou du lapin .