Nuestro equipo brinda atención al cliente rápida y eficiente. Solo los programadores participan en el apoyo, ya que los programadores también hacen preguntas y tenemos que pensar en muchas de ellas. Me gustaría describir una de las llamadas de soporte recientes sobre el tema de los falsos positivos, lo que condujo a un pequeño estudio del problema descrito en la carta.
Estamos trabajando duro para reducir la cantidad de falsos positivos generados por el analizador PVS-Studio. Desafortunadamente, los analizadores estáticos a menudo no pueden distinguir el código correcto del error, ya que simplemente no tienen suficiente información. Como resultado, hay falsos positivos de todos modos. Sin embargo, esto no es un problema, ya que sintonizar el analizador es fácil lograr una situación en la que
9 de cada 10 advertencias indiquen errores reales.
Aunque las falsas alarmas no son un problema tan grande como podría parecer a primera vista, estamos luchando constantemente con ellas, mejorando los diagnósticos. Notamos algunos falsos positivos flagrantes, algunos de los cuales nos han sido escritos por clientes y usuarios gratuitos.
Recientemente, uno de nuestros clientes escribió una carta de aproximadamente el siguiente contenido:
Por alguna razón, el analizador dice que el puntero siempre es nulo, aunque esto claramente no es así. Además, el analizador se comporta de manera extraña e inestable en un proyecto de prueba: da una advertencia o no. Un ejemplo sintético que reproduce un falso positivo:#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; }
Me puedo imaginar cómo se ven las respuestas similares de nuestros usuarios. Está claro de inmediato que la función
GetNamedSecurityInfo cambia el valor de la variable
pDACL . ¿Podrían los desarrolladores no ser capaces de escribir un controlador para situaciones tan simples? Además, no está claro por qué el analizador emite un mensaje o no. ¿Quizás ellos mismos tienen algún tipo de error en la herramienta, como una variable no inicializada?
Eh ... No es un trabajo fácil mantener un analizador de código estático. Pero qué hacer, yo mismo elegí ese destino. Me arremangué y procedí a investigar la causa del falso positivo.
Para empezar, estudié la descripción de la función
GetNamedSecurityInfo y me aseguré de que su llamada realmente condujera a un cambio en el valor de la variable
pDACL . Aquí hay una descripción del argumento de la sexta función:
ppdacl
Un puntero a una variable que recibe un puntero a la DACL en el descriptor de seguridad devuelto o NULL si el descriptor de seguridad no tiene DACL. El puntero devuelto es válido solo si establece el indicador DACL_SECURITY_INFORMATION. Además, este parámetro puede ser NULL si no necesita la DACL.
|
Sé que el analizador PVS-Studio definitivamente debe procesar correctamente un código tan simple y no dar una advertencia sin sentido. Ya en este momento, mi intuición me dijo que este sería un caso inusual, que tendrá que pasar tiempo.
Confirmé mis temores cuando no pude reproducir un falso positivo en la versión alfa actual del analizador o en la versión exacta que estaba instalada en el cliente. Tal y tal, pero el analizador está en silencio.
Le pedí al cliente que me enviara un
archivo i preprocesado generado para un programa con un ejemplo sintético. Él generó, envió el archivo y comencé a examinarlo en detalle.
En el archivo enviado, el analizador emitió inmediatamente un falso positivo. Por un lado, esto es bueno, ya que el error se reproduce. Por otro lado, experimenté los sentimientos que esta imagen describe mejor.
¿Por qué exactamente estos? Sé muy bien cómo funcionan el analizador y el diagnóstico del
V547 . Bueno, no puede haber tal actuación!
Ok, prepara té y continúa.
La llamada a la función
GetNamedSecurityInfo se expande a:
::GetNamedSecurityInfoW(L"ObjectName", SE_FILE_OBJECT, (0x00000004L), 0, 0, &pDACL, 0, &pSD);
Este código se ve igual tanto en mi propio archivo i preprocesado como en el archivo enviado por el cliente.
Hmm ... Bien, ahora examinaremos cómo se declara esta función. En mi archivo veo:
__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 );
Todo es lógico, todo está claro. Nada inesperado
A continuación, miro el archivo del cliente y ...
Ahí veo algo de la realidad paralela:
__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 );
Tenga en cuenta que el argumento formal de
ppDacl está marcado como
const .
Wat? WTF? Wat? WTF?¿Qué
const ? ¿De dónde es él?
Al menos está claro de inmediato que el analizador no tiene la culpa aquí y puedo defender su honor.
El argumento es un puntero a un objeto constante. Resulta que, desde el punto de vista del analizador, la función
GetNamedSecurityInfoW no puede cambiar el objeto al que se refiere el puntero. Por lo tanto aquí:
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 no puede cambiar y el analizador genera una advertencia razonable (la expresión 'pDACL == 0' siempre es verdadera).
Por qué se emite una advertencia es comprensible. Pero no está claro de dónde vino esta
constante . ¡Simplemente no puede estar allí!
Sin embargo, hay una corazonada y se confirma mediante búsquedas en Internet. Resulta que hay un antiguo archivo aclapi.h no válido con una descripción de función incorrecta. También encontré dos enlaces interesantes en Internet:
Entonces, había una vez una descripción de la función en el archivo 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 );
Entonces alguien quiso arreglar el tipo de argumento formal
pObjectName , pero en el camino confundió los tipos de punteros escribiendo
const . Y aclapi.h (6.1.7601.23418-Windows 7.0) se convirtió así:
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 );
Queda claro que es precisamente el archivo incorrecto aclapi.h incorrecto el que utiliza el cliente. Más tarde confirmó esta hipótesis en correspondencia. Utilizo una versión más reciente, por lo que el error no se reprodujo.
Así es como se ve la descripción de la función ya corregida en 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 );
El tipo de argumento
pObjectName se mantuvo igual, pero
se eliminaron las
constantes adicionales. Todo ha vuelto a su lugar, pero los archivos de encabezado con declaraciones de función incorrectas continúan viviendo en el mundo.
Todo esto se lo digo al cliente. Está complacido y complacido de que la situación se haya aclarado. Además, encuentra la razón por la que ve un falso positivo o no:
Olvidé que una vez experimenté con conjuntos de herramientas en este proyecto de prueba. En el proyecto de prueba de depuración, la configuración está configurada en Platform Toolset de forma predeterminada para Visual Studio 2017 - "Visual Studio 2017 (v141)", pero la configuración de lanzamiento está configurada en "Visual Studio 2015 - Windows XP (v140_xp)". Ayer, en algún momento cambié la configuración, y la advertencia apareció y desapareció.Eso es todo. Puedes poner fin a la investigación. Decidimos con el cliente que no haremos ninguna copia de seguridad especial en el analizador para que tenga en cuenta este error en el archivo de encabezado. Lo principal es que la situación ahora está clara. Como dice el refrán, "el caso está cerrado".
ConclusiónEl analizador PVS-Studio es un producto de software complejo que recopila mucha información del código del programa y lo utiliza para diversas
tecnologías de análisis . Específicamente, en este caso, la inteligencia excesiva del analizador ha llevado al hecho de que, debido a una descripción incorrecta de la función, comenzó a producir un falso positivo.
Conviértase en nuestro cliente y recibirá un soporte rápido y de calidad de parte mía y de mis colegas.
Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Andrey Karpov.
Positivos falsos en PVS-Studio: cuán profundo es la madriguera del conejo .