Friday, October 23, 2015

Pointer vs Array - Possible Memory Leak

Usage of pointers is probably the most common source of error and vulnerability in C; this is why subsequent programming languages invented afterwards relinquish the concept of a pointer.

I personally like the concept of pointers because they are very useful and efficient in many ways, such as in a tree data structure. Furthermore, it helps me better understand how the code works at the low level in more details; it is my belief that a good programmer should know both the low level and high level sides of a system.

At the same time, I admit that one needs to be very careful with pointers because improper use of pointers will lead to a disaster. Here, I would like to give an example of such improper use of a pointer that will lead to memory leak.


People often interchangeably use pointers and arrays. Below is example1.c code:
#include <stdio.h>
int main () {
   char *str1 = "pppp";
   char str2[] = "aaaa";
   printf ("%s\n%s\n", str1, str2);
   return 0;
}

The code compiles with no error or warning:
$ gcc example1.c -o example1 && ./example1
pppp
aaaa


OK. As you may have expected, the string can be either a pointer or an array; there seems nothing wrong with this. So, is it true that you can interchangeably use the pointer or the array to define a string? The answer is NO.


In the above example, str1 is a pointer to "pppp", which is allocated outside the function main stack. In other words, the variable str1 is a local variable in main, but it points to "pppp" that is saved in the memory outside the main's scope. On the other hand, str2 is a local variable array which saves the string value "aaaa".


Let's examine the memory using gdb. Before that, we need to compile the source with -g option in gcc to supply symbols:
$ gcc -g example1.c -o example1


Next, run gdb and examine str1 and str2:
$ gdb -q example1
Reading symbols from example1...done.
(gdb) b 6
Breakpoint 1 at 0x80484b3: file example1.c, line 6.
(gdb) r
Starting program: /home/linuxnme/Documents/
pppp
aaaa


Let's examine the memory content of &str1
(gdb) x/wx &str1
0xbffff570: 0x08048560


In my case, 0xbffff570 is the memory address of the variable str1, which stores the content 0x08048560; this address is where the string "pppp" is saved.
(gdb) x/s str1
0x8048560: "pppp"


Let's examine &str2 now.
(gdb) x/wx &str2
0xbffff577: 0x61616161
(gdb) x/s &str2
0xbffff577: "aaaa"


Can you tell the difference now? Let's quit gdb with
(gdb) q
Quit anyway? (y or n) y


Now in order to examine memory leakage, let's consider example2.c code:
#include <stdio.h>
char* array() {
   char string[] = "aaaa";
   return string;
}
char* ptr() {
   char *string = "pppp";
   return string;
}
int main() {
   printf ("%s\n", array());
   printf ("%s\n", ptr());
   return 0;
}


Let's compile and run it. When compiling the code, your compiler should warn you that the function returns the address of the local variable.
$ gcc example2.c -o example2 && ./example2
?
pppp


As you can see, when a string is defined as an array in a local function, it is saved in the function's stack and therefore it is completely cleared out when the function returns. However, if a string is defined as a pointer, then the string is saved outside the function's scope, and therefore it will remain in the memory. This is the memory leak in the program. Here, it is only a 5-byte memory leak, but it can get worse very quickly if the function is called multiple times during its run. Although I only demonstrated this with a char string, it could be of any data types including structures or classes.


So, the main lesson here is never define anything directly from a pointer, especially in a local function because it will lead to memory leak.

No comments:

Post a Comment