We help IT Professionals succeed at work.

Memory overrun

pgnatyuk
pgnatyuk asked
on
I attached a program with an obvious mistake:
int *ivar = new int(12);
instead of
int *ivar = new int[12];
Also in the end, there is line delete [] ivar;.

The program complies well by gcc 4.2.2 (Xcode) and works "fine". No warnings. No crash.  
I thought, there is only one variable in this program and so there is nothing to crash. I added few other variables - no crash at all. :( I compiled it manually in a terminal session :) - same result.

Same program in Visual Studio is complied well (no warnings), but an exception raised in the debug session -"Microsoft C++ exception: std::bad_alloc at memory location...". It was not because of the "delete [] ivar;" line. It is in the second loop, in "std::cout << ivar[k];. Actually, almost any touch, a try to read this 'ivar' in VS, causes a crash.

So this code (attached) is correct from the compiler point of view?
How you, guys, detect such memory problems, if you don't work with VS?
Is it again a problem/bug in the gcc from Apple?

#include <iostream>

int main (int argc, char * const argv[]) 
{
	int * ivar = new int(12);
	for (int i = 0; i < 12; ++i)
		ivar[i] = i;
	
	for (int k = 0; k < 12; ++k)
		std::cout << "[" << k << "] = " << ivar[k] << "\n";

	delete [] ivar;
    return 0;
}

Open in new window

Screen-shot-2010-07-02-at-9.53.5.png
Comment
Watch Question

Top Expert 2009
Commented:
>> So this code (attached) is correct from the compiler point of view?

Sure. The compiler doesn't care that you write beyond the bounds of a buffer.


>> How you, guys, detect such memory problems, if you don't work with VS?

When using code like this, eventually you will get a crash, or notice other strange behavior. When you do, you can run a memory profiler against the binary to find out what's going wrong.

Alternatively, you can run a memory profiler against your binary BEFORE deploying it. Just to get rid of most of these kind of problems.


>> Is it again a problem/bug in the gcc from Apple?

No, this is not a bug in the compiler. I compiler might give you a warning about this (but it's not easy for a compiler to detect out-of-bounds accesses), but it is not obliged to do so.

In fact, sometimes you DO want to access data outside of an allocated buffer.
Top Expert 2009
Commented:
>> No crash.  

Whether the code crashes because of the writes outside of the buffer, depends on many variables. It depends on where the dynamic memory was allocated. It depends on the memory that surrounds the allocated buffer. It depends on the kind of operation you perform when accessing a memory location outside of the allocated buffer. It depends on what the overwritten memory location is used for. Etc.

The delete [] at the end though, is a lot more likely to make the code crash. But in this case, it depends on the memory manager used by your compiler - ie. how it lays out dynamically allocated blocks in memory, and what the difference is between allocating an array or a single value.

But again : if it doesn't crash, it doesn't mean the code is correct ;) (as you know)


For example: when I run the code (compiled with g++ 4.4.1 on an i686 Linux platform), it doesn't care about the writes outside of the buffer. However, it crashes on the delete [] line, because it doesn't like what it finds in that memory location :

        *** glibc detected *** ./a.out: free(): invalid next size (fast): 0x09e90008 ***

Author

Commented:
BTW, the console window of this program left on my screen on the Windows XP computer. Task Manager didn't kill it. Even Shut Down didn't work.
:)

On Windows I use Bound Checker and Purify+. In Xcode there are a lot of instruments too. I heard there is something really great on Linux. I hope, Purify+ and BoundChecker do detect this memory overrun. In Xcode, with current knowledge I cannot do it.

Sometimes ( rarely :) ), I understand people moving to C# and Objective-C. :)

Author

Commented:
>> depends on many variables
I tested it too. Not in VS, but only in XCode. They are ok.
Top Expert 2009

Commented:
>> I heard there is something really great on Linux.

You might be thinking of Valgrind :

        http://valgrind.org/

Author

Commented:
>>You might be thinking of Valgrind
Thanks.

"The main improvement is that Valgrind now works on Mac OS X."
We'll see.

Commented:
>> So this code (attached) is correct from the compiler point of view?

Yes,

int* ivar = new int(12);

will allocate a single integer which is initialized to 12.
It is like when you have a class

class Foo {
   public:
        Foo(int);
};

then you can write

Foo* f = new Foo(12);

All base types automatically have a single argument 'constructor' which allows to initialize the
variable.

In the rest of the program, you are indeed reading/writing outside the arrays bounds, which may or may not cause a crash. Valgrind will detect this.

>> BTW, the console window of this program left on my screen on the Windows XP computer. Task Manager didn't kill it. Even Shut Down didn't work.
That consistently happens with VS 2010 Express. Are you using that? The only way I found to get away with it without having to restart is - Log Off / Log On.
 
>> All base types automatically have a single argument 'constructor' which allows to initialize the variable.
All POD types actually.
struct pod { int a; }
new pod; // uninitialized
new pod(); // initialized
new pod(0); // non-default initialized, here it begins to differ for primitive types. All primitives have an implicit non-reference-copy-constructor. Udts must be provided one otherwise wont compile.
>> So this code (attached) is correct from the compiler point of view? How you, guys, detect such memory problems, if you don't work with VS? Is it again a problem/bug in the gcc from Apple?
Sigh, if only VS were good enough to detect ** all ** such errors! Even in tihs case, the bad part (first loop) goes undetected.
The compilers are not at fault here, and similarly the CRT is not obliged to detect such issues. We are only expecting the CRT to be able to detect and catch it. The compiler actually can not ** assume ** that you are writing outside the bounds and therefore does the right thing of not assuming anything. Why can't it assume even for this simple code? For one, because this code relies on CRT for allocations and also what if the intent here is to actually write/read out of bound? How complicated can it become when there are mem managers out there that even try to make your program work in the presense of such corruptions? See http://www.diehard-software.org/, for example. Making assumptions will run them out of business :D
Historically, VC compilers have tried to aid dev by generating extra code, guard bytes, debug bit patterns etc like 0xcdcd. But none of those actually protect against anything. They are just benign attempts meant to aid troubleshooting/debugging. I think gcc doesnt do anything like that. Sometimes I wish there were a "checked" build config available with the VS compilers and a corresponding CRT that does exhaustive mem corruption testing. Something like valgrind/duma built right into CRT. That would safe countless hours of smacking head into walls.
 

Author

Commented:
Thanks.