Analyzing code dump

Most of the operating systems produce core file if any process attempts illegal memory access, subsequently the process crashes. In case of linux, this core file is not generated by default. We need to set the limit of core file size to unlimited to get a core file in case of crash. The following command sets that

$ulimit -c unlimited
The following software problems lead to process crash,

Invalid Array Indexing
Out of bound array indexing will corrupt data structures that allocated memory before/after the array.

int a;
int b[100];
int c;

When array b is indexed with -1, variable a will be corrupted and when it is indexed with value greater than 99, then c will be corrupted.

Un-initialized Pointer Dereference

If a pointer is dereferenced before it is initialized, it can result in corrupting, any area of the memory. This shall create nightmares for debuggers as the pointer causing memory corruption might be located in completely unrelated area of the code.

A special case of this problem is dereferencing a NULL pointer.

void *p;

printf(“%d”, *p);
Dangling Pointer Access
After memory is freed, application should not access the memory as it might have already been allocated to different thread. This is dangerous in case of multi threaded programs.

free(buf);
//do something else
buf->a = NULL;


Illegal pointer type dereference

This happens when a program passes a pointer of wrong type to a function. This leads to stack corruption and it is one of the hardest bugs to fix.

int retrieve(int *cnt)
{
    //code to compute the value comes here
    *cnt = value;
    return 0;
}
int main()
{
   short a;
   retrieve(&a);
}
Invalid operations

Processors detect various exception conditions and abort program execution when they detect an error condition. A few of these conditions are:

Divide by zero

Program attempted access to an illegal address. The address might be out of range or the program might not have the privilege to perform the access. This creates SIGSEGV signal whose default action is to dump the core file.

Misaligned access to memory like long word operation attempted at addresses that is not divisible by 4.
Consider the following program(test.c) which tries to dereference NULL pointer,

#include <stdio.h>
int main()
{
   int *p = NULL;
   printf(“%d\n”, *p);
}

$gcc -o test test.c

$./test
Segmentation fault (core dumped)

If you do not get the core dumped message, try setting core file size to unlimited

You can verify this by running the ls command

$ls
core.24391  test  test.c

It generates core file with suffix containing the process ID. 
You can use debugger to open the core file and see what happened when program crashed as follows

$gdb ./test core.24391

GNU gdb Red Hat Linux (6.6-8.fc7rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type “show copying” to see the conditions.
There is absolutely no warranty for GDB.  Type “show warranty” for details.
This GDB was configured as “i386-redhat-linux-gnu”…
Using host libthread_db library “/lib/libthread_db.so.1”.

warning: Can’t read pathname for load map: Input/output error.
Reading symbols from /lib/libc.so.6…done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2…done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./test’.
Program terminated with signal 11, Segmentation fault.
#0  0x080483cf in main () at test.c:5
5          printf(“%d\n”, *p);
It says that the error occurred at test.c line number 5.
If you program is large containing multiple functions, then you can try ‘where’ command in gdb prompt to display the function call stack with its arguments. We can inspect the arguments and understand the flow of code using the stack trace.

objdump
This tool gives the content of any section in the executable file including the assembly instructions in the .text segment.It comes handy when you do not have access to the source code and have only the executable file.
This tool is also used for core dump analysis to figure out which instruction (at some particular address displayed by backtrace of gdb) dumped the core. From this we shall figure out which C source code caused the core dump.

Consider the following example program (dump.c)

#include<stdio.h>
typedef struct element{
    int data;
}Element;
void function()
{
    printf(“p is initialized to null first\n”);
    Element *p = NULL;
    printf(“I forgot to allocate memory\n”);
    p->data = 10;
    printf(“It would have dumped already\n”);
}
int main()
{
    function();
    return 0;
}

compile and get the executable

$gcc -O2 -o dump dump.c
set the core file size to unlimited to get the core file in case of dump as follows

$ulimit -c unlimited
Run the executable

$./dump

It will generate the core file in your current directory.

run gdb to figure out which instruction dumped core

$gdb ./dump core*
GNU gdb Red Hat Linux (6.6-8.fc7rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type “show copying” to see the conditions.
There is absolutely no warranty for GDB.  Type “show warranty” for details.
This GDB was configured as “i386-redhat-linux-gnu”…
(no debugging symbols found)
Using host libthread_db library “/lib/libthread_db.so.1”.

warning: Can’t read pathname for load map: Input/output error.
Reading symbols from /lib/libc.so.6…(no debugging symbols found)…done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2…(no debugging symbols found)…done.
Loaded symbols for /lib/ld-linux.so.2
(no debugging symbols found)
Core was generated by `./dump’.
Program terminated with signal 11, Segmentation fault.
#0  0x080483de in function ()
(gdb)
gdb output tells that segmentation fault(core dump) occured at address 0x080483de.
Now let us see which instruction is present at this address using the objdump command

$objdump -S ./dump >out

Open the out file using vi editor and search for 80483de. The function() will contain following assembly instructions

 080483c0 <function>:
 80483c0:       55                      push   %ebp
 80483c1:       89 e5                   mov    %esp,%ebp
 80483c3:       83 ec 08                sub    $0x8,%esp
 80483c6:       c7 04 24 00 85 04 08    movl   $0x8048500,(%esp)
 80483cd:       e8 fe fe ff ff          call   80482d0 <puts@plt>
 80483d2:       c7 04 24 20 85 04 08    movl   $0x8048520,(%esp)
 80483d9:       e8 f2 fe ff ff          call   80482d0 <puts@plt>
 80483de:       c7 05 00 00 00 00 0a    movl   $0xa,0x0
 80483e5:       00 00 00
 80483e8:       c7 04 24 3c 85 04 08    movl   $0x804853c,(%esp)
 80483ef:       e8 dc fe ff ff          call   80482d0 <puts@plt>
 80483f4:       c9                      leave
 80483f5:       c3                      ret
 80483f6:       8d 76 00                lea    0x0(%esi),%esi
 80483f9:       8d bc 27 00 00 00 00    lea    0x0(%edi),%edi

Look at address 80483de, it shows that NULL (0x0) is being moved. Now we have to figure out where this occurs in our source code. Note the call to puts() function from address 80483cd,  80483d9 and 80483ef. The dumping address comes between the 2nd and 3rd puts() function.
In our code we have three printf() statements, which means that core dump occurred between the 2nd and 3rd printf() statement, which is at line p->data = 10; Now we can walk through the code and identify the memory for p is not allocated before we access an element in it.

The most obvious memory errors result in a “Segmentation violation” message. This may alert the programmer to the location of the memory error when the program is run in gdb. The following errors discussed are the not so obvious errors.

Memory errors:

Heap memory errors:

Attempting to free memory already freed.

Freeing memory that was not allocated.

Attempting to write to memory already freed.

Attempting to write to memory which was never allocated.

Memory allocation error.

Reading/writing to memory out of the bounds of a dynamically allocated array

stack (local variables) memory errors:

Reading/writing to memory out of the bounds of a static array. (array index overflow – index too large/underflow – negative index)

Function pointer corruption: Invalid passing of function pointer and thus a bad call to a function.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s