2

Why does the following work and not throw some kind of segmentation fault?

char *path = "/usr/bin/";
char *random = "012";

// path + random + \0
// so its malloc(13), but I get 16 bytes due to memory alignment (im on 32bit)
newPath = (char *) malloc(strlen(path) + strlen(random) + 1);

strcat(newPath, path);
strcat(newPath, "random");
// newPath is now: "/usr/bin/012\0" which makes 13 characters.

However, if I add

strcat(newPath, "RANDOMBUNNIES");

shouldn't this call fail, because strcat uses more memory than allocated? Consequently, shouldn't

free(newPath)

also fail because it tries to free 16 bytes but I used 26 bytes ("/usr/bin/012RANDOMBUNNIES\0")?

Thank you so much in advance!

Jonas Gröger
  • 1,558
  • 2
  • 21
  • 35

9 Answers9

7

Most often this kind of overrun problems doesn't make your program explode in a cloud of smoke and the smell of burnt sulphur. It's more subtle: the variable that is allocated after the overrun variable will be altered, causing unexplainable and seemingly random behavior of the program later on.

fvu
  • 32,488
  • 6
  • 61
  • 79
  • 4
    It would be fabulous if memory overruns _did_ cause the program to explode - think of how easy it would be to find them! When you can allocate _n_ bytes and write _n_ +1 bytes and see no ill effects 99 times out of 100, it's very difficult to find the problem. – Graeme Perrow Jul 16 '11 at 15:56
  • @graeme agree 100%. That's why specifically in C programmers should be careful, very careful, and *VERY* organized – fvu Jul 16 '11 at 15:58
  • 1
    @graeme: if you'd like that kind of behaviour, you should look into [Electric Fence](http://perens.com/works/software/ElectricFence/). From the README: Electric Fence is a different kind of malloc() debugger. It uses the virtual memory hardware of your system to detect when software overruns the boundaries of a malloc() buffer. It will also detect any accesses of memory that has been released by free(). Because it uses the VM hardware for detection, Electric Fence stops your program on the first instruction that causes a bounds violation. – psYchotic Jul 16 '11 at 21:13
3

The whole program snippet is wrong. You are assuming that malloc() returns something that has at least the first byte set to 0. This is not generally the case, so even your "safe" strcat() is wrong.

But otherwise, as others have said, undefined behavior doesn't mean your program will crash. It only means it can do anything (including crashing, but also not crashing, if you are unlucky).

(Also, you shouldn't cast the return value of malloc().)

Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • Why should you not cast the return value of `malloc`? – Graeme Perrow Jul 16 '11 at 16:59
  • @Graeme: see http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – Alok Singhal Jul 16 '11 at 17:06
  • OK, thanks for the link. I knew that in C it wasn't _necessary_, but I never thought it was actually a bad idea. – Graeme Perrow Jul 16 '11 at 17:13
  • Why am I assuming that the first byte is set to 0? Don't get it. – Jonas Gröger Jul 17 '11 at 15:55
  • @pewpew, `strcat(newPath, path);` assumes that `newPath` is a null-termintated string. Since `newPath` is the return value of `malloc()` it is not guaranteed that there will be a 0 byte in it (`malloc()` just allocates memory, it doesn't set it to anything. See `memset()` or `calloc()` if you want that, or even `newPath[0] = 0;` before the first `strcat()` will work.) – Alok Singhal Jul 17 '11 at 15:57
  • @Alok: Thanks! That is one fairly good hidden monkey throwing coconuts :) – Jonas Gröger Jul 20 '11 at 00:38
1

Writing more characters than malloced is an Undefined Behavior.
Undefined Behavior means anything can happen and the behavior cannot be explained.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
1

Segmentation fault generally occurs because of accessing the invalid memory section. Here it won't give error(Segmentation fault) because you can still access memory. However you are overwriting other memory locations which is undefined behavior, your code runs fine.

schrodinger
  • 247
  • 1
  • 2
  • 7
0

It will fail and not fail at random, depending on the availability of the memory just after the malloc'd memory.

Also when you want to concat random you shouldn't be putting in quotes. that should be

strcat(newPath, random);
Paul
  • 139,544
  • 27
  • 275
  • 264
0

Many C library functions do not check whether they overrun. Its up to the programmer to manage the memory allocated. You may just be writing over another variable in memory, with unpredictable effects for the operation of your program. C is designed for efficiency not for pointing out errors in programming.

iandotkelly
  • 9,024
  • 8
  • 48
  • 67
0

You have luck with this call. You don't get a segfault because your calls presumably stay in a allocated part of the address space. This is undefined behaviour. The last chars of what has been written are not guaranteed to not be overwritten. This calls may also fail.

marc
  • 6,103
  • 1
  • 28
  • 33
  • That's not lucky. Instead it means you clobbered other valuable data (likely internal `malloc` bookkeeping) which will result in incorrect behavior (worst case: security vulns or massive data loss) later in program execution. Getting lucky would be if the program crashed so you'd immediately know there was a bug, with nothing else getting trashed... – R.. GitHub STOP HELPING ICE Jul 16 '11 at 15:53
  • Some debuugers would crash there. Of course, lucky meant that the calls did not go wrong because they can't but because e.g. the page was alloced where the additional bytes got stored. – marc Jul 16 '11 at 15:57
  • I know why. I just think "not crashing" is the unlucky (dangerous) possibility and "crashing" is the lucky one. – R.. GitHub STOP HELPING ICE Jul 16 '11 at 16:29
0

Buffer overruns aren't guaranteed to cause a segfault. The behavior is simply undefined. You may get away with writing to memory that's not yours one time, cause a crash another time, and silently overwrite something completely unrelated a third time. Which one of these happens depends on the OS (and OS version), the hardware, the compiler (and compiler flags), and pretty much everything else that is running on your system.

This is what makes buffer overruns such nasty sources of bugs: Often, the apparent symptom shows in production, but not when run through a debugger; and the symptoms usually don't show in the part of the program where they originate. And of course, they are a welcome vulnerability to inject your own code.

tdammers
  • 20,353
  • 1
  • 39
  • 56
0

Operating systems allocate at a certain granularity which is on my system a page-size of 4kb (which is typical on 32bit machines), whether a malloc() always takes a fresh page from the OS depends on your C runtime library.

Bernd Elkemann
  • 23,242
  • 4
  • 37
  • 66