How to Detect Out-Of-Memory Segfaults

How to detect out-of-memory segfaults?

On Linux, an out-of-memory condition can manifest in one of two ways:

  • If overcommit is disabled, a brk() or mmap() call fails with ENOMEM. Shortly thereafter, the application attempts to dereference the NULL pointer returned from malloc() and crashes.
  • If overcommit is enabled, then the OOM killer kicks in, and kills the process with SIGKILL. A message is left in dmesg.

As such, you can rule out OOMs by checking that strace doesn't show brk() or mmap() calls failing with ENOMEM, and verifying that no OOM killer messages appear in dmesg.

Determine the line of code that causes a segmentation fault?

GCC can't do that but GDB (a debugger) sure can. Compile you program using the -g switch, like this:

gcc program.c -g

Then use gdb:

$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>

Here is a nice tutorial to get you started with GDB.

Where the segfault occurs is generally only a clue as to where "the mistake which causes" it is in the code. The given location is not necessarily where the problem resides.

Can a memory leak be a reason of a segmentation fault?

No, memory leaks by themselves would not cause a segmentation fault. However, memory leaks usually indicate sloppy code, and in sloppy code other issues, which would cause a segmentation fault, are likely to be present.

Can low memory cause seg faults in native code?

There are two general possibilities:

  1. A low memory condition in of itself is not going to somehow trigger a segfault in a running application. What can happen is that when the application asks for additional memory to be allocated to it, the memory allocation request fails. This is a well defined memory condition. It is documented that the relevant system calls can fail in allocating memory. But what often happens is that the application are not coded properly to check for a failed memory allocation request, and they crash for that reason. In that case, it is not true that a low memory condition is responsible for an application segfault, it is an application bug.

  2. The Linux kernel overcommits the available memory. As a result of that it is possible that the kernel will have no option but to select a process to be killed, when all available RAM has been exhausted.

However, in the case of the OOM killer kicking in, the chosen victims are terminated with a SIGKILL. A SEGFAULT indicates an application bug.

Segfaults and Memory leaks

Is it possible that, if a segfault occurs after allocating memory but
before freeing it, this leaks memory (that is, the memory is never
freed resulting in a memory leak)?

Yes and No: The process which crashes should be tiedied completely by the OS. However consider other processes spawned by your process: They might not get terminated completely. However usually these shouldn't take too many resources at all, but this may vary depending on your program. See http://en.wikipedia.org/wiki/Zombie_process

If so, is there any way to ensure allocated memory is cleaned up in
the event of a segfault?

In case the program is non critical (meaning there are no lives at stake if it crashes) I suggest fixing the segmentation fault. If you really need to be able to handle segmentation faults see the answer on this topic: How to catch segmentation fault in Linux?

UPDATE: Please note that despite the fact that it is possible to handle SIGSEGV signals (and continuning in program flow) it is not a secure way to rely on, since - as pointed out in the comments below - it is undefined behaviour meaning differen platforms/compilers/... may react differently.

So by any means possible fixing segmentation faults (as well as access violations on windows) should have first priority. Still using the suggested solution to handle signals this way must be thoroughly tested and if put in production code you must be aware of it and draw any consequences - which may vary and depend on your requirements so I will not name any.

Segmentation fault but unable to reason how, memory allocation looks fine to me

Counting the stars.

 FOO * foo_ptr = malloc (sizeof (FOO) * n_foos);
// ^ ^
// | |
// one star to the left of `=` one star to the right of `=`

The rule of thumb: the number of stars must be the same at either side of the assignment. Why?

      sizeof(FOO)    sizeof(FOO)    sizeof(FOO)   
_____________ _____________ ______________
/ \/ \/ \
_____________ _____________ ______________
[_____FOO_____][_____FOO_____][______FOO_____]
^
|
FOO* foo_ptr; // a FOO* points to a FOO
// pointer arithmetic works by incrementing the address\
// by sizeof(FOO)
// and so on

Other examples of good code:

 FOO ** foo_ptr = malloc (sizeof (FOO*) * n_foos); // same number of stars
FOO *** foo_ptr = malloc (sizeof (FOO**) * n_foos); // still same

Bad code:

 FOO ** foo_ptr = malloc (sizeof (FOO) * n_foos); // numbers don't match
FOO * foo_ptr = malloc (sizeof (FOO*) * n_foos); // numbers don't match

Your line

HashTable = malloc(sizeof(node*) * numOfElements);

(after substituting the type of HashTable which is node*) falls squarely into the bad code bin, so try to fix that.

If you want an array of nodes:

HashTable = malloc(sizeof(node) * numOfElements);

If you want an array of pouners to nodes, you can have that too. This is not really recommended, because space savings are small, performance degradation will probably be substantial, and the code is less elegant. But you can have it:

node** HashTable = malloc(sizeof(node*) * numOfElements); // count! the! stars!

Congratulations! You now have an array of numOfElements uninitialized pointers. Now you need to initialize them to some value, typically NULL:

for (i = 0; i < numOfElements; ++i) HashTable[i] = NULL;

And you need to allocate a new node each time you want to put a value to the table:

if (HashTable[hashValue] == NULL) 
{
HashTable[hashValue] = malloc(sizeof(node));
if (HashTable[hashValue] == NULL)
{
panic ("Out of memory!");
}
HashTable[hashValue]->word = ...
HashTable[hashValue]->next = ...
}
else
{
// collision etc
}

While we're at it, please note these moments which are tangential to the main question: how to properly check for NULL, how to check the return value of malloc, and how to use an array index instead of mutating a global pointer variable back and forth. (If you want to use pointer arithmetic, have a local pointer variable in putInHashTable).

(Of course if you don't use n_foos, or use calloc, you need to make mental adjustments to the number of stars).

What is a segmentation fault?

Segmentation fault is a specific kind of error caused by accessing memory that “does not belong to you.” It’s a helper mechanism that keeps you from corrupting the memory and introducing hard-to-debug memory bugs. Whenever you get a segfault you know you are doing something wrong with memory – accessing a variable that has already been freed, writing to a read-only portion of the memory, etc. Segmentation fault is essentially the same in most languages that let you mess with memory management, there is no principal difference between segfaults in C and C++.

There are many ways to get a segfault, at least in the lower-level languages such as C(++). A common way to get a segfault is to dereference a null pointer:

int *p = NULL;
*p = 1;

Another segfault happens when you try to write to a portion of memory that was marked as read-only:

char *str = "Foo"; // Compiler marks the constant string as read-only
*str = 'b'; // Which means this is illegal and results in a segfault

Dangling pointer points to a thing that does not exist anymore, like here:

char *p = NULL;
{
char c;
p = &c;
}
// Now p is dangling

The pointer p dangles because it points to the character variable c that ceased to exist after the block ended. And when you try to dereference dangling pointer (like *p='A'), you would probably get a segfault.



Related Topics



Leave a reply



Submit