Application Development in C - Debugging Tools

2 views
Skip to first unread message

achindra...@gmail.com

unread,
May 24, 2005, 11:13:59 AM5/24/05
to giGA...@googlegroups.com
Two of the most common problems in coding with C are
1. Memory Leaks - when memory allocated by malloc() is never free()d.
2. Buffer Over runs where we write past end of the array or allocated
memory.

This thread will discuss the solutions to buggy code!

1: #include <stdlib.h>
2: #include <stdio.h>
3:
4: char global[5];
5:
6: int main (void) {
7: char * dyn;
8: char local[5];
9:
10: /* First, overwrite a buffer just a little bit */
11: dyn = malloc(5);
12: strcpy(dyn, "12345");
13: printf("1: %s\n", dyn);
14: free(dyn);
15:
16: /* Now overwrite the buffer a lot */
17: dyn = malloc(5);
18: strcpy(dyn, "12345678");
19: printf("2: %s\n", dyn);
20:
21: /* Walk past the beginning of the malloc()ed buffer */
22: *(dyn -1) = '\0';
23: printf("3: %s\n", dyn);
24: /* We did not free()d the pointer */
25:
26: /* Now some local variable */
27: strcpy(local, "12345");
28: printf("4: %s\n", local);
29: local[-1]='\0';
30: printf("5: %s\n", local);
31:
32: /* Finally the Global Space */
33: strcpy(global, "12345");
34: printf("6: %s\n", global);
35:
36: /* Walk past the begining of global buffer */
37: global[-1]='\0';
38: printf("7: %s\n", global);
39:
40: return 0;
41: }

The above code corrupts three types of memory regions:
1. memory from dynamic memory pool via malloc()
2. local variables from program stack
3. global variables from seperate memory area in memory

$ gcc -Wall -o memleak memleak.c
$ ./memleak
1: 12345
2: 12345678
3: 12345678
4: 12345
5: 12345
6: 12345
7: 12345

When we run the code, it runs fine, while we have corrupted the
memory!!!

Lets start using some debuggers - Memory Debugging Tools

Electric Fence

Electric Fence does not find memory leaks, but help to isolate buffer
overruns. As linux, like every modern operating system provides
hardware memory protection to isolate programs from each other.

Electric Fence replaces C libraries malloc() function with a version
that allocates the requested memory and usually allocates a section of
memory immideately after this, which the process is not allowed to
access!

When the process tries to access the memory block pass the allocated
one, kernel traps and halts the process with a segmentation fault! Most
of us might have struggled with this error while working with pointer.

This way Electric Fence manages to get the program killed when it
passes the allocated space.

libefence.a is the library for Electric Fence!

Now compile the code against Electric Fence library...

$ gcc -ggdb -Wall -o memleak memleak.c -lefence

-ggdb generates debugging information in the code.

$ ./memleak
Electric Fence ... Copyright ...
1: 12345
Segmentation Fault (core dumped)

Although Electric Fence did not tell where the exact problem is, but it
tolw the problem exists.

Now lets run the code in Debugger to trace the problem...

$ gdb memleak
GDB .....
....
...
...
(gdb) run
Starting program: /home/achindrab/memleak
Electric Fence... Copyright ...
1: 12345
Program received signal SIGSEGV, segmentation fault.
...
...
(gdb) where
...
.... in main() at memleak.c:18
...
(gdb)

So now we know the error is at line number 18!!!

(gdb) is the debuggers prompt, gdb is a debugger, and supports command
line control


Most modern computers require multibyte objects to start at particular
offsets in memory (RAM), eg: a 32-bit system requires all 32-bit (4
byte) objects start at memory location evenly divisible by 4, and
64-bit(8 byte) at locatons evenly divisible by 8.

Due to this consideration, malloc() implementations normally return
memory whose first byte is alligned according to the processor word
size.

Electric Fence simulates this behavior by providing a malloc() that
returns only address that are an even multiple of sozeof(int).

In most programs, such allignment is not required as memory allocation
is done in incremental order which is based on machines word size or
simple character strings whose elements are already 1byte long.

