As you said in your question, the statement printf("%d\n", *c);
exposes undefined behaviour. Being undefined, any expectation you might have against it is false. This includes getting a specific error or any error at all.
The C runtime library doesn't check the way your program accesses the memory. If it would do it, the program would run much, much slower. Regarding the heap, the C runtime library does some basic checks; for example, it puts a certain value at address 0
during the program startup and checks if the value is still there when the program ends. If the value changed then a null pointer was de-referenced for writing and it can warn you about this.
After c = c + 20;
, c
most probably points to a block of memory that belongs to your program. It can be a free area on the heap, it can be inside the data structures used by the heap manager to handle the heap, but there are big chances that it is still on the same memory page.
If you have (bad) luck and c + 20
lands outside the memory page that stores c
then an exceptional situation happens that is handled by the operating system. It terminates the program and displays an error message similar with the one you listed in the question (the idea is the same, the words and the presentation differ on each OS).
Update
Allocating memory is not some kind of magic. The program starts with a block of memory (called "heap") that is assigned by the OS to the program for this purpose.
The C runtime library contains code that manages the heap. This code uses a small part of this memory for its bookkeeping. A common implementation uses double-linked lists, the payload of each node from the list being a block of memory "allocated" by the program using the functions declared in <memory.h>
(malloc()
, calloc()
etc). When a call to malloc()
happens, this code runs, creates a new node in the list and returns the address of the node's payload (an address inside the heap).
The program uses this pointer as it wants. Your program is free to write at c-1
, for example. In fact, inside malloc()
it actually wrote information there. After malloc()
returns c
, your code can also write at c-1
. From the OS
point of view, there is no difference between these two write operations. And because C
is not a managed or interpreted language, there is no code included in your program that watches what the code you wrote does or hold its hand to not write in the wrong place.
If you write at c-1
there is a big chance that you corrupt the data structures used by the heap manager. Nothing wrong happens immediately. No error message is displayed and your program continues to run apparently normal. But on the next call to a function that handles the heap (be it a memory allocation or a release) the program will start wreaking havoc. The heap data structures being corrupt anything could happen.
Regarding CBMC, I don't know how it works. Maybe it cannot detect such situations. Or maybe it reports your program as being safe because it doesn't write at c
after it was incremented.