0

I thought the following code snippets would cause double free, and the program would core dump. But the truth is that there is no error when I run the code?

Similar problem shows that it caused double free!

My Question is why does there have no error show that there is a double free? And why does there have no core dump?

#include <iostream>
using namespace std;

int main()
{
    int *p = new int(5);
    cout << "The value that p points to: " << (*p) << endl;
    cout << "The address that p points to: " << &(*p) << endl;
    delete p;
    cout << "The value that p points to: " << (*p) << endl;
    cout << "The address that p points to: " << &(*p) << endl;
    delete p;
    cout << "The value that p points to: " << (*p) << endl;
    cout << "The address that p points to: " << &(*p) << endl;
    delete p;
}

The program's output when I ran this program is shown as followed: Program output

After modifying the code snippet like the following, the core dump occured:

#include <iostream>
using namespace std;

int main()
{
    int *p = new int(5);
    for (;;)
    {
        cout << "The value that p points to: " << (*p) << endl;
        cout << "The address that p points to: " << &(*p) << endl;
        delete p;
    }
    return 0;
}

And the program output is : Modified program output

So there is another question that why this program will core dump every time?

George
  • 35
  • 4
  • *why does there have no error show that there is a double free?* – Because a compiler is not required to provide diagnostic for something like that. Don't use owning pointers in the first place. – Swordfish Sep 04 '20 at 01:21

2 Answers2

4

Yes, it is a double free (well, triple, really) which puts it into undefined behaviour territory.

But that's the insidious thing about undefined behaviour, it's not required to crash or complain, it's not required to do anything at all(a). It may even work.

I can envisage an implementation that stores the free state of a block in the control information for it so that freeing it twice would have no effect. However, that would be inefficient, and also wouldn't cover the case where it had been reallocated for another purpose (it would prevent double frees, but not a piece of code freeing the block when some other piece still thinks it still has it).

So, given it's not required to work, you would be well advised to steer clear of it since it may also download maniacal_laughter.ogg and play it while erasing your primary drive.

As an aside, modern C++ has smart pointers that are able to manage their own lifetime, and you would be doing yourself a big favour if you started using those instead of raw pointers.
And, although the removal of raw pointer from C++ was a joke, there are some that think it's not such a bad idea :-)


(a) The C++20 standard has this to say when describing undefined behaviour in [defns.undefined] (my emphasis):

Behavior for which this document imposes **NO** requirements.

paxdiablo
  • 772,407
  • 210
  • 1,477
  • 1,841
-1

why does there have no error show that there is a double free? And why does there have no core dump?

delete p;
cout << "The value that p points to: " << (*p) << endl;

The moment you referenced to a deleted pointer is when the program entered an undefined behaviour, and then there is no guarantee that there would be an error or a crash.

It's not entirely the same, but the analogy between memory and a hotel room is applicable, which explains well what an undefined behaviour means. Highly recommended reading:

Can a local variable's memory be accessed outside its scope?

artm
  • 16,141
  • 4
  • 27
  • 46
  • *when the program entered an undefined behaviour* – A program cannot "enter ub". If there is ub in a program the whole program has ub and is no longer a valid C++-program. – Swordfish Sep 04 '20 at 01:24
  • Is there anyway to modify the code snippet and let it core dump every time? – George Sep 04 '20 at 01:25
  • @George re-read my comment. The answer to your question: NO. – Swordfish Sep 04 '20 at 01:26
  • @Swordfish it's just terminology, enter UB or has UB, to me not that much difference. – artm Sep 04 '20 at 01:26
  • @artm it isn't just terminology. If a program has UB it has UB. The whole thing. Not just when execution comes to a point at which UB might occur. The whole thing. – Swordfish Sep 04 '20 at 01:27
  • UB can time-travel, unfortunately, so you can't count on the program being unaffected until the flow hits it. Admittedly this is rare, but it's a real mind when it happens. – user4581301 Sep 04 '20 at 01:30
  • 1
    @George The behaviour of Undefined Behaviour is undefined, so there is no way you can modify your program to guarantee a particular result. That said, the results are often quite predictable in practice, given sufficient information about the target platform, but I can't think of a way to guaranteed hard-and-visible error here. The best thing to do is avoid the error entirely [with smarter pointer management](https://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one). – user4581301 Sep 04 '20 at 01:36
  • An example of predictable UB: in Visual Studio's `std::vector` implementation for debug builds, the programmers went above the call of duty and added bounds checking and an abort if the bounds are violated. As a result, the results of out of bounds access in this case are 100% predictable. And that proves my earlier statement somewhat wrong: You could overload the `new` and `delete` operators to add some extra book-keeping to trap mistakes like this. This is similar to what Valgrind does, replacing low-level memory management functions, with slower, smarter versions. – user4581301 Sep 04 '20 at 01:42