1

We can create an object through casting a contiguous memory block into object. If we create an object with little memory, it's not crashing while accessing it. But it's crashing while going out of scope. Why?

class A
{
    public:
        int i;
};
void main(int argc, char * argv[])
{
    {
        vector<BYTE> v(1);
        A* a = (A*)v.data();
        a->i = 70000;          // a's memory can't hold 4 bytes (int). Why no crash?
        cout << a->i << endl;  // Printed value - 70000. Why not crash here
    }                          // Crashed here with heap corruption detected
    cout << "End of scope\n";
}
Naresh T
  • 309
  • 2
  • 14
  • 3
    Casting does not allocate memory. It just interprets existing memory different. So there is no problem putting smaller data in bigger memory. On the other hand bigger data in a memory which is smaller does not fit, which causes errors. – Hayt Aug 31 '16 at 09:30
  • 1
    Putting larger data in smaller memory is a UB. Yes, in your case this program does not crash, but it's just a plain luck – alexeykuzmin0 Aug 31 '16 at 09:33
  • 1
    please can you provide details for BYTE? Is it "typedef unsigned char BYTE;" I used typedef std::bitset<8> BYTE; which does not crash in VS2015. – robor Aug 31 '16 at 09:33
  • There is no concept of "crashing" in C++. As long as the behaviour of your program is defined by the C++ language, it does not "crash". – Kerrek SB Aug 31 '16 at 09:34
  • @KerrekSB now now... Also, how would you describe the behaviour of a program that crashes due to stack overflow? (According to the standard, any implementation that fails to execute a program due to stack overflow is non-conforming) – M.M Aug 31 '16 at 09:39
  • What your program does is Undefined Behaviour. It can therefore do anything from appearing to work to crashing etc. – Richard Critten Aug 31 '16 at 09:40
  • Also for C++ when allocating an object inside a block of memory use Placement New http://en.cppreference.com/w/cpp/language/new Remember to take alignment into account. – Richard Critten Aug 31 '16 at 09:44
  • Casting doesn't create an object. Even if `v` *did* have room for an `A`, `a->i` would be undefined. – molbdnilo Aug 31 '16 at 09:47
  • BYTE has the following declaration: typedef unsigned char BYTE; – Naresh T Aug 31 '16 at 09:57
  • @Neo Please [edit] your question and add this there. And while you are at it, you can also add the #includes, and make your program a [mcve]. While not strictly needed in this case, it's always a good idea. – Fabio says Reinstate Monica Aug 31 '16 at 10:00
  • Btw: your reported "error" is something which is inserted by the compiler and not a native c++ behavior. So to the "why" it only crashes there: "Debug checks" – Hayt Aug 31 '16 at 10:03

5 Answers5

3

On a formal point of view, you are just invoking Undefined Behaviour because you are trying to access a BYTE * as a A *. Per the strict aliasing rule, it is enough for the compiler to to anything it wants.

In the real world, it will depend on the implementation, and it is likely to give correct results. Under the hood, common implementations for vector use allocated memory for their data member. That means that:

  • it will be correctly aligned for any type
  • the actual allocated memory is likely to be at least enough for an int (because of the alignment, it is at least 4 bytes on a 32 bits system and 8 on a 64 one)
  • you can safely change the type of the allocated memory

That means that it is not surprising that this code does not crash, even if it is plain Undefined Behaviour.

Note: except in very special cases, and with notices in red flashing font, you should never rely on implementation detail for your code, because it can crash with next version of your compiler, or simply if you change build options.

The heap corruption is only a warning, because your implementation has put a mark after the last byte that it has given to you, and has found that mark overwritten. But on a low level point of view you have not erased anything of major importance - assuming your implementation is like mine :-)

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1

Assuming that BYTE is some type or typedef such that sizeof(int) > sizeof(BYTE), and your real code had some #include and using directives that you forgot to paste, then your program causes undefined behaviour in several ways:

  1. An expression of type int is used to write to storage of type BYTE, this violates the strict aliasing rule.
  2. Even if we ignore (1) for a moment, it attempts to write past the end of the storage for the vector.
  3. The program violates the rule that main must have a return type of int. Violating a diagnosable rule means that any executable generated has completely undefined behaviour.

When undefined behaviour occurs, anything can happen. This includes, but is not limited to, crashing and not crashing. You should not expect any particular events or lack thereof.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • He asked though why it occurs only on the end of the stack. Not really why it crashes in general. – Hayt Aug 31 '16 at 09:52
  • @Hayt my final paragraph covers that - "any particular events" includes crashing or other behaviour at any particular point – M.M Aug 31 '16 at 10:11
  • well the exact reason here would be compiler inserted diagnostics though. – Hayt Aug 31 '16 at 10:18
1

The vector you're using allocates its internal buffer on heap.

You make a buffer overflow writing 4 bytes on the 1 byte buffer, thus corrupting the heap.

When the scope is closed, the vector's destructor deallocate its internal buffer, the heap corruption is detected.

Tryum
  • 660
  • 7
  • 22
0

Welcome in Undefined Behavior land.

a->i = 70000; is totally undefined => you write an int inside something that have only one byte. So you can't know what will happen:

  • Works fine
  • Segmentation fault
  • Works fine but break something in background (seams to be your case)
  • another unpredictable behavior

Just by guessing, I suspect that the line a->i = 70000; will produce a buffer overflow which will overwrite some internal variables from vector. So during assignment, no visible trouble because enough memory has been allocated. But when vector is destroyed, internal variables produce incorrect behavior which produce a "crash".

Once again, it's just guessing, because it's undefined behavior. Only with a debugger you can observe what's happen just before crashing.

Garf365
  • 3,619
  • 5
  • 29
  • 41
0

Your program does not crash. The heap corruption is an extra warning added by the compiler.

Now when the object gets destroyed it checks whether the memory is still "OK". Because you put a bigger object into the memory than intended, the memory past the object was overwritten. The implicit runtime checks by the compiler trigger and give you this error to tell you "something is wrong with the memory".

When this happens is compiler dependent and of the kind of memory checks. You can turn of the memory checks, then you wont get the error there but you have still corrupt memory and maybe have some strange (undefined) behavior later on.

There are several methods for this checks. Usually by adding extra special bytes at the end (sometimes also front) of allocated memory an on "delete" check if they are still there.

Hayt
  • 5,210
  • 30
  • 37