Valgrind详细教程(1) Memcheck

一、简介

Memcheck是Valgrind的王牌,它用于C/C++程序的内存错误检测:

  1. 非法访问内存(堆、栈、内存段错误)
  2. 引用未初始化的变量
  3. 非法释放对内存(重复释放、释放与申请不匹配)
  4. 内存重叠错误
  5. 内存泄露
  6. 错误地申请内存

此外,Memcheck还可以用于内存树的分析。


二、非法访问内存

2.1 代码

int main( void )
{
    int *ptr = 0;
    *ptr = 0;
    return 0;
}

2.2 执行

$ gcc test.c -g
$ ./a.out
Segmentation fault (core dumped)

2.3 调试

$ valgrind ./a.out
···
==2681== Invalid write of size 4
==2681==    at 0x10860A: main (test.c:4)
==2681==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==2681== 
==2681== 
==2681== Process terminating with default action of signal 11 (SIGSEGV)
==2681==  Access not within mapped region at address 0x0
==2681==    at 0x10860A: main (test.c:4)
==2681==  If you believe this happened as a result of a stack
==2681==  overflow in your program's main thread (unlikely but
==2681==  possible), you can try to increase the size of the
==2681==  main thread stack using the --main-stacksize= flag.
==2681==  The main thread stack size used in this run was 8388608.
···

test.c的第4行:非法访问地址为0x0的变量。这一错误导致程序被信号SIGSEGV终止。

注意:

  1. 编译时需要带调试选项
  2. 由于memcheck是默认工具,因此省略--tool=<name>memcheck

三、引用未初始化的变量

3.1 代码(一)

#include <stdio.h>

int main( void )
{
    int x;
    printf("x = %d\n", x);
    return 0;
}

3.2 调试(一)

$ valgrind ./a.out
···
==2698== Conditional jump or move depends on uninitialised value(s)
==2698==    at 0x4E988DA: vfprintf (vfprintf.c:1642)
==2698==    by 0x4EA0F25: printf (printf.c:33)
==2698==    by 0x108667: main (test.c:6)
==2698== 
==2698== Use of uninitialised value of size 8
==2698==    at 0x4E9486B: _itoa_word (_itoa.c:179)
==2698==    by 0x4E97F0D: vfprintf (vfprintf.c:1642)
==2698==    by 0x4EA0F25: printf (printf.c:33)
==2698==    by 0x108667: main (test.c:6)
···

test.c的第6行:在函数 printf() 中访问了一个未初始化的变量。

3.3 代码(二)

#include <stdlib.h>

int main( void )
{
    int *arr = malloc(sizeof(int));
    exit(arr[0]);
    return 0;
}

3.4 调试(二)

$ valgrind ./a.out
···
==2707== Syscall param exit_group(status) contains uninitialised byte(s)
==2707==    at 0x4F20E06: _Exit (_exit.c:31)
==2707==    by 0x4E7F111: __run_exit_handlers (exit.c:132)
==2707==    by 0x4E7F139: exit (exit.c:139)
==2707==    by 0x1086AC: main (test.c:6)
···

test.c的第6行:在系统调用 exit() 中访问了一个未初始化的变量。


四、非法释放对内存

4.1 代码(一)

#include <stdlib.h>

int main( void )
{
    int *ptr = malloc(10);
    free(ptr);
    free(ptr);
    return 0;
}

4.2 调试(一)

$ valgrind ./a.out
···
==2715== Invalid free() / delete / delete[] / realloc()
==2715==    at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2715==    by 0x1086B7: main (test.c:7)
==2715==  Address 0x522d040 is 0 bytes inside a block of size 10 free'd
==2715==    at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2715==    by 0x1086AB: main (test.c:6)
==2715==  Block was alloc'd at
==2715==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2715==    by 0x10869B: main (test.c:5)
···

内存在 test.c 的第5行分配,第6行正确释放,而第7行发生非法释放。

