tags:

views:

668

answers:

15
char *cp = (char *) malloc(1);
strcpy(cp, "123456789");
puts(cp);

output is "123456789" on both gcc (Linux) and Visual C++ Express, does that mean when there is free memory, I can actually use more than what I've allocated with malloc()?

and why malloc(0) doesn't cause runtime error?

Thanks.

+2  A: 

No. It means that your program behaves badly. It writes to a memory location that it does not own.

Didier Trosset
okay, so, is it possible that the extra memory I used would be allocated to someone else?
woongiap
@woon: *Anything* is possible. You've got UB, trying to guess what happens next is a dead-end. Yes, it could be handed out, maybe not on a common platform, maybe so on some other platform, who knows.
GMan
It can have been allocated to some other malloc call, in which case other variables of your program will change value without actually being changed; or still be unused, and your program will work by chance; or be used by the C library heap memory manager to store malloc internal data, and your program may crash on subsequent call of malloc or free.
Didier Trosset
+19  A: 

No. You get undefined behavior. That means anything can happen, from it crashing (yay) to it "working" (boo), to it reformatting your hard drive and filling it with text files that say "UB, UB, UB..." (wat).

There's no point in wondering what happens after that, because it depends on your compiler, platform, environment, time of day, favorite soda, etc., all of which can do whatever they want as (in)consistently as they want.

More specifically, using any memory you have not allocated is undefined behavior. You get one byte from malloc(1), that's it.

GMan
+1 for "UB, UB, UB...".
RBerteig
It is undefined by the language specification, but that doesnt mean it cannot be predicted. As you say, it depends on the OS, compiler and state of your virtual memory. Since this last part is extremely hard to predict and say something useful about it, people call it undefined. But look at the answer PP has given. Finally, it most certainly is not correlated in any way to your favourit soda ;) The behavior is very deterministic, given that you have enough information.Btw, +1 anyway since the main idea is correct and for the funny story :)
Henri
@Henri: I think PP's answer is good in that it touches on some generic platform, but it does fail to talk about the language. It's important people learn "undefined behavior" means "don't do it", not "oh, but how does it work, how is it *defined*?" One can always learn about a platform out of curiosity, not trying to understand some undefined behavior.
GMan
@Henri: For the C language, “undefined” has a very specific meaning. It does not imply that no one can guess what happens, but it does mean that the standard has not defined what should in any way and does not require a compiler writer to do so. (The latter is the primary difference to “unspecified behavior.”) In practice, it usually means that crashes or, sometimes, worse can happen at what looks like random situations. Including the not-so-rare situation where everything works in your tests and the system goes south when your customer touches it. And only the programmer and QA are to blame…
Christopher Creutzig
+2  A: 

You get undefined behavior - anything can happen. Don't do it and don't speculate about whether it works. Maybe it corrupts memory and you don't see it immediately. Only access memory within the allocated block size.

sharptooth
+11  A: 

When you ask malloc for 1 byte, it will probably get 1 page (typically 4KB) from the operating system. This page will be allocated to the calling process so as long as you don't go out of the page boundary, you won't have any problems.

Note, however, that it is definitely undefined behavior!

Consider the following (hypothetical) example of what might happen when using malloc:

  1. malloc(1)
  2. If malloc is internally out of memory, it will ask the operating system some more. It will typically receive a page. Say it's 4KB in size with addresses starting at 0x1000
  3. Your call returns giving you the address 0x1000 to use. Since you asked for 1 byte, it is defined behavior if you only use the address 0x1000.
  4. Since the operating system has just allocated 4KB of memory to your process starting at address 0x1000, it will not complain if you read/write something from/to addresses 0x1000-0x1fff. So you can happily do so but it is undefined behavior.
  5. Let's say you do another malloc(1)
  6. Now malloc still has some memory left so it doesn't need to ask the operating system for more. It will probably return the address 0x1001.
  7. If you had written to more than 1 byte using the address given from the first malloc, you will get into troubles when you use the address from the second malloc because you will overwrite the data.

So the point is you definitely get 1 byte from malloc but it might be that malloc internally has more memory allocated to you process.

