Please note that I don't want to solve any problem with my question - I was thinking about probabilities of things to happen and thus was wondering about something:
What exactly happens if you delete on object and use gcc as compiler?
Last week I was investigating a crash, where a race condition lead to an double delete of an object.
The crash occured when calling the virtual destructor of the object, because the pointer to the virtual function table already was overwritten.
Is the virtual function pointer overwritten by the first delete?
If not, is the second delete safe then, as long as no new memory allocation is made in the meantime?
I'm wondering why the problem I had was not recognized before and the only exlanation is that either the virtual function table is overwritten immediatly during the first delete or the second delete does not crash.
(The first means that the crash always occurs on the same location if the "race" happens - the second one, that usually nothing happens when the race happens - and only if a third thread overwrites the delete object in the meantime the problem occurs.)
Edit/Update:
I did a test, the following code crashes with a segfault (gcc 4.4, i686 and amd64):
class M
{
private:
int* ptr;
public:
M() {
ptr = new int[1];
}
virtual ~M() {delete ptr;}
};
int main(int argc, char** argv)
{
M* ptr = new M();
delete ptr;
delete ptr;
}
If I remove the 'virtual' from the dtor, the program is aborted by glibc because it detects the double-free. With the 'virtual' the crash occurs when doing the indirect function call to the destructor, because the pointer to the virtual function table is invalid.
On both amd64 and i686 the pointer points to a valid memory region (heap), but the value there is invalid (a counter? It's very low, e.g 0x11, or 0x21) so the 'call' (or 'jmp' when the compiler did a return-optimization) jumps to an invalid region.
Program received signal SIGSEGV,
Segmentation fault. 0x0000000000000021
in ?? () (gdb)
#
0 0x0000000000000021 in ?? ()
#
1 0x000000000040083e in main ()
So with the above mentioned conditions, the pointer to the virtual function table is ALWAYS overwritten by the first delete, so the next delete will jump to nirvana if the class has a virtual destructor.