Memcheck:一个内存错误检测器

要使用此工具,可以--tool=memcheck 在Valgrind命令行中指定。不过,由于Memcheck是默认工具,所以您不需要。

4.1。概观

Memcheck是一个内存错误检测器。它可以检测C和C ++程序中常见的以下问题。

  • 访问内存您不应该,例如超越和低估堆块,超出堆栈的顶部,以及在释放后访问内存。

  • 使用未定义的值,即尚未初始化的值,或已从其他未定义值导出的值。

  • 不正确的释放堆内存,比如双重释放堆块,或使用 mallocnewnew[] 对 freedelete/delete[]

  • 重叠src和 dst指针 memcpy以及相关功能。

  • 将一个腥味(大概为负)值传递给 size一个内存分配功能的参数。

  • 内存泄漏。

像这样的问题可能难以通过其他方式找到,经常长时间不被发现,然后导致偶尔的,难以诊断的崩溃。

4.2。来自Memcheck的错误消息说明

Memcheck发出一系列错误消息。本节将简要介绍哪些错误消息的意思。错误检查机械的精确行为在Memcheck检查机械的详细信息中有所描述

4.2.1。非法读取/非法写入错误

例如:

大小4读取无效
   在0x40F6BBCC :(在/usr/lib/libpng.so.2.1.0.9中)
   由0x40F6B804 :(在/usr/lib/libpng.so.2.1.0.9中)
   by 0x40B07FF4:read_png_image(QImageIO *)(kernel / qpngio.cpp:326)
   by 0x40AC751B:QImageIO :: read()(kernel / qimage.cpp:3621)
 地址0xBFFFF0E0不是stack'd,malloc'd或free'd

当您的程序在Memcheck认为不应该读取或写入内存时,会发生这种情况。在这个例子中,程序做了4个字节的地址处读0xBFFFF0E0,内系统提供的库libpng.so.2.1.0.9,将其从其他地方以相同的库调用某处,从326行调用qpngio.cpp,等等上。

Memcheck尝试确定非法地址可能涉及什么,因为这通常是有用的。所以,如果它指向一个已经被释放的内存块,那么你会被通知这个,而且块被释放。同样,如果它应该是刚刚结束堆块,这是数组下标中一个一个错误的常见结果,你会被告知这个事实,以及块分配的地方。如果您使用--read-var-info选项Memcheck将运行更慢,但可能会更详细地描述任何非法地址。

在这个例子中,Memcheck不能识别地址。实际上地址在堆栈上,但是由于某种原因,这不是有效的堆栈地址 - 它不在堆栈指针下面,这是不允许的。在这种特殊情况下,这可能是由GCC产生的无效代码造成的,这是古代GCC的一个已知bug。

请注意,Memcheck仅告诉您,您的程序即将访问非法地址的内存。它不能阻止访问发生。所以,如果您的程序进行通常会导致分段错误的访问,您的程序仍然会遇到相同的命运 - 但​​是在此之前您将收到来自Memcheck的消息。在这个特定的例子中,在堆栈上读取垃圾是非致命的,并且程序保持活着。

4.2.2。使用未初始化的值

例如:

有条件的跳跃或移动取决于未初始化的值
   在0x402DFA94:_IO_vfprintf(_itoa.h:49)
   by 0x402E8476:_IO_printf(printf.c:36)
   通过0x8048472:main(tests / manuel1.c:8)

当程序使用尚未初始化的值(换句话说,未定义)时,将报告未初始化值使用错误。这里,未定义的值用于printfC库机械的 某处。运行以下小程序时报告此错误:

int main()
{
  int x;
  printf(“x =%d \ n”,x);
}

重要的是要了解,您的程序可以尽可能多地复制垃圾(未初始化)数据。Memcheck观察这个并且跟踪数据,但不会抱怨。只有当您的程序尝试以可能影响程序外部可见行为的方式使用未初始化的数据时,才会发出投诉。在此示例中,x未初始化。Memcheck观察到传递给的值_IO_printf,然后发送给_IO_vfprintf,但不发表任何评论。但是, _IO_vfprintf必须检查它的值, x所以它可以把它变成相应的ASCII字符串,而在这一点Memcheck抱怨。

未初始化数据的来源往往是:

  • 程序中的局部变量尚未初始化,如上例所示。

  • 在你(或构造函数)之前的堆块(分配给 mallocnew或类似的函数)的内容写在那里。

要查看程序中未初始化数据源的信息,请使用该--track-origins=yes选项。这使得Memcheck运行速度更慢,但可以更容易地追踪未初始化值错误的根本原因。

4.2.3。在系统调用中使用未初始化或不可寻址的值

Memcheck检查系统调用的所有参数:

  • 它会自动检查所有直接参数,无论它们是初始化的。

  • 此外,如果系统调用需要从程序提供的缓冲区中读取,Memcheck会检查整个缓冲区是否可寻址,并将其内容初始化。

  • 此外,如果系统调用需要写入用户提供的缓冲区,Memcheck会检查缓冲区是否可寻址。

系统调用后,Memcheck更新其跟踪信息,以精确反映系统调用引起的内存状态变化。

以下是两个无效参数的系统调用示例:

  #include <stdlib.h>
  #include <unistd.h>
  int main(void)
  {
    char * arr = malloc(10);
    int * arr2 = malloc(sizeof(int));
    写(1 / * stdout * /,arr,10);
    出口(ARR2 [0]);
  }