Job
That is not true at all, at least on Windows. You are given exactly what you ask for. The heap will allocate 1 byte, no more. In the event it is written past it is undefined, but he is definitely NOT given a page.
linuxuser27
@linuxuser27: I'm no expert on Windows, but I *seriously* doubt it gets 1 byte. It would be a waste, it's cleaner just to get a chunk of memory and use that, until you need another chunk of memory.
GMan
It does get 1 byte. When an application starts in Windows a chunk of memory is pre-allocated by the OS for the heap. Calls to malloc() and the like eventually head down to HeapAlloc() which then divide up the pre-allocate heap to the process. Of course the heap can grow as is needed, but when you ask for 1 byte, that is what you get. If you want to see it you can call HeapWalk() and see for yourself. I hate to be a stickler for this, but I work on the visual studio profiler and do a lot of memory profiling :)
linuxuser27
@linuxuser27: "a chunk of memory is pre-allocated" I think this is what me and Job are talking about (total memory usage), while you're talking about the returned result.
GMan
@GMan @linuxuser27: Yes that's what I'm talking about. See my edits.
Job
@linuxuser27: Calls to malloc eventually head down to HeapAlloc, and calls to HeapAlloc eventually result in calls to VirtualAlloc, which allocates full pages. So allocating 1 byte *will* allocate a full page. The full page is readable/writable for the process. If the memory is committed, the full 4 KB are used by the process.
nikie
@nikie yes you are correct. If the current heap size is not enough, VirtualAlloc() is called and it can be called in other instances too.My criticism was originally that the post seemed to imply that if you request even 1 byte, you are going to get a full page allocated for that particular allocation. Which could be interpreted as meaning that the remainder of the page will not be used. It was how I read it, the edits clear that up. Sorry for picking nits here.@Job Step 6 would not return 0x1001 though. It would need to be aligned. But your point has been made. +1
linuxuser27
+1  A: 

You just got lucky there. You are writing to locations which you don't own this leads to undefined behavior.

codaddict
+2  A: 

You may be allowed to use until the memory reaches some program memory or other point at which your applicaiton will most likely crash for accessing protected memory

ckv
Which will invariably happen while doing a dog-and-pony show for the customer. And they say that Undefined Behaviour bugs aren't conscious, ha!
msw
+1  A: 

On most platforms you can not just allocate one byte. There is often also a bit of housekeeping done by malloc to remember the amount of allocated memory. This yields to the fact that you usually "allocate" memory rounded up to the next 4 or 8 bytes. But this is not a defined behaviour.

If you use a few bytes more you'll very likeley get an access violation.

jdehaan
thanks, just to make the code snippet simple
woongiap
+35  A: 

You've asked a very good question and maybe this will whet your appetite about operating systems. Already you know you've managed to achieve something with this code that you wouldn't ordinarily expect to do. So you would never do this in code you want to make portable.

To be more specific, and this depends entirely on your operating system and CPU architecture, the operating system allocates "pages" of memory to your program - typically this can be in the order of 4 kilobytes. The operating system is the guardian of pages and will immediately terminate any program that attempts to access a page it has not been assigned.

malloc, on the other hand, is not an operating system function but a C library call. It can be implemented in many ways. It is likely that your call to malloc resulted in a page request from the operating system. Then malloc would have decided to give you a pointer to a single byte inside that page. When you wrote to the memory from the location you were given you were just writing in a "page" that the operating system had granted your program, and thus the operating system will not see any wrong doing.

The real problems, of course, will begin when you continue to call malloc to assign more memory. It will eventually return pointers to the locations you just wrote over. This is called a "buffer overflow" when you write to memory locations that are legal (from an operating system perspective) but could potentially be overwriting memory another part of the program will also be using.

If you continue to learn about this subject you'll begin to understand how programs can be exploited using such "buffer overflow" techniques - even to the point where you begin to write assembly language instructions directly into areas of memory that will be executed by another part of your program.

When you get to this stage you'll have gained much wisdom. But please be ethical and do not use it to wreak havoc in the universe!

PS when I say "operating system" above I really mean "operating system in conjunction with privileged CPU access". The CPU and MMU (memory management unit) triggers particular interrupts or callbacks into the operating system if a process attempts to use a page that has not been allocated to that process. The operating system then cleanly shuts down your application and allows the system to continue functioning. In the old days, before memory management units and privileged CPU instructions, you could practically write anywhere in memory at any time - and then your system would be totally at the mercy of the consequences of that memory write!

PP
Very nice answer!
cwap
Excellent answer! I've been doing OS development for a few years and fixing MMU problems on and off for the last year. Until now, I have never thought of thinking of some of the MMU behaviour as a callback into the OS--just as an exception that gets triggered and work that needs to be done. It simplifies things; thankyou!
Sparky
A: 

It could be that you're in Debug mode, where a call to malloc will actually call _malloc_dbg. The debug version will allocate more space than you have requested to cope with buffer overflows. I guess that if you ran this in Release mode you might (hopefully) get a crash instead.

Default
no crash in both debug and release
woongiap
any comment on the downvote?
Default
A: 

You should use new and delete operators in c++... And a safe pointer to control that operations doesn't reach the limit of the array allocated...

Charlie
no, plain c is used here
woongiap
Sorry, I misunderstood ;) I thought you was programming in C++.
Charlie
+1  A: 

There is no "C runtime". C is glorified assembler. It will happily let you walk all over the address space and do whatever you want with it, which is why it's the language of choice for writing OS kernels. Your program is an example of a heap corruption bug, which is a common security vulnerability. If you wrote a long enough string to that address, you'd eventually overrun the end of the heap and get a segmentation fault, but not before you overwrote a lot of other important things first.

