Debugging Segmentation Faults

Overview

Debugging segmentation faults in C programs can be a frustrating experience. "Seg Faults" typically occur by attempting to access an invalid memory location when you dereference a pointer variable.

For example the following execution sequence will result in a seg fault:

int i = 3;     // declare an integer variable 

int j = 1;     // declare an integer variable

int *ip = 0;   // declare a pointer to an integer variable

j = *ip        // dereferencing pointer ip attempts to access memory location 0

Fortunately the gnu debuuger can save you a lot of time and angst by (in most cases) identifying the function name and line number where the seg fault occurred.

Here's a simple example to demonstrate how to locate (and fix) a seg fault using the debugger building on the above example:

Download crash.c, compile it using the debugger option (-g) and run it to see the seg fault occur:

bash-4.4$ cat crash.c
#include <stdlib.h>
#include <stdio.h>

int i = 3;     // integer variable
int j = 1;     // integer variable
int *ip = 0;   // pointer to an integer varible

int main() {

// set j to i using *ip pointer

  j = *ip;   // SEG FAULT !!!

  printf("j = %d\n", j);

  return 0;
}

bash-4.4$ gcc -g -o crash crash.c
bash-4.4$ ./crash

Segmentation fault (core dumped)

Lets's say this was a more complex program and the seg fault was not so obvious. In this case we could use the debugger:

bash-4.4$ gdb crash
GNU gdb 6.3
Copyright 2004 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 "amd64-unknown-openbsd6.2"...
(gdb) r
Starting program: /home/tjr/work/2175/swen250/C/CDebug/Test/crash

Program received signal SIGSEGV, Segmentation fault.
0x000016cf2a100537 in main () at crash.c:12
12        j = *ip;   // SEG FAULT !!!

Note we used the gdb command 'r' (run) to start the program. We can list ('l') the source - with line numbers - while in the debugger

(gdb) l 1,20
1       #include <stdlib.h>
2       #include <stdio.h>
3
4       int i = 3;     // integer variable
5       int j = 1;     // integer variable
6       int *ip = 0;   // pointer to an integer varible
7
8       int main() {
9
10      // set j to i using *ip pointer
11
12        j = *ip;   // SEG FAULT !!!
13
14        printf("j = %d\n", j);
15
16        return 0;
17      }
(gdb)

We can inspect (print) variables right after the seg fault occurred. 
What was the value of ip that caused the seg fault?

(gdb) p ip
$1 = (int *) 0x0

What happens when we attempt to dereference ip?

(gdb) p *ip
Cannot access memory at address 0x0

Yup, that's a seg fault. Let's edit crash.c and add the following statement at line 9:

    ip = &i;    // set ip pointer to the address of variable i

bash-4.4$ nano crash.c
< editing done here >
bash-4.4$ gcc -g -o crash crash.c
bash-4.4$ ./crash
j = 3

That worked, but let's look at the corrected version in the debugger:

bash-4.4$ gdb crash
GNU gdb 6.3
Copyright 2004 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 "amd64-unknown-openbsd6.2"...


(gdb) b 9                                // set a breakpoint at line 9
Breakpoint 1 at 0x530: file crash.c, line 9.
(gdb) r
Starting program: /home/tjr/work/2175/swen250/C/CDebug/Test/crash
Breakpoint 1 at 0x168080000530: file crash.c, line 9.

Breakpoint 1, main () at crash.c:9       // list source when breakpoint hit
9         ip = &i;      // set ip pointer to the address of variable i
(gdb) l
4       int i = 3;     // integer variable
5       int j = 1;     // integer variable
6       int *ip = 0;   // pointer to an integer varible
7
8       int main() {
9         ip = &i;      // set ip pointer to the address of variable i
10      // set j to i using *ip pointer
11
12        j = *ip;   // SEG FAULT no more!
13
(gdb) p ip                                    // inspect variables
$1 = (int *) 0x0
(gdb) p &i
$2 = (int *) 0x168080201000
(gdb) n                                        // excute next line ('n')
12        j = *ip;   // SEG FAULT no more!
(gdb) p j
$3 = 1
(gdb) p ip
$4 = (int *) 0x168080201000
(gdb) p *ip
$5 = 3
(gdb) n
14        printf("j = %d\n", j);
(gdb) c
Continuing.
j = 3

Program exited normally.
(gdb)

Success!!