Valgrind通过Memcheck实时监控堆内存生命周期,free后访问即报Invalid read/write;需-g编译保留调试信息,否则无法定位源码行;仅检测堆内存,栈/全局越界需AddressSanitizer辅助。

Valgrind 怎么发现 use-after-free 错误
Memcheck 能在程序运行时实时拦截所有内存访问指令,并为每块堆内存维护一个“生命周期状态”。一旦 free 被调用,对应内存块的状态就从 “allocated” 变为 “freed”,后续任何对该地址的读或写都会被立即标记为非法。
关键依赖:编译时必须带 -g 且不 strip 符号
没有调试信息,Valgrind 只能报告出错地址和汇编偏移,无法定位到源码行。常见错误现象是看到一堆 vg_replace_malloc.c 或 ??? 的栈帧,根本没法修。
- 正确做法:
gcc -g -O0或g++ -g -O0编译(-O0避免内联掩盖调用链) - 千万别用
strip ./a.out—— 符号一丢,valgrind就失去源码映射能力 - 如果用 CMake,确认
set(CMAKE_BUILD_TYPE Debug)已启用,且未设置CMAKE_STRIP
--tool=memcheck 默认就检测 use-after-free,无需额外开关
只要没显式关掉 Memcheck,默认行为就是全程监控所有堆内存读写。你不需要加 --leak-check 或其他参数来触发 use-after-free 检测——它和内存泄漏是两个独立检查项,互不依赖。
- 典型错误输出开头是:
Invalid read of size 4或Invalid write of size 1 - 紧接着会显示该地址最初由哪次
malloc/new分配,以及在哪被free/delete - 最后一行栈帧指向真正出问题的那行 C/C++ 代码,比如
at 0x1093AB: string_tolower (utilities.c:125)
容易被忽略的边界情况:栈/全局变量不会被检测
Memcheck 只监控堆内存(malloc/new 分配的),对栈上局部数组或全局静态变量的越界访问不报错。比如:
char buf[10]; buf[15] = 'x'; // Valgrind 不管这个
这种问题得靠 AddressSanitizer(-fsanitize=address)来捕获。另外,Memcheck 也无法识别指针算术溢出本身,只在最终解引用时才触发,所以提前做边界检查仍是必要的。


