你得到这些投诉...

  Syscall param write(buf)指向未初始化的字节
     在0x25A48723:__write_nocancel(在/lib/tls/libc-2.3.3.so)
     by 0x259AFAD3:__libc_start_main(in /lib/tls/libc-2.3.3.so)
     由0x8048348 :(在/auto/homes/njn25/grind/head4/a.out中)
   地址0x25AB8028是大小为10的块内的0个字节
     在0x259852B0:malloc(vg_replace_malloc.c:130)
     由0x80483F1:主(ac:5)

  Syscall param exit(error_code)包含未初始化的字节
     在0x25A21B44:__GI__exit(在/lib/tls/libc-2.3.3.so)
     通过0x8048426:main(ac:8)

因为程序具有(a)从堆块将未初始化的垃圾写入标准输出,并且(b)将未初始化的值传递给exit。请注意,第一个错误是指由buf(不是 buf本身)指向的内存 ,但是第二个错误直接指向了exit参数 arr2[0]

4.2.4。非法释放

例如:

无效()
   在0x4004FFDF:free(vg_clientmalloc.c:577)
   通过0x80484C7:main(tests / doublefree.c:10)
 地址0x3807F7B4是一个大小为177的空闲块内的0个字节
   在0x4004FFDF:free(vg_clientmalloc.c:577)
   通过0x80484C7:main(tests / doublefree.c:10)

Memcheck使用malloc/ 跟踪程序分配的块new,所以它可以准确地知道free/ / 的参数 delete是否合法。这里,这个测试程序已经释放了同一个程序段两次。与非法读/写错误一样,Memcheck尝试了解地址的释放。如果在这里,地址是先前被释放的地址,那么你会被告知 - 同一块的重复释放容易发现。如果您尝试释放不指向堆块开始的指针,您还将收到此消息。

4.2.5。当一个堆块被释放不正当的解除分配功能

在以下示例中,分配的块 new[]错误地被释放 free

free()/ delete / delete []
   在0x40043249:free(vg_clientfuncs.c:171)
   by 0x4102BB4E:QGArray ::〜QGArray(void)(tools / qgarray.cpp:149)
   由0x4C261C41:PptDoc ::〜PptDoc(void)(include / qmemarray.h:60)
   通过0x4C261F0E:PptXml ::〜PptXml(void)(pptxml.cc:44)
 地址0x4BB292A8是一个大小为64个alloc'd的块内的0个字节
   在0x4004318C:operator new [](unsigned int)(vg_clientfuncs.c:152)
   by 0x4C21BC15:KLaola :: readSBStream(int)const(klaola.cc:314)
   by 0x4C21C155:KLaola :: stream(KLaola :: OLENode const *)(klaola.cc:416)
   通过0x4C21788F:OLEFilter :: convert(QCString const&)(olefilter.cc:272)

C++它与它是如何分配兼容的方式来释放内存是很重要的。交易是:

  • 如果与分配 malloc, calloc, realloc, valloc或者 memalign,您必须取消分配free

  • 如果分配new,你必须解除配置delete

  • 如果分配new[],你必须解除配置delete[]

最糟糕的是,在Linux上显然无论如何混合使用,但同样的程序可能会在不同的平台(例如Solaris)上崩溃。所以最好是妥善解决它。根据KDE的人“令人惊奇的是有多少C ++程序员不知道这个”。

要求背后的原因如下。在某些C ++实现中,delete[]必须使用分配的对象,new[]因为在指针实际返回之前,编译器会将数组的大小和指针成员存储到数组的内容的析构函数中。 delete不解决这个问题,会混淆,可能会破坏堆。

4.2.6。重叠的源和目标块

下面的C库函数从一个存储器块复制一些数据到另一个(或类似的东西): , memcpy, strcpy, strncpy, 。strcat strncat由他们src和 dst指针指向的块不允许重叠。POSIX标准具有“如果在重叠的对象之间进行复制,行为未定义”。因此,Memcheck检查这一点。

例如:

== 27492 == memcpy中的源和目的地重叠(0xbffff294,0xbffff280,21)
== 27492 == 0x40026CDC:memcpy(mc_replace_strmem.c:71)
== 27492 == by 0x804865A:main(overlap.c:40)

您不希望两个块重叠,因为其中一个块可能被复制部分覆盖。

你可能会认为,Memcheck在dst小于等于 的情况下被过度迂回地报告src。例如,明显的实现方式memcpy是从第一个字节到最后一个字节的复制。但是,某些架构的优化指南建议从最后一个字节复制到第一个。而且,复制前的一些实现memcpy为零 dst,因为归零目的地的高速缓存行可以提高性能。

故事的道德是:如果你想写真正的便携式代码,不要对语言实现做任何假设。

4.2.7。有趣的参数值

所有内存分配函数都会使用一个参数来指定应该分配的内存块的大小。显然,请求的大小应该是非负值,通常不会过大。例如,在64位机器上,分配请求的大小超过2 ** 63个字节是非常不起眼的。更可能的是,这样的值是错误大小计算的结果,并且实际上是负值(恰好恰好出现过大,因为位模式被解释为无符号整数)。这样的值被称为“腥值”。所述size的分配如下功能参数被检查为腥: malloc, calloc, realloc, memalign, new, new []。 __builtin_new, __builtin_vec_newcalloc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值