【问题标题】:Visual Studio 2015 Code Analysis C6386 warns of buffer overrunVisual Studio 2015 代码分析 C6386 警告缓冲区溢出
【发布时间】:2017-06-16 01:50:03
【问题描述】:

我已经阅读了很多关于 Visual Studio 代码分析警告 C8386 的内容,但无法通过我的代码找出这个特定问题。我已将其简化为以下小程序:

unsigned int nNumItems = 0;

int main()
{
    int *nWords=nullptr;
    unsigned int nTotal;

    nTotal = 3 + 2 * nNumItems;
    nWords = new int[nTotal];

    nWords[0] = 1;
    nWords[1] = 2; // this is line 18, warning C6386

    delete[] nWords;
    return 0;
}

Analyze->Run Code Analysis->On Solution会给出如下警告:

file.cpp(18):警告 C6386:写入“nWords”时缓冲区溢出:可写大小为“nTotal*4”字节,但可能写入“8”字节。

这是合法的吗?现在,如果我移动全局变量并使其成为本地变量,警告就会消失!

int main()
{
    unsigned int nNumItems = 0;
...
}

但我不能像在完整代码中那样做,这是一个成员变量。

同样,如果我将 nTotal 的定义移动到 'new int' 中,我也可以删除警告:

    nWords = new int[3 + 2 * nNumItems];

但我不能这样做,因为在完整代码的其他地方提到了 nWords

这只是 Visual Studio 静态代码分析器的问题,还是此代码的合法问题?

【问题讨论】:

  • 不会“解决”问题,而是使用std::vector<int>
  • 当它是全局的时,也许编译器认为别的东西可能会改变它。所以如果nNumItems = 0xffffffff,那就是缓冲区溢出。但这并没有改变在这种情况下警告是荒谬的事实。

标签: c++ visual-studio-2015 code-analysis static-code-analysis


【解决方案1】:

静态代码分析很难,跟踪像3 + 2 * nNumItems 这样的表达式的可能值很难,而且跟踪实际可能值 通常几乎是不可能的。这就是为什么它是一个警告,而不是错误。到目前为止是显而易见的。

现在,看看你如何描述这个警告的行为,我会打赌一个“错误”,或者更确切地说,我应该在静态分析器中以较少的压力和缺陷说出来。

我可以在原始 nWords[1] = 2 和全局 nNumItems 上看到此警告背后的一些想象中的可能原因。它们真的很奇怪,我认为合理的分析师不会将这样的规则添加到分析器中。另外,我是对的,那么你也应该在nWords[0] = 1 上看到这些警告。

你没有看到他们的事实证明我的想法是错误的,所以我停在这里。

相反,我想强调静态代码分析很难。分析器及其规则编写得多么好并不重要。在某些情况下,它会出错,而在其他情况下,它会简单地失败,甚至无法猜测,而在其他情况下,它会超时并放手。在我们在 AI 方面取得突破或在解决 NP 难题方面取得突破之前,您可能必须习惯这样一个事实,即当您使用静态代码分析器时,您必须以他们可以理解的方式编写代码,不要指望他们能理解你能写的一切。

最后想到,当我看到这个错误时:

file.cpp(18):警告 C6386:写入“nWords”时缓冲区溢出:可写大小为“nTotal*4”字节,但可能写入“8”字节。

我首先注意到的是nTotal*48。如果您使用的是硬编码值,您可能会收到类似

的错误

file.cpp(18):警告 C6386:写入“nWords”时缓冲区溢出:可写大小为“1024”字节,但可能写入“8192”字节。

您看到nTotal*4 的事实似乎暗示静态代码分析器实际上未能猜出nTotal 下的值并将其保留为符号名称,从而形成了与8 无法比拟的表达式。因此,分析器做了它唯一能做的事情——它报告了一个问题,并尽可能地描述了它。不过,这只是我的猜测。

// 编辑 - 丹关于猜测的答案的注释:nNumItems

我实际上认为他的大小可能与 SIZE_MAX 差不多。我玩了一些微软的 SAT 求解器,他们做得很好的一件事是求解整数域中的一组约束。实际上 unsigned int x = SIZE_MAX; std::cout << ( (3+2*x)*sizeof(int) ); 打印 4(当然),这是 x 的唯一值,其表达式小于 8。

我很确定我玩过的微软的约束求解器可以在检查整数环域中((3+2*x)*4) < 8 的可满足性时检测到这种情况 - 因此可能会发出警告。但是,我希望警告包含结果并打印如下内容:

nTotal*4

因为分析仪已经有了这些信息。但是,那是……可能对它期望太高了。它背后的开发人员可能不会想到格式化如此详细的警告消息,或者认为当前的消息格式更加用户友好。

【讨论】:

  • 静态分析至少和停机问题一样难。这就是为什么静态分析器并不总是做得很好的原因。
【解决方案2】:

由于nNumItems 是全局的,因此代码分析器似乎认为nNumItems 可能会在您的代码执行之前在其他地方设置为SIZE_MAX。您可以通过以下示例看到这一点:

size_t nNumItems = 0;

void foo()
{
    nNumItems = SIZE_MAX;
}
void bar()
{
    const size_t nTotal = 3 + 2 * nNumItems;
    auto nWords = new int[nTotal];

    nWords[0] = 1;
    nWords[1] = 2;
}

int main()
{
    foo();
    bar();

    return 0;
}

也许最好的解决方法是使用std::vector<int> 来回避整个问题。

【讨论】:

    【解决方案3】:

    将 Global 设为 const:

    const unsigned int nNumItems = 0;
    

    【讨论】:

    • 想必其他部分代码要改nNumItems
    【解决方案4】:

    这可能不是一个常见的解决方案,但在我的情况下,VS 产生了我认为是误报的警告。重新启动 VS 后,警告消失了。

    【讨论】:

    • 警告不会因重新启动 IDE 而消失。当然,您不会直接看到它们,因为消息窗口在启动时为空,但每次编译代码时都会再次出现警告。此外,其他答案已经证明这不是误报。在实际用例中,它可能会导致缓冲区溢出。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    相关资源
    最近更新 更多