我们的团队提供快速有效的客户支持。 用户请求仅由程序员处理,因为我们的客户本身就是程序员,他们经常问一些棘手的问题。 今天,我将向您介绍一个关于误报的最新要求,该要求甚至迫使我进行一次小规模调查以解决问题。
我们努力将PVS-Studio产生的误报数量降至最低。 不幸的是,静态分析器通常无法分辨出错误的正确代码,因为它们只是没有足够的信息。 因此,误报是不可避免的。 但是,这不是问题,因为您可以轻松自定义分析仪,从而使
十分之九的警告指向真正的错误。
尽管误报似乎没什么大不了,但我们从未通过改进诊断程序来与之抗衡。 我们的团队发现了一些公然的误报; 其他则由我们的客户和免费版本用户报告。
我们的一位客户最近向我们发送了一封电子邮件,内容如下:
由于某种原因,分析器会说某个指针始终为空,而并非如此。 而且,它在测试项目上的行为是奇怪且不稳定的:有时会发出警告,有时则不会。 这是一个合成的示例,可重现该误报:#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; }
不难猜测,我们的用户如何看到这样的误报。
GetNamedSecurityInfo函数显然会修改变量
pDACL的值。 是什么导致开发人员无法为此类简单情况创建处理程序? 为什么不在每个会话中发出警告? 也许这是分析仪本身的错误,例如未初始化的变量?
las ...支持静态代码分析器的用户并非易事,但这是我自己的选择。 因此,我卷起袖子,开始研究问题。
我首先检查了
GetNamedSecurityInfo函数的描述,并确保它的调用确实暗示着修改
pDACL变量的值。 这是第六个参数的说明:
聚氯乙烯
指向变量的指针,该变量在返回的安全描述符中接收到指向DACL的指针;如果安全描述符没有DACL,则返回NULL。 仅当您设置DACL_SECURITY_INFORMATION标志时,返回的指针才有效。 另外,如果不需要DACL,则此参数可以为NULL。
|
我知道PVS-Studio显然应该能够处理这样的简单代码而不会产生错误警告。 到那时,我的直觉已经告诉我这个案子不是一件小事,要花很长时间才能解决。
当我无法使用当前的分析仪的Alpha版本或用户计算机上安装的版本来重现误报时,我的疑虑得到了证实。 不管我做什么,分析仪都保持沉默。
我要求客户将为示例程序生成的经过
预处理的i文件发送给我。 他这样做了,我继续进行调查。
分析仪确实在该文件上立即产生了误报。 一方面,很好的是我终于设法重制了它。 另一方面,这张图片可以最好地说明我的感觉:
为什么会有这种感觉? 您知道,我非常了解分析仪和
V547诊断程序如何工作。 他们根本不可能产生这样的误报!
好吧,让我们泡茶并继续。
对
GetNamedSecurityInfo函数的调用扩展为以下代码:
::GetNamedSecurityInfoW(L"ObjectName", SE_FILE_OBJECT, (0x00000004L), 0, 0, &pDACL, 0, &pSD);
该代码在我的计算机上预处理的i文件和用户发送的文件中看起来都相同。
嗯...好,让我们看一下这个函数的声明。 这是我在文件中得到的:
__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 );
一切都是逻辑清晰的。 没什么不寻常的。
然后我偷看用户的文件并...
我在那里看到的不属于我们的现实:
__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 );
注意,形式参数
pp Dacl被标记为
const 。
? WTF? ? WTF?那是什么
常量 ! 这是在做什么!
好吧,至少我确信分析仪是无辜的,我可以捍卫它的荣誉。
该参数是指向常量对象的指针。 事实证明,从分析器的角度来看,
GetNamedSecurityInfoW函数无法修改指针引用的对象。 因此,在以下代码中:
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;
pDACL变量不能更改,分析器正确地警告我们(表达式'pDACL == 0'始终为真。)。
好,现在我们知道是什么触发了警告。 我们仍然不知道
const关键字的来源。 就是不能在那里!
好吧,我有一个猜测,我在互联网上发现的事实证实了这一点。 原来,文件aclapi.h的版本较旧,其功能说明不正确。 我还遇到了几个有趣的链接:
因此,曾有一段时间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 );
然后有人更改了
pObjectName参数的类型,但是通过添加
const关键字弄乱了指针的类型。 最终aclapi.h文件(6.1.7601.23418-Windows 7.0)如下所示:
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 );
现在很明显,我们的用户使用的是非常错误的aclapi.h版本,然后他在电子邮件中确认了该版本。 由于使用的是更新版本,因此无法重现该错误。
这就是最新的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 );
pObjectName参数的类型仍然相同,但是多余的
const消失了。 一切都很好,但是仍然有坏的标题在使用。
我向客户解释了所有这些问题,他很高兴看到问题已解决。 此外,他发现了为什么误报率没有经常发生的原因:
我现在回想起前一段时间在这个测试项目上使用工具集进行实验。 默认情况下,Visual Studio 2017的“调试”配置设置为“平台工具集”-“ Visual Studio 2017(v141)”,而发布配置则设置为“ Visual Studio 2015-Windows XP(v140_xp)”。 昨天我只是在配置之间进行切换,并且警告会相应出现并消失。仅此而已。 调查结束了。 我们与客户端讨论此问题,并决定不向分析器添加任何垃圾以使其能够处理此头文件错误。 最重要的是我们已经解决了问题。 正如他们所说,“解散案件”。
结论PVS-Studio是一个复杂的软件产品,它从程序的代码中收集大量信息,并将其用于各种
分析技术中 。 在这种特殊情况下,事实证明它太聪明了,由于功能描述不正确,最终导致误报。
成为我们的客户,您一定会得到我和我的队友的迅速专业支持。