PVS-Studio中的误报:兔子洞有多深

独角兽PVS-Studio和GetNamedSecurityInfo

我们的团队提供快速有效的客户支持。 只有程序员才能参与支持,因为程序员也会提出问题,我们必须考虑其中的许多问题。 我想描述一个关于误报这个问题的最近的支持请求,这导致对信中描述的问题进行了很小的研究。


我们正在努力减少PVS-Studio分析仪产生的误报数量。 不幸的是,静态分析器通常无法将正确的代码与错误区分开,因为它们根本没有足够的信息。 结果,仍然存在误报。 但是,这不是问题,因为已经对分析仪进行了调谐,所以很容易达到这样的情况, 即每10条警告中有9条会指示实际错误。

尽管虚假警报看起来并不像乍看起来那样大,但我们一直在与之抗争,不断改进诊断能力。 我们自己注意到一些明显的虚假肯定,其中一些是由客户和免费用户写给我们的。

最近,我们的一位客户写了一封信,内容如下:

出于某种原因,分析器认为指针始终为空,尽管显然并非如此。 此外,分析器在测试项目上的行为异常且不稳定:它发出警告或不发出警告。 一个综合的例子,它会产生误报:

#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变量的值。 开发人员真的不能为这种简单情况编写处理程序吗? 此外,不清楚分析器为何发出消息或不发出消息。 也许他们自己在工具中存在某种错误,例如未初始化的变量?

恩...维护静态代码分析器并非易事。 但是该怎么办,我自己选择了这样的命运。 我sleeve起袖子,继续调查假阳性的原因。

首先,我研究了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 ); 

注意, ppDacl的形式参数标记为const

WTF? WTF?

什么const ! 他从哪里来?

至少立即清楚的是分析仪不应归咎于此,我可以捍卫他的荣誉。

该参数是指向常量对象的指针。 事实证明,从分析器的角度来看, 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; // V547 Expression 'pDACL == 0' is always true. 

pDACL变量无法更改,并且分析仪会生成合理的警告(表达式“ pDACL == 0”始终为真。)。

为什么发出警告是可以理解的。 但是尚不清楚该常量来自何处。 就是不能在那里!

但是,有一种预感,并且可以通过Internet上的搜索得到确认。 原来,存在一个旧的无效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 ); 

差异1


很明显,客户端使用的正是错误的错误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 ); 

差异2


参数类型pObjectName保持不变,但多余的const被删除。 一切都回到了原处,但是带有错误函数声明的头文件仍然存在。

这一切我都告诉客户。 他对这种情况已经消除感到高兴和高兴。 此外,他找到了自己看不到误报的原因:

我忘记了我曾经在这个测试项目上尝试过工具集。 在“调试”测试项目中,默认情况下,在Visual Studio 2017的平台工具集中配置配置-“ Visual Studio 2017(v141)”,但是在“ Visual Studio 2015-Windows XP(v140_xp)”上配置发布配置。 昨天,我只是在某个时候更改了配置,警告出现并消失了。

仅此而已。 您可以结束调查。 我们决定与客户端一起在分析器中不进行任何特殊备份,以便在头文件中考虑此错误。 最主要的是情况现在已经很清楚了。 俗话说“结案”。

结论

PVS-Studio分析仪是一种复杂的软件产品,可以从程序代码中收集很多信息,并将其用于各种分析技术 。 具体而言,在这种情况下,分析仪的过分智能导致以下事实:由于对功能的错误描述,它开始产生误报。

成为我们的客户,您将获得我和我的同事的快速,优质的支持。



如果您想与说英语的读者分享这篇文章,请使用以下链接:Andrey Karpov。 PVS-Studio中的误报:兔子洞有多深

Source: https://habr.com/ru/post/zh-CN441126/


All Articles