In the program, malloc() allocates 5 bytes. for Electric Fence to meet
the allignment requirements, it must treat the allocation as a request
for 8bytes. And set up memory with extra 3 bytes on accesible space
after malloc()ed region. Now small buffer overruns in the region are
not cought because of this!!!

As malloc() allignment concerns can normally be ignored and the
allignment can allow buffer overruns to remain undetected, EF_ALIGNMENT
environment variable can be used to control how Electric Fence controls
the allignment.

If set, all malloc() results are alligned accordingly to its value.

eg:. if malloc()ed region is set to 5, all locations will be alligned
to a multiple of 5.

Set EF_ALIGNMENT to turn the feature off.

Although Linux kernel handles improper alligned accesses.

EF_PROTECT_BELOW environment variable is set to 1, Electric Fence will
trap any buffer underruns. Although Electric Fence will not be able to
do buffer overrun checks. Due to memory allignment concerns. It will
pad the inaccessible memory regions to the starting of the memory.

EF_PROTECT_FREE when set to 1, and free() is called, the memory is
returned to the inaccessible pool of memory, thus no access and kernel
traps the illegal move!!!

Topics to be handled in further discussion are
1. Finding Overruns with checker
2. Finding Memory Leaks with checker and mpr
3. Finding Memory Corruptions with mcheck
4. Any other tools in use.

achindra...@gmail.com

unread,
May 25, 2005, 3:02:05 AM5/25/05
to giGA...@googlegroups.com
I am done with Checker!!!

Checker adds extra code to a program to help find overflows and memory
leaks. Instead of compiling a program with gcc, we can use checkergcc
and the resulting program will debug itself.

The compilation string will be the same:

$ checkergcc -o memleak memleak.c

On executing a program compiled with checkergcc, a long report of
errors is printed ...

$ ./memleak
>From Checker (pid:xxxxx) ... violation in heap
When Writing 1 byte .... at ...
0 bytes after ...
The block was allocated from:
.. in malloc() at ...
.. in main() at memleak.c:11
.. in _start() at :0
Stack frames are:
.. in strcpy() at strcpy.c:35
.. in main() at memleak.c:12
.. in _start)_ at :0

The first part of the output tells that we wrote to the part of heap
(memory allocated by malloc()) that was out of all9ocated region. It
tells exactly where the block was allocated - line 11, and which
function caused - strcpy() at line 12.

When we write too many bytes to the local variable, checker gives a
warning that we are writing off the end of stack.

Documentation states, that checker cannot find writes past end of local
buffers, but writes past end of stacks. Thus if more variables have
been allocated after local, checker would not find any problem.

Problem with global variables can not be tracked down by checker.

CHECKEROPTS an environment variable has lot of options for control and
customisation of Checker.

when CHECKEROPT is set to --detector=end, Checker detects memory-leaks.
It tells the number of memory leaks in the program and location of
leaks.

achindra...@gmail.com

unread,
May 26, 2005, 1:34:28 AM5/26/05
to giGA...@googlegroups.com
mpr is another tool to play with memory leak problems.

The approach that mpr takes to find the memory leak problems is to map
each malloc( ) with a corresponding free( ) function call. This is a
similar approach, that we take, most often, to detect a memory leak. To
find memory corruption mpr includes a replacement malloc( ) library
from GNU project that includes some support for memory allocation
debugging, which is enabled by calling the mcheck( ) function! We need
to link the application against libmpr.a using command line option:
-lmpr, mcheck( ) will be activated automatically.



Using mcheck( )
Unlike Electric Fence, mpr's malloc ( ) function adds sequesnce of
bytes, before and after the allocated memory. free( ) looks for those
signatures, and cals abort( ) if they are disturbed...

Running a mpr linked program in gdb shows exactly which region has been
corrupted, but does not pinpoints where the corruption occured. It is
up to the programmer to identify that.

When a program(mpr linked) is run in gdb, it aborts with a signal
abort. on running command 'where', gdb tells the error at free( )
function call, which indicates the problem region.

Global and Local variables, overrun is still undetected!

Any budy know, how to detect over runs and under runs in Global and
Local Variables.... ?

Reply all
Reply to author
Forward
Message has been deleted
Message has been deleted
Message has been deleted
0 new messages