
往期精选文章
AddressSanitizer:C/C++内存问题的终极检测利器
相关文章
AddressSanitizer:C/C++内存问题的终极检测利器
引言
在Linux环境下进行C/C++开发时,内存管理一直是最具挑战性的任务之一。系统性的内存错误轻则导致程序异常退出,重则引发安全漏洞。传统调试工具如GDB虽然强大,但对于内存错误的检测往往力不从心。这正是Valgrind大显身手的地方——它不仅是内存错误检测器,更是提高代码质量的利器。
作为Linux开发工具链的重要组成部分,Valgrind已在众多大型项目(如Linux内核、GCC、Firefox等)中证明其价值。本文将深入讲解如何在Linux环境下高效使用Valgrind进行内存调试。
Valgrind是一个用于构建动态分析工具的框架,核心是一个虚拟的CPU环境,程序在其中运行,从而实现对程序行为的完全监控。最常用的工具是Memcheck,专门用于检测内存管理错误。(地址: https://valgrind.org/docs/manual/manual.html)
# Ubuntu/Debiansudo apt updatesudo apt install valgrind# CentOSsudo yum install valgrind


# 下载最新源码wget https://sourceware.org/pub/valgrind/valgrind-3.19.0.tar.bz2tar -xjf valgrind-3.19.0.tar.bz2cd valgrind-3.19.0# 编译安装./configure --prefix=/usr/localmakesudo make install
Memcheck通过在指令级别插入检测代码来监控所有内存访问。它为堆内存维护一个"有效性位图"(validity bitmap)和"地址位图"(addressability bitmap),分别跟踪哪些字节是已定义的以及哪些字节是可寻址的。

堆内存泄漏: 程序结束前未释放所分配的内存
栈溢出
使用未初始化的值:条件判断、数据复制等。
非法释放:对非堆内存或者无效地址使用free/delete释放。
堆内存重复释放:对同一内存块多次调用free/delete释放。
堆内存溢出。
#include<stdio.h>#include<stdlib.h>#include<memory>// 1. check memory leakvoidtest_memory_leak(){printf("test memory leak...\n");int* p = (int*)malloc(sizeof(int));// free(p);}intmain(int argc, char** argv){test_memory_leak();return 0;}
g++ -g -o main main.cpp
#include<stdio.h>#include<stdlib.h>#include<memory>// 2. check memory out of bondvoidtest_out_of_bond(){printf("test out of bond...\n");int arr[10];arr[10] = 6; // max index = 9}intmain(int argc, char** argv){test_out_of_bond();return 0;}

#include<stdio.h>#include<stdlib.h>#include<memory>// 3. check use uninit memoryvoidtest_uninitialized_memory(){printf("test uninitialized memory...\n");int value;if(value >0) printf("value is pasitive...\n");}intmain(int argc, char** argv){test_uninitialized_memory();return 0;}
#include<stdio.h>#include<stdlib.h>#include<memory>// 4. invalid freevoidtest_invalid_free(){printf("test invalid free...\n");int stack_var;int *stack_ptr = &stack_var;free(stack_ptr);}intmain(int argc, char** argv){test_invalid_free();return 0;}

#include<stdio.h>#include<stdlib.h>#include<memory>// 5. double freevoidtest_double_free(){printf("test double free...\n");int* p = (int*)malloc(sizeof(int));free(p); // first freefree(p); // double free}intmain(int argc, char** argv){test_double_free();return 0;}

#include<stdio.h>#include<stdlib.h>#include<memory>// 6. heap overflowvoidtest_heap_overflow(){char* p = (char*)malloc(sizeof(char) * 4);p[4] = 6; // max index = 3free(p);}intmain(int argc, char** argv){test_heap_overflow();return 0;}

想要看到更多详细的信息,可以在执行时添加--verbose参数
valgrind --verbose ./main 如果想要检测结果信息输出到文件中,可以在执行时添加--log-file=xxx参数,如下所示
valgrind --log-file=valgrind_report.txt ./main 
Note:通过上述实测可知,valgrind还是存在比较大的局限性的,它主要是用于面向检测堆内存的一些场景。
性能开销大:程序运行速度降低20-30倍
无法检测所有栈溢出:某些栈内越界访问可能检测不到
需要源代码:对于优化过的二进制文件,定位问题困难
不支持某些系统调用:某些特定的内核交互可能有问题
Valgrind替代方案(AddressSanitizer)
关于AddressSanitizer 的使用说明在这篇文章AddressSanitizer:C/C++内存问题的终极检测利器 已经有详细说明,欢迎感兴趣的小伙伴前往阅读。