When malloc() doesn't have enough free memory in its reserve pool to satisfy an allocation, it grabs pages from the kernel in chunks of at least 4 kb, and often much larger, so you're probably writing into reserved but un-malloc()ed space when you initially exceed the bounds of your allocation, which is why your test case always works. Actually honoring allocation addresses and sizes is completely voluntary, so you can assign a random address to a pointer, without calling malloc() at all, and start working with that as a character string, and as long as that random address happens to be in a writable memory segment like the heap or the stack, everything will seem to work, at least until you try to use whatever memory you were corrupting by doing so.

Chris
thanks, but why doesn't malloc(0) return NULL?
woongiap
@woongiap: It doesn't have to. It can return memory if it wants, but that memory cannot be used.
GMan
Since the compiler doesn't check if your function arguments are stupid, many malloc() implementations will allocate their smallest allocation unit, usually 8 bytes, for malloc(0) calls. Some malloc() implementations will print errors to standard out or crash if you set environment variables to turn on checking for stupid things like that, but by default it will generally attempt to do what you ask, however idiotic it may be.
Chris
@Chris, it's fine that the compiler doesn't check that. malloc(0) might be stupid, but malloc(a-b) isn't. I think malloc() implementation should simply return NULL if given size is not larger than 0 since the actual allocation algorithm is already complicated enough.
woongiap
Prior to modern threading libraries, malloc(0) was sometimes abused for unique token generation in multithreaded programs, since the synchronization was handled automatically. malloc(1) would work just as well, but malloc(0) was preferred because it made it obvious you were abusing malloc. Platforms that have been around long enough to have code like this written for them are reluctant to change that behavior and break old code.
Chris
+1  A: 

malloc allocates the amount of memory you ask in heap and then return a pointer to void (void *) that can be cast to whatever you want.

It is responsibility of the programmer to use only the memory that has been allocate. Writing (and even reading in protected environment) where you are not supposed can cause all sort of random problems at execution time. If you are lucky you the program crash immediately with an exception and you can quite easily find the bug and fix it. If you aren't lucky it will crash randomly or produce unexpected behaviors.

For the Murphy's Law, "Anything that can go wrong, will go wrong" and as a corollary of that, "It will go wrong at the right time, producing the most large amount of damage". It is sadly true. The only way to prevent that, is to avoid that in the language that you can actually do something like that.

Modern languages do not allowed the programmer to do write in memory where he/she is not supposed (at least doing standard programming). That is how Java got a lot of its traction. I prefer C++ to C. You can still make damages using pointers but it is less likely. That is the reason why Smart Pointers are so popular.

In order to fix these kind of problems, a debug version of the malloc library can be handy. You need to call a check function periodically to sense if the memory was corrupted. When I used to work intensively on C/C++ at work, we used Rational Purify that in practice replace the standard malloc (new in C++) and free (delete in C++) and it is able to return quite accurate report on where the program did something it was not supposed. However you will never be sure 100% that you do not have any error in your code. If you have a condition that happen extremely rarely, when you execute the program you may not incur in that condition. But eventually will happen in production on the most busy day on the most sensitive data (according to Murphy's Law ;-)

Chris Cinelli
+2  A: 

So many responses and only one that gives the right explanation. While the page size, buffer overflow and undefined behaviour stories are true (and important) they do not exactly answer the original question. In fact any sane malloc implementation will allocate at least in size of the alignment requirement of an intor a void *. Why, because if it allocated only 1 byte then the next chunk of memory wouldn't be aligned anymore. There's always some book keeping data around your allocated blocks, these data structures are nearly always aligned to some multiple of 4. While some architectures can access words on unaligned addresses (x86) they do incure some penalties for doing that, so allocator implementer avoid that. Even in slab allocators there's no point in having a 1 byte pool as small size allocs are rare in practice. So it is very likely that there's 4 or 8 bytes real room in your malloc'd byte (this doesn't mean you may use that 'feature', it's wrong).

EDIT: Besides, most malloc reserve bigger chunks than asked for to avoid to many copy operations when calling realloc. As a test you can try using realloc in a loop with growing allocation size and compare the returned pointer, you will see that it changes only after a certain threshold.

tristopia
A: 

strcpy() doesn't check if the memory it's writing to is allocated. It just takes the destination address and writes the source character by character until it reaches the '\0'. So, if the destination memory allocated is smaller than the source, you just wrote over memory. This is a dangerous bug because it is very hard to track down.

puts() writes the string until it reaches '\0'.

My guess is that malloc(0) only returns NULL and not cause a run-time error.

zooropa
+1  A: 

To answer your second question, the standard specifically mandates that malloc(0) be legal. Returned value is implementation-dependent, and can be either NULL or a regular memory address. In either case, you can (and should) legally call free on the return value when done. Even when non-NULL, you must not access data at that address.

ig2r