-2
int main()
{
    char *p = new char[100];
    strcpy(p, "Test");
    cout << "Before heap corruption: " << p << endl;
    p[150] = '\0';

    cout << "after heap corruption: " << p;

    delete p;

    getchar();
    return 0;
}

In the above mentioned code above, I am writing '\0' at a memory location which is not mine, even then no exception is thrown. If the above code is run with commenting delete p, no exeception will be thrown. But if it is uncommented, the attached exception is thrown. So, it is delete that validates the memory ownership. So, may I know how eaxctly delete works and why there is nosuch validation while writing out of memory block

enter image description here

trincot
  • 317,000
  • 35
  • 244
  • 286
Mani Kanta
  • 67
  • 7

5 Answers5

6

It's undefined behaviour. If you access memory you shouldn't, then anything could happen. Nothing is required to validate that you didn't do it; it's up to you to write your program so that it doesn't.

If you want runtime validation, you'll need higher-level abstractions rather than raw arrays and pointers:

std::vector<char> p(100);
p.at(150) = 0;              // out of bounds, throws exception
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
4

The instant you use undefined behaviour, all bets are off.

It can work perfectly, fail immediately or fail in some obscure manner two weeks from now. It can even, if it wishes, format your hard drive and laugh derisively at you through the sound card :-)

The likely cause of it not failing until allocating or deallocating memory is because that's the ideal time to do checks, as it's the time when memory allocation functions will split or coalesce blocks to either give you memory or take it back.

You'll often see messages like Memory arena corrupted because, when trying to manipulate the arena, the memory allocation code has noticed the structure is damaged (checksums and sentinel values may not be what is expected).

To catch something like:

p[150] = '\0';

would generally take quite a bit of run-time overhead, something that's totally unnecessary if the rules are followed.

Runtime protection is possible if you use C++ collections (like vector) instead of straight arrays, but at the cost of raw performance.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

C++ uses the concept of "Undefined behaviour" to let the compilers/operating system decide what they want to do. C++ does not throw an exception (or exit the program nicely), since doing all these things requires additional checks in your code. In C++ you don't get what you don't pay for (i.e. C++ cares a lot about performance).

Undefined behaviour is just that: anything can happen, with some probability of course. Accessing memory that isn't yours is just that.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
1

Randomly poking zeros into memory locations isn't going to produce repeatable behaviour and may or may not trigger one of the memory protection methods designed to keep you out of trouble.

So, it is delete that validates the memory ownership. So, may I know how eaxctly delete works

It depends on the compiler's standard library implementation. For example in debug mode, MSVC pads out heap blocks with a known pattern before and after so it can detect overruns. You won't find it in release mode because it reduces performance.

and why there is nosuch validation while writing out of memory block

Inserting bounds checks around every memory access would be painfully expensive. Code instrumentation tools that do just that do exist. I remember using Bounds Checker during the 90s and my goodness it was painfully slow but sometimes the most expedient way to find a difficult bug.

That said, the CPU does a certain amount of access checks through its integrated MMU. If you access memory that hasn't been allocated to your process then it will be trapped. Likewise if you try to access a page using a mode that doesn't match what it should be then it will be trapped.

Andy Brown
  • 11,766
  • 2
  • 42
  • 61
0

Checking very array access would require at least two extra instructions with every indexed operation - more if you want to catch s++ = 0;, where s is a pointer into some array (not only do we need to track the size, but also where the pointer region started), and even more if the data was dynamically allocated too (because now we need to keep track of where the original allocation was, and how large it was). I've got array bounds checks in my Pascal compiler, and it adds approximately 15% overhead - in some cases 300%, some cases 5-10%. But it only works for fixed size arrays, not dynamically allocated memory due to the problems described above. The 5-15% isn't really a big problem for most code. The cases where it is 300% is a problem - and it would be even worse if it supported dynamically allocated memory too!

The above is the simple cases where we know where the memory "came from". What if you have a function that takes a pointer to something - it would require extra storage for every pointer to arrange for somewhere to store the size of the pointee memory size - and that memory would have to be read on every memory access, a compare and a branch instruction would have to be added. Quite often, a pointer access is only a single instruction, so we have now added at least three more instructions (and a branch which is never a good thing). And of course, that data has to be filled in before it is used - and ideally in a way that doesn't ruin people's ideas of data-layout in memory...

This is why running code with valgrind and similar tools is around 10 times slower than running "full speed".

To add a bit of "padding" (aka "crumble-zone") to the memory allocation, and check at delete-time that the "padding" is still intact is less intrusive, and thus the preferred method in most cases - it is only a few percent slower in itself, and catches that "your code is not behaving as you expect", even if it doesn't catch it IMMEDIATELY.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227