为何Valgrind未检测到危险的堆内存释放错误?
Great question—let’s break down why your Valgrind 3.13.0 run isn’t flagging the issue, even though you’re clearly forcing an extra destructor call by tampering with heap metadata.
Key Reasons Valgrind Isn’t Reporting an Error
1. Valgrind Memcheck focuses on memory access, not destructor count mismatches
Valgrind’s Memcheck tool is built to catch invalid memory reads/writes, use-after-free, double-free, and memory leaks. It doesn’t inherently track whether the number of destructor calls matches the number of objects allocated—unless that extra destructor call leads to an illegal memory operation.
In your code, the Foo destructor only prints a string to cout; it never accesses any member variables or memory tied to the object. Even though you’re calling ~Foo() a fourth time (for an object that was never created), there’s no invalid memory access happening here. Valgrind has nothing to flag.
2. Your metadata modification works in Valgrind, but doesn’t trigger a violation
You’re modifying *(reinterpret_cast<int*>(ar)-2) = 4 based on assumptions about how glibc’s heap stores array size metadata. While Valgrind replaces the system malloc/free with its own tracking layer, the compiler still emits code that stores array size metadata in the same relative position (before the array pointer) as it would natively. So your tampering successfully tricks delete[] into calling 4 destructors—but since none of those calls touch invalid memory, Valgrind stays silent.
3. Old Valgrind version lacks modern checks
Valgrind 3.13.0 is a 2017 release, and newer versions (3.20+) have improved detection for edge cases like metadata tampering. Additionally, tools like AddressSanitizer (ASAN) are far more sensitive to this kind of issue: ASAN explicitly tracks allocation sizes and metadata, so it would immediately flag the mismatch between the 3 objects allocated and 4 destructors called.
How to Make Valgrind Catch This
To trigger a Valgrind error, modify your Foo struct to access member memory in the destructor:
#include <iostream> using namespace std; struct Foo { int data; // Add a member variable Foo(){ cout << "Creation Foo" << endl; data = 42; } ~Foo(){ cout << "Deletion Foo: " << data << endl; } // Access the member }; int main() { Foo* ar = new Foo[3]; *(reinterpret_cast<int*>(ar)-2) = 4; delete[] ar; return 0; }
Now, the fourth destructor call will attempt to read data from memory that was never allocated for a Foo object. Valgrind will catch this as an invalid read and report it clearly.
Alternative: Use AddressSanitizer
For this kind of metadata mismatch, ASAN is a better tool. Compile your code with:
g++ -g -fsanitize=address main.cpp
When you run the executable, ASAN will immediately detect the mismatch between the allocated array size (3 Foos) and the number of destructors called (4), and output a detailed error report.
内容的提问来源于stack exchange,提问作者Alex Aparin

