为什么在Visual Studio 2012的代码分析中,这段代码会发出缓冲区溢出警告(C6385/C6386)?

3

我正在尝试使用Visual Studio 2012的代码分析功能。我只是运行了现有项目上的分析,并发现了一些缓冲区溢出警告(C6385/C6386),这些警告出现在包含我自己实现的Knuth减法PRNG(又名RAN3)的部分中。然而,我不明白为什么会出现这种情况,因为看起来很好(我没有看到越界读取/写入)。所以我制作了一个等效的简短版本(如下),但仍然收到相同的警告,无法找出原因。

int main() {
  unsigned int k = 1U, seed = 12345U, randomNumbers[55];

  randomNumbers[54] = seed;
  for(unsigned int i = 1U; i <= 54U; ++i) {
    unsigned int ii = ((21U * i) % 55U) - 1U;
    randomNumbers[ii] = k;
    k = seed - k;
    seed = randomNumbers[ii];
  }

  return 0;
}

使用上述代码,我在第7行得到了C6386警告,在第9行得到了C6385。这段代码有什么问题?我是否遗漏了什么?

3个回答

1

使用-g++ 4.8和clang++ 3.3编译此代码时,不会出现警告或错误(使用-Wall -Werror)。实际上,我们可以使用C++11的std::array及其at方法来进行边界检查,同时保留HTML标签。

#include <array>

int main() {
  unsigned int k = 1U, seed = 12345U;
  std::array<int,55> randomNumbers;

  randomNumbers.at(54) = seed;

  for(unsigned int i = 1U; i <= 54U; ++i) {
    unsigned int ii = ((21U * i) % 55U) - 1U;
    randomNumbers.at(ii) = k;
    k = seed - k;
    seed = randomNumbers.at(ii);
  }

  return 0;
}

你所声称的代码没有越界访问,我认为你的代码很好。VS担心这一行((21U * i) % 55U) - 1U)可能会导致0-1的溢出,因为ii是无符号整数。如果使用int而不是unsigned int,VS是否仍然会抱怨?
(使用Python,您的索引映射似乎很好:
>>> sorted([21*n % 55 - 1 for n in range(1,55)])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53]

不应该出现任何越界错误,特别是在使用无符号整数时,您不会“到达” -1。


我尝试用int替换unsigned int:http://ideone.com/URAjjw 然而,相同的警告被发出。嗯,它们是误报吗? - k2_8191

0

输出变量ii的值,查看是否超出了54 该行代码计算如下 unsigned int ii = ((21U * i) % 55U) - 1U;


当 ((21U * i) % 55U) = 0 时,ii 将为 -1,表示溢出。 - Anand Rathi
2
@AnandRathi 这是我最初的想法,但循环从1开始。 - juanchopanza

0
首先,请理解静态分析的目的,它会对代码库进行分析,以审查并确保代码符合行业标准。这是软件质量控制的第一步,并且始终与动态分析一起使用,以发现静态分析无法找到的微妙问题和漏洞。模糊测试用于通过动态分析确保软件安全和质量。
静态代码分析也可能导致误报,代码审计员应该忽略这些情况。您在此处报告的情况是虚警。
#pragma warning(suppress:6385)

现在让我们深入探讨您的情况,静态分析发现您的指令((21U * i) % 55U)可能会评估为零,这可能导致ii = -1;这进一步可能导致软件崩溃甚至安全漏洞(缓冲区溢出攻击)。与动态分析不同,静态分析不会执行所有循环迭代来断言代码的每个分支都执行良好。
现在让我们谈谈您的代码,我有一些建议和改进。请引入一些常量或#定义来保存数组的大小。
#define _SIZE 55U

现在,不要再在代码中使用硬编码的数字'55'和'54',而是使用_SIZE。您可以引入实用函数来获取数组的安全边界。
int SafeBoundsInt32(int min, int max, int value)
{
    if (value < 0)
        return 0;
    if (value >= max)
        return max - 1;
    //Valid value
    return value;
}

我将根据所介绍的函数更改您的代码。

int main()
{
    unsigned int k = 1U, seed = 12345U, randomNumbers[_SIZE];

    randomNumbers[_SIZE - 1] = seed;
    for (unsigned int i = 1U; i <= _SIZE - 1; ++i)
    {
        unsigned int ii = ((21U * i) % _SIZE) - 1U;
        randomNumbers[SafeBoundsInt32(0, _SIZE, ii)] = k;
        k = seed - k;
        seed = randomNumbers[SafeBoundsInt32(0, _SIZE, ii)];
    }

    return 0;
}

现在执行代码分析,它将不会报告任何警告。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接