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)
上一篇:C风格字符串


下一篇:C语言简单实现substr函数