Visual Studio /analyze不好之处---漏报(四)
分析是一种强大的VisualC++特性,可以帮助发现bug。然而,它有时忽略了关键问题。在这篇简短的文章中,我描述了一个有趣的危险模式,它无法识别,并解释了一个真正的解决方法。
这些测试都是用Visual Studio 2010 SP1,C/C++优化编译器版本16.00 .40219.01为80×86。
你想复制一个字符串吗?
我想我们都同意strcpy是危险的。在一些情况下,它可以安全使用,但这是一个坏习惯。所以考虑一下这个代码:
void StringCopyTests( const char* pEvil ) { char buffer[100]; // warning C4996: ‘strcpy’: This function or variable may be unsafe. // Consider using strcpy_s instead. // warning C6204: Possible buffer overrun in call to ‘strcpy’: use // of unchecked parameter ‘pEvil’ strcpy(buffer, pEvil); // No warning strcpy_s(buffer, strlen(pEvil) + 1, pEvil); }
VC++警告说strcpy是危险的,strcpy是一个更好的选择。With/analyze VC++还警告说,这是strcpy的一种特别危险的用法。也可以。然而,它没有评论strcpy_s在语义上相同的用法。
我希望每个人都会因为在这件事上使用strcpy_s而感到震惊。这样的代码表明作者根本没有清楚地考虑它们的缓冲区。不是我写的代码。
以任何方式使用源缓冲区属性的目标缓冲区大小计算都将中断。我需要/analyze来告诉我这个模式是否存在于我们的代码库中,而现在它让我失望了。
安全的拷贝字符串
VC++弃用警告在理论上是个好主意,但在现实世界中,它们常常失败。从strcpy/strncpy转换为strcpy/strncpy_s/strncpy_s有好有坏的方法,经验告诉我,虽然大多数转换没有这一个糟糕,但可悲的事实是,大多数开发人员没有有效地转换到更安全的CRT。在这种情况下,有两种合理的方法来修改代码:
void StringCopyFix( const char* pEvil ) { char buffer[100]; // Terminate if source is too big strcpy_s(buffer, pEvil); // Truncate if source is too big strncpy_s(buffer, pEvil, _TRUNCATE); }
在strcpy_s和strncpy_s与_TRUNCATE之间进行选择是一个策略决定–如果字符串不适合,您是要继续还是要中止?没有讨论的是尺寸参数。它必须被正确地指定,而且,事实证明,最好的方法就是省略它。更安全的CRT函数都有模板重载,如果你给它们一个机会,它们将推断数组的大小。这些版本应该是首选的,因为人类有六种不同的方法可以搞乱目标缓冲区的大小*,而编译器总是能正确地处理。如果编译器不能推断出正确的大小,它将拒绝编译代码。
我发现缓冲区大小指定不正确的一些方式包括:
- 使用strcpy计算缓冲区大小
- 使用与缓冲区大小不匹配的硬编码数字
- 使用与缓冲区大小不匹配的命名常量
- 调用_countof时使用sizeof
- 使用错误数组的大小
- 在打算使用sizeof(buffer)-1时使用sizeof(buffer-1)
为虫子生,为虫子死,为虫子奋斗一辈子