4.3 代码(二)

#include <cstdlib>

int main(void)
{
    int *ptr = (int*)malloc(10);
    delete ptr;
    return 0;
}

4.4 调试(二)

$ valgrind ./a.out
···
==2265== Mismatched free() / delete / delete []
==2265==    at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2265==    by 0x1086F0: main (test.cpp:6)
==2265==  Address 0x5b7dc80 is 0 bytes inside a block of size 10 alloc'd
==2265==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2265==    by 0x1086DB: main (test.cpp:5)
···

内存在 test.c 的第5行分配 (malloc),第6行发生非法释放 (free)。


五、内存重叠错误

5.1 代码

#include <stdio.h>
#include <string.h>

void *memcpy(void *dest, const void *src, size_t n)
{
    char *d=dest;
    const char *s=src;
    for( int i=0; i<n; ++i) {
        d[i] = s[i];
    }
    return (void*)0;
}

int main(void)
{
    char arr[16];
    memset(arr, 0, sizeof(arr));

    strcpy(arr, "overlap");
    printf("%s\n", arr);

    memcpy(arr+3, arr, strlen(arr));
    printf("%s\n", arr);

    return 0;
}

5.2 想要达到的输出

overlap
oveoverlap

5.3 发生内存重叠时的输出

overlap
oveoveoveo

5.4 Valgrind输出示例

==27492== Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)
==27492==    at 0x40026CDC: memcpy (mc_replace_strmem.c:71)
==27492==    by 0x804865A: main (overlap.c:40)

5.5 改正方法

将memcpy函数修改为下面代码即可

void *memcpy(void *dest, const void *src, size_t n)
{
    char *arr = malloc(n);
    char *d=dest;
    const char *s=src;

    for( int i=0; i<n; ++i) {
        arr[i] = s[i];
    }

    for( int i=0; i<n; ++i) {
        d[i] = arr[i];
    }

    return (void*)0;
}

通过先将src复制一份,达到防止内存覆盖错误的目的。


六、内存泄露

6.1 代码

#include <stdlib.h>

int main( void )
{
    char *ptr = malloc(100);
    ptr = malloc(50);
    free(ptr);
    return 0;
}

6.2 调试

$ valgrind --tool=memcheck --leak-check=full ./a.out
···
==2646== HEAP SUMMARY:
==2646==     in use at exit: 100 bytes in 1 blocks
==2646==   total heap usage: 2 allocs, 1 frees, 150 bytes allocated
==2646== 
==2646== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2646==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2646==    by 0x10869B: main (test.c:5)
==2646== 
==2646== LEAK SUMMARY:
==2646==    definitely lost: 100 bytes in 1 blocks
==2646==    indirectly lost: 0 bytes in 0 blocks
==2646==      possibly lost: 0 bytes in 0 blocks
==2646==    still reachable: 0 bytes in 0 blocks
==2646==         suppressed: 0 bytes in 0 blocks
···

第5行:malloc() 申请的内存泄露了。很明显,两个 malloc() 只有一个 free() 与之对应。

Leak Summary 各项解析:

类型解析
definitely lost确切泄露
indirectly lost间接泄露
possibly lost可能泄露
still reachable未free,但尚可引用

七、错误地申请内存

7.1 代码

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
    int val = -1;
    
    char *ptr = malloc(val);
    printf("ptr = %p \n", ptr);
    
    if( ptr != 0 ) free(ptr);
    return 0;
}

7.2 调试

$ valgrind ./a.out
···
==2885== Argument 'size' of function malloc has a fishy (possibly negative) value: -1
==2885==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2885==    by 0x1086F2: main (test.c:8)
==2885== 
ptr = (nil)
···

第6行:向系统申请负空间是不明智的。此时 malloc() 返回一个空指针。因此,检查 malloc() 返回的指针是很有必要的!同理,检查 malloc() 的入参也很有必要!

  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值