3

我以前没有使用过 valgrind,但我认为它应该检测到一些内存错误。

我的代码:

#include <stdio.h>
unsigned int a[2];

int main()
{
    a[-1] = 21;

    printf("%d,", a[-1]);

    return 1;
}

如您所见,我正在访问a[-1]我不应该访问的内容。

我如何使用 valgrind?

我正在编译gcc -g -O0 codeFile.c

并执行:valgrind -s ./a.out

结果是:

==239== Memcheck,内存错误检测器

==239== 版权所有 (C) 2002-2017 和 GNU GPL,由 Julian Seward 等人提供。

==239== 使用 Valgrind-3.16.0.GIT 和 LibVEX;使用 -h 重新运行以获取版权信息

==239== 命令:./a.out

==239== 21,==239==

==239== 堆摘要:

==239== 在退出时使用:0 个块中的 0 个字节

==239== 总堆使用量:1 次分配,1 次释放,分配 1,024 字节

==239==

==239== 所有堆块都被释放——不可能有泄漏

==239==

==239== 错误摘要:0 个上下文中的 0 个错误(抑制:0 个来自 0)

valgrind 不应该发现这些错误,还是我用错了?

[ _ -10] 而不是 [1]。相同的行为。

int main()
{
    unsigned int a[2];
    a[-10] = 21;

    printf("%d,", a[-10]);

    return 1;
}

如果我使用 [-100] 它实际上会引发错误。这是怎么回事?

编辑 2

此外,为什么这没有错误

while (i <= 2)
    {
        j = a[i];       
        i++;
    }

但这确实

while (i <= 2)
    {
        printf("%d,", a[i]);        
        i++;
    }
4

2 回答 2

5

Valgrind 通常无法找到正在修改的内存与当前堆栈指针或内存的负偏移量与内存中的另一个变量重合的内存错误。

例如,如果a在堆栈上,a[3]将触发memcheck. a[-1]不会,因为据 Valgrind 所知,这很容易成为有效记忆。

为了对此进行扩展,这里引用了文档中的引述,并强调了这一点:

在此示例中,Memcheck 无法识别地址。实际上地址在堆栈上,但是,由于某种原因,这不是一个有效的堆栈地址——它在堆栈指针之下,这是不允许的

这句话实际上部分不正确;当它说“低于堆栈指针”时,它实际上意味着与堆栈指针有正偏移,或者干扰另一个函数的堆栈内存。

我还应该注意到(从您的第二次编辑中)Valgrind 实际上并没有抱怨,直到该值以某种有意义的方式使用。在 Valgrind 看来,赋值不是以有意义的方式使用值。这是另一个引用来支持这一点,并补充了我的重点:

重要的是要了解您的程序可以尽可能多地复制垃圾(未初始化)数据。Memcheck 观察到这一点并跟踪数据,但不会抱怨。仅当您的程序尝试以可能影响程序的外部可见行为的方式使用未初始化的数据时,才会发出投诉。

因为a它是一个全局变量,所以您将很难检查它的内存。我之前使用过的一个 Valgrind 工具是exp-sgcheck(实验性静态和全局变量检查),尽管我发现它不可靠(很可能是因为它是实验性的)。

检测这些问题的更简单和更好的方法是启用编译器警告或使用静态分析器(我最喜欢的是 LLVM's scan-build)。

于 2019-12-14T20:22:25.687 回答
1

您声明a为全局数组,因此用于--tool=exp-sgcheck检查堆栈和全局数组是否溢出。请记住,这--tool=exp-sgcheck是一个实验性的实现,所以它不会在启用时出现,-s或者--show-error-list=yes,您可以在此处阅读更多相关信息。

于 2019-12-14T20:39:19.367